Android自定义View之圆形进度条源码

作者: Anlia | 来源:发表于2017-10-12 11:27 被阅读304次

    版权声明:本文为博主原创文章,未经博主允许不得转载。
    教程原文:Android自定义View——从零开始实现圆形进度条

    大家要是看到有错误的地方或者有啥好的建议,欢迎留言评论

    源码已上传至github,要获取最新源码可点此传送

    CircleBarView.java

    public class CircleBarView extends View {
        private Paint bgPaint;//绘制背景圆弧的画笔
        private Paint progressPaint;//绘制圆弧的画笔
        private RectF mRectF;//绘制圆弧的矩形区域
        private CircleBarAnim anim;
        private float progressNum;//可以更新的进度条数值
        private float maxNum;//进度条最大值
        private int progressColor;//进度条圆弧颜色
        private int bgColor;//背景圆弧颜色
        private float startAngle;//背景圆弧的起始角度
        private float sweepAngle;//背景圆弧扫过的角度
        private float barWidth;//圆弧进度条宽度
        private int defaultSize;//自定义View默认的宽高
        private float progressSweepAngle;//进度条圆弧扫过的角度
        private TextView textView;
        private OnAnimationListener onAnimationListener;
        public CircleBarView(Context context, AttributeSet attrs) {
            super(context, attrs);
            init(context,attrs);
        }
        private void init(Context context,AttributeSet attrs){
            TypedArray typedArray = context.obtainStyledAttributes(attrs,R.styleable.CircleBarView);
            progressColor = typedArray.getColor(R.styleable.CircleBarView_progress_color,Color.GREEN);
            bgColor = typedArray.getColor(R.styleable.CircleBarView_bg_color,Color.GRAY);
            startAngle = typedArray.getFloat(R.styleable.CircleBarView_start_angle,0);
            sweepAngle = typedArray.getFloat(R.styleable.CircleBarView_sweep_angle,360);
            barWidth = typedArray.getDimension(R.styleable.CircleBarView_bar_width,DpOrPxUtils.dip2px(context,10));
            typedArray.recycle();//typedArray用完之后需要回收,防止内存泄漏
    
            progressNum = 0;
            maxNum = 100;
            defaultSize = DpOrPxUtils.dip2px(context,100);
            mRectF = new RectF();
            anim = new CircleBarAnim();
    
            progressPaint = new Paint();
            progressPaint.setStyle(Paint.Style.STROKE);//只描边,不填充
            progressPaint.setColor(progressColor);
            progressPaint.setAntiAlias(true);//设置抗锯齿
            progressPaint.setStrokeWidth(barWidth);
            progressPaint.setStrokeCap(Paint.Cap.ROUND);//设置画笔为圆角
    
            bgPaint = new Paint();
            bgPaint.setStyle(Paint.Style.STROKE);//只描边,不填充
            bgPaint.setColor(bgColor);
            bgPaint.setAntiAlias(true);//设置抗锯齿
            bgPaint.setStrokeWidth(barWidth);
            bgPaint.setStrokeCap(Paint.Cap.ROUND);
        }
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            int height = measureSize(defaultSize, heightMeasureSpec);
            int width = measureSize(defaultSize, widthMeasureSpec);
            int min = Math.min(width, height);// 获取View最短边的长度
            setMeasuredDimension(min, min);// 强制改View为以最短边为长度的正方形
            if(min >= barWidth*2){
                mRectF.set(barWidth/2,barWidth/2,min-barWidth/2,min-barWidth/2);
            }
        }
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            canvas.drawArc(mRectF,startAngle,sweepAngle,false,bgPaint);
            canvas.drawArc(mRectF,startAngle,progressSweepAngle,false, progressPaint);
        }
        public class CircleBarAnim extends Animation{
            public CircleBarAnim(){
            }
            @Override
            protected void applyTransformation(float interpolatedTime, Transformation t) {
                super.applyTransformation(interpolatedTime, t);
                progressSweepAngle = interpolatedTime * sweepAngle * progressNum / maxNum;
                if(textView !=null){
                    textView.setText(onAnimationListener.howToChangeText(interpolatedTime, progressNum,maxNum));
                }
                onAnimationListener.howTiChangeProgressColor(progressPaint,interpolatedTime, progressNum,maxNum);
                postInvalidate();
            }
        }
        private int measureSize(int defaultSize,int measureSpec) {
            int result = defaultSize;
            int specMode = View.MeasureSpec.getMode(measureSpec);
            int specSize = View.MeasureSpec.getSize(measureSpec);
            if (specMode == View.MeasureSpec.EXACTLY) {
                result = specSize;
            } else if (specMode == View.MeasureSpec.AT_MOST) {
                result = Math.min(result, specSize);
            }
            return result;
        }
        /**
         * 设置进度条最大值
         * @param maxNum
         */
        public void setMaxNum(float maxNum) {
            this.maxNum = maxNum;
        }
        /**
         * 设置进度条数值
         * @param progressNum 进度条数值
         * @param time 动画持续时间
         */
        public void setProgressNum(float progressNum, int time) {
            this.progressNum = progressNum;
            anim.setDuration(time);
            this.startAnimation(anim);
        }
        /**
         * 设置显示文字的TextView
         * @param textView
         */
        public void setTextView(TextView textView) {
            this.textView = textView;
        }
        public interface OnAnimationListener {
            /**
             * 如何处理要显示的文字内容
             * @param interpolatedTime 从0渐变成1,到1时结束动画
             * @param updateNum 进度条数值
             * @param maxNum 进度条最大值
             * @return
             */
            String howToChangeText(float interpolatedTime, float updateNum, float maxNum);
            /**
             * 如何处理进度条的颜色
             * @param paint 进度条画笔
             * @param interpolatedTime 从0渐变成1,到1时结束动画
             * @param updateNum 进度条数值
             * @param maxNum 进度条最大值
             */
            void howTiChangeProgressColor(Paint paint, float interpolatedTime, float updateNum, float maxNum);
        }
        public void setOnAnimationListener(OnAnimationListener onAnimationListener) {
            this.onAnimationListener = onAnimationListener;
        }
    }
    

    DpOrPxUtils.java

    public class DpOrPxUtils {
        public static int dip2px(Context context, float dpValue) {
            final float scale = context.getResources().getDisplayMetrics().density;
            return (int) (dpValue * scale + 0.5f);
        }
        public static int px2dip(Context context, float pxValue) {
            final float scale = context.getResources().getDisplayMetrics().density;
            return (int) (pxValue / scale + 0.5f);
        }
    }
    

    ShowActivity.java

    public class ShowActivity extends AppCompatActivity {
        private Button btnRestart;
        private CircleBarView circleBarView;
        private TextView textProgress;
        private CircleBarView circleBarView2;
        private TextView textProgress2;
        private int num;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_show);
            textProgress = (TextView) findViewById(R.id.text_progress);
            circleBarView = (CircleBarView)findViewById(R.id.circle_view);
            circleBarView.setTextView(textProgress);
            circleBarView.setOnAnimationListener(new CircleBarView.OnAnimationListener() {
                @Override
                public String howToChangeText(float interpolatedTime, float updateNum, float maxNum) {
                    DecimalFormat decimalFormat=new DecimalFormat("0.00");
                    String s = decimalFormat.format(interpolatedTime * updateNum / maxNum * 100)+"%";
                    return s;
                }
                @Override
                public void howTiChangeProgressColor(Paint paint, float interpolatedTime, float updateNum, float maxNum) {
                    LinearGradientUtil linearGradientUtil = new LinearGradientUtil(Color.YELLOW,Color.RED);
                    paint.setColor(linearGradientUtil.getColor(interpolatedTime));
                }
            });
            circleBarView.setProgressNum(80,1000);
            
            textProgress2 = (TextView) findViewById(R.id.text_progress2);
            circleBarView2 = (CircleBarView)findViewById(R.id.circle_view2);
            circleBarView2.setTextView(textProgress2);
            circleBarView2.setOnAnimationListener(new CircleBarView.OnAnimationListener() {
                @Override
                public String howToChangeText(float interpolatedTime, float updateNum, float maxNum) {
                    DecimalFormat decimalFormat=new DecimalFormat("0.00");
                    String s = decimalFormat.format(interpolatedTime * updateNum / maxNum * 100)+"%";
                    return s;
                }
                @Override
                public void howTiChangeProgressColor(Paint paint, float interpolatedTime, float updateNum, float maxNum) {
                }
            });
            num = 0;
            setProgressNumInThread();
            
            btnRestart = (Button) findViewById(R.id.btn_restart);
            btnRestart.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    circleBarView.setProgressNum(80,1000);
                    setProgressNumInThread();
                }
            });
        }
        private Handler handler=new Handler(){
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                switch (msg.what){
                    case 0:
                        circleBarView2.setProgressNum(num,0);
                        break;
                }
            }
        };
        private void setProgressNumInThread(){
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        for (int i=1;i<=100;i++){
                            num = i;
                            handler.obtainMessage(0).sendToTarget();
                            Thread.sleep(50);
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }
    }
    

    activity_show.xml

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context="com.anlia.bauzviews.activity.ShowActivity">
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical">
            <Button
                android:id="@+id/btn_restart"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="重置"
                android:layout_gravity="center_horizontal"
                android:layout_marginTop="10dp"/>
            <RelativeLayout
                android:layout_width="100dp"
                android:layout_height="100dp"
                android:layout_gravity="center_horizontal"
                android:layout_marginTop="10dp">
                <com.anlia.progressbar.CircleBarView
                    android:id="@+id/circle_view"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:layout_gravity="center_horizontal"
                    app:start_angle="135"
                    app:sweep_angle="270"
                    app:progress_color="@color/red"
                    app:bg_color="@color/gray_light"
                    app:bar_width="8dp"/>
                <TextView
                    android:id="@+id/text_progress"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_centerVertical="true"
                    android:layout_centerHorizontal="true"/>
            </RelativeLayout>
            <TextView
                android:layout_width="200dp"
                android:layout_height="wrap_content"
                android:layout_gravity="center_horizontal"
                android:text="单次设置进度条数值,动画时间为1秒"
                android:layout_marginTop="5dp"/>
            <RelativeLayout
                android:layout_width="100dp"
                android:layout_height="100dp"
                android:layout_gravity="center_horizontal"
                android:layout_marginTop="15dp">
                <com.anlia.progressbar.CircleBarView
                    android:id="@+id/circle_view2"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:layout_gravity="center_horizontal"
                    app:start_angle="270"
                    app:sweep_angle="360"
                    app:progress_color="@color/green_light"
                    app:bg_color="@color/gray_light"
                    app:bar_width="8dp"/>
                <TextView
                    android:id="@+id/text_progress2"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_centerVertical="true"
                    android:layout_centerHorizontal="true"/>
            </RelativeLayout>
            <TextView
                android:layout_width="200dp"
                android:layout_height="wrap_content"
                android:layout_gravity="center_horizontal"
                android:text="模拟多线程下载,进度条数值缓慢递增,动画时间设为0"
                android:layout_marginTop="5dp"/>
        </LinearLayout>
    </RelativeLayout>
    

    相关文章

      网友评论

        本文标题:Android自定义View之圆形进度条源码

        本文链接:https://www.haomeiwen.com/subject/ztenyxtx.html