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

因为最近被公司派去做 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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,132评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,802评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,566评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,858评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,867评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,695评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,064评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,705评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,915评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,677评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,796评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,432评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,041评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,992评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,223评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,185评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,535评论 2 343

推荐阅读更多精彩内容