美文网首页
自定义View_06(小试牛刀)还是圆形进度条

自定义View_06(小试牛刀)还是圆形进度条

作者: __Y_Q | 来源:发表于2020-01-14 00:13 被阅读0次

    因为最近被公司派去做 java 后台了, 很多东西都要学习, 后面我学习 java 后台的笔记也会在这里记录, 不然会忘得很快. 欢迎大家一起成长.

    今晚抽空再练习一个控件. 这次还是圆形进度条, 继续练习画笔, Canvas中基本画圆 API 的使用, 上效果.


    效果.gif

    先来分析一波,

    • 绘制内圈(360°的圆形)
    • 绘制外圈(这里外圈需要绘制为圆弧了, 扫描的那种. 从0扫描360°)
    • 绘制文字
    • 设置百分比
    • 设置属性动画
    • 收工.
      介于前面已经写过自定义圆形进度条了, 这次直接就不分步骤写了, 直接上代码了, 有详细的注释. 没有看过的同学请戳下面.
      自定义View_04(小试牛刀)圆形进度条

    1. 新建自定义属性文件 values/attrs.xml

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <declare-styleable name="ProgressBar">
            <attr name="bottomBackground" format="color"/>
            <attr name="topBackground" format="color"/>
            <attr name="roundWidth" format="dimension"/>
            <attr name="progressTextSize" format="dimension"/>
            <attr name="progressTextColor" format="color"/>
        </declare-styleable>
    </resources>
    

    2. 新建Class文件,继承View

    • 继承View
    • 重写三个构造函数
    • 获取自定义属性文件, 给默认值
    • dip 转 px 函数
    • sp 转 px 函数
    • 初始化三个画笔(底部, 顶部, 文字)
    public class ProgressBar extends View {
    
        //底部圆的默认亚瑟
        private int mBottomBackground = Color.RED;
        //顶部圆的默认颜色
        private int mTopBackground = Color.RED;
        //注意哦:这里的10是 dp 需要转为px
        private int mRoundWidth = 10;
        //注意哦:这里的15是 sp 需要转为px
        private float mProgressTextSize = 15;
        //设置文字的默认颜色
        private int mProgressTextColor = Color.RED;
        //三个画笔
        private Paint mBottomPaint, mTopArcPaint, mTextPaint;
    
        public ProgressBar(Context context) {
            this(context, null);
        }
    
        public ProgressBar(Context context, @Nullable AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public ProgressBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            //获取自定义属性
            TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.ProgressBar);
            mBottomBackground = array.getColor(R.styleable.ProgressBar_bottomBackground, mBottomBackground);
            mTopBackground = array.getColor(R.styleable.ProgressBar_topBackground, mTopBackground);
            mProgressTextColor = array.getColor(R.styleable.ProgressBar_progressTextColor, mProgressTextColor);
            mRoundWidth = (int) array.getDimension(R.styleable.ProgressBar_roundWidth, dip2px(mRoundWidth));
            mProgressTextSize = array.getDimensionPixelSize(R.styleable.ProgressBar_progressTextSize, sp2px(mProgressTextSize));
            //初始化三个画笔
            initBottomPaint();
            initTopArcPaint();
            initTextPaint();
            //回收, 一定不要忘了这一步
            array.recycle();
        }
    
        //dip转px,这里为了演示所以才写到这里, 实际开发当中是需要抽到 Utils 类中
        private float dip2px(int dip) {
            return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip, getResources().getDisplayMetrics());
        }
    
        //sp转px
        private int sp2px(float sp) {
            return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp, getResources().getDisplayMetrics());
        }
    
        //初始化底部画笔
        private void initBottomPaint() {
            mBottomPaint = new Paint();
            //设置抗锯齿
            mBottomPaint.setAntiAlias(true);
            //设置画笔宽度
            mBottomPaint.setStrokeWidth(mRoundWidth);
            //设置底部圆的颜色
            mBottomPaint.setColor(mBottomBackground);
            //设置为画笔只描边
            mBottomPaint.setStyle(Paint.Style.STROKE);
        }
    
        //初始化顶部画笔
        private void initTopArcPaint() {
            mTopArcPaint = new Paint();
            //抗锯齿
            mTopArcPaint.setAntiAlias(true);
            //设置顶部画笔宽度
            mTopArcPaint.setStrokeWidth(mRoundWidth);
            //顶部画笔颜色
            mTopArcPaint.setColor(mTopBackground);
            //只描边
            mTopArcPaint.setStyle(Paint.Style.STROKE);
            //设置画笔末端样式为圆形, (底部不设置是因为底部一开始就是一个360°的圆)
            mTopArcPaint.setStrokeCap(Paint.Cap.ROUND);
        }
    
        //初始化文字画笔
        private void initTextPaint() {
            mTextPaint = new Paint();
            //抗锯齿
            mTextPaint.setAntiAlias(true);
            //设置文字画笔颜色
            mTextPaint.setColor(mProgressTextColor);
            //设置文字画笔要绘制文字的大小
            mTextPaint.setTextSize(mProgressTextSize);
        }  
    }
    

    3. 测量

    准备工作做完后, 开始需要测量大小了. 覆写 onMeausr 方法, 开始测量大小

        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            //获取布局文件中设置的宽高
            int width = MeasureSpec.getSize(widthMeasureSpec);
            int height = MeasureSpec.getSize(heightMeasureSpec);
            //获取后,调用 setMeasuredDimension 设置宽高.
            //保证是正方形, 这里我们假设没有设置 padiing, 如果有的话,还需要加上 padiing 的距离.
            setMeasuredDimension(Math.min(width, height), Math.min(width, height));
        }
    

    4. 绘制

    又到了最关键的步骤了. 还记得在 自定义View_04(小试牛刀)圆形进度条 中的步骤吗, 不过现在这里需要绘制底部是一个 360°的圆形,

    • 绘制一个圆形 (底部的)
    • 绘制一个圆弧 (顶部的), 开始位置是0, 扫描的角度等于 当前进度 / 总进度 * 360.
    • 绘制文字.
    • 定义当前进度, 总进度变量. 设置当今进度变量的 set 方法
      上代码
        //总进度
        private int mToalProgress = 100;
        //当前进度
        private int mCurrentProgress = 0;
    
        public synchronized void setCurrentProgress(int currentProgress){
            this.mCurrentProgress = currentProgress;
            invalidate();
        }
    
        /**
         * drawCircle 参数说明
         * drawCircle(float cx, float cy, float radius, Paint paint)
         * cx, 圆心的 x 坐标, 
         * cy, 圆心的 y 坐标
         * radius 圆的半径
         * paint 画笔
         */
        @Override
        protected void onDraw(Canvas canvas) {
            //1.设置中心点,画内圆
            int center = getWidth() / 2;
            // radius 圆的半径 为什么要用 center - 画笔的宽度 / 2 呢 ?, 
            //因为笔触的宽度, 并不是往笔触内侧增加宽度的, 而是外侧一半, 内侧一半
            //最简单,最直白. 意思就是, 加入画笔画的宽度10, 
            //那么画笔落笔的时候是在中间的位置5, 外侧5,内侧5,画笔就是在中间落笔的, 所以要 / 2
            canvas.drawCircle(center, center, center - mRoundWidth / 2, mBottomPaint);
    
            //2.画外圆,不能画圆,只能画圆弧
            if (mToalProgress == 0) {
                return;
            }
    
            float sweepAngle = (float) mCurrentProgress / mToalProgress;
            //设置落笔的左上右下, 需要考虑到画笔宽度 / 2 (原因上面已经说明)
            //因为我们没有设置padiing, 所以左边起始位置落笔的就是 宽度 /2 
            //右边的的就需要总的宽度 -  画笔/2 的距离,得到右侧的坐标
            RectF rect = new RectF(mRoundWidth / 2, mRoundWidth / 2, getWidth() - mRoundWidth / 2, getHeight() - mRoundWidth / 2);
            //参数1: 定义圆弧的形状和大小的范围
            //参数2: 设置圆弧是从哪个角度来顺时针绘画的
            //参数3: 设置圆弧扫过的角度
            //参数4: 圆弧在绘画的时候,是否经过圆心
            //参数5: 画笔
            canvas.drawArc(rect, 0, sweepAngle * 360, false, mTopArcPaint);
    
            //3.绘制进度文字
            String str = ((int)(sweepAngle * 100)) + "%";
            //获取文本的bounds
            Rect textRect = new Rect();
            mTextPaint.getTextBounds(str, 0, str.length(), textRect);
            //获取文本的x坐标, 整个空间的宽度/2 - 文本的宽度/2
            int dx = getWidth() / 2 - textRect.width() / 2;
            //获取文本的y坐标
            Paint.FontMetricsInt fontMetricsInt = mTextPaint.getFontMetricsInt();
            int dy = (fontMetricsInt.bottom - fontMetricsInt.top) / 2 - fontMetricsInt.bottom;
            //计算基线
            int baseLine = getHeight() / 2 + dy;
            canvas.drawText(str, dx, baseLine, mTextPaint);
        }
    

    5. 调用

    布局文件

    <?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=".MainActivity">
        <org.zyq.ProgressBar
            android:id="@+id/progressBar"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_margin="10dp"
            app:bottomBackground="@color/colorPrimary"
            app:progressTextColor="@color/colorPrimaryDark"
            app:progressTextSize="20sp"
            app:roundWidth="20dp"
            app:topBackground="@color/colorAccent" />
        <Button
            android:onClick="start"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_below="@id/progressBar"
            android:layout_centerInParent="true"
            android:layout_marginTop="20dp"
            android:text="开始" />
    </RelativeLayout>
    

    MainActivity

    public class MainActivity extends AppCompatActivity {
    
        ProgressBar progressBar;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            progressBar = findViewById(R.id.progressBar);
        }
        //设置属性动画
        public void start(View view) {
            ValueAnimator valueAnimator = ObjectAnimator.ofFloat(0, 100);
            valueAnimator.setDuration(2000);
            valueAnimator.start();
            valueAnimator.setInterpolator(new DecelerateInterpolator());
            valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    float value = (float) animation.getAnimatedValue();
                    progressBar.setCurrentProgress((int)value);
                }
            });
        }
    }
    

    你们的进度条出来了吗? , 反正我的是出来了, 收工睡觉! Q_Q

    相关文章

      网友评论

          本文标题:自定义View_06(小试牛刀)还是圆形进度条

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