• Android 自定义view --圆形百分比(进度条)


    转载请注明出处:http://blog.csdn.net/wingichoy/article/details/50334595

    注:本文由于是在学习过程中写的,存在大量问题(overdraw onDraw new对象),请读者们不要被误导!!解决办法见后面的博客。

    起因

        最近公司项目有需求需要用到轻量级图表如下图,是一些简单的扇形图,圆形图,折线图,虽然有好用的三方库MPChart
        (教程地址http://blog.csdn.net/wingichoy/article/details/50428246),
        但是过于庞大,像这样的简约的界面明显不合适,又因为许久没有坚持写博客,觉得自己很是堕落,深知自学不易,于是便想写一些博客来帮助后来像我一样的初学者,所以便想把这些效果写一个系列。其中包括:
        1.圆形百分比图表  
        2.简易折线图图表
        3.简易柱状图图表
    

    这里写图片描述

    效果图

    说了这么多没有效果图还是废话,效果图如下:
    这里写图片描述

    预备知识

    本篇博客的预备知识内容有:
    1.自定义属性
    2.自定义view
    关于以上两点,可以查看 Hongyang大神的博客:自定义View(一)

    知识点补充

    如果你已经掌握了以上的基本知识,那么可以开始本篇博客的内容啦。但是还是有几点内容补充。

    1.MeasureSpec对象包含了测量的模式和大小。他是一个32位的int值,其中高两位为测量的模式,低30位是测量的大小。采用位运算和运行效率有关。所以可以从一个MeasureSpec对象分别获取模式和值 如:

    //获取模式  值为 EXACTLY AT_MOST UNSPECIFIED
    int specMode = MeasureSpec.getMode(measureSpec);
    //获取测量值
    int specSize = MeasureSpec.getSize(measureSpec);

    2.画扇形,调用drawArc()方法,其中第一个参数是圆所在的矩形,第二个参数是开始的弧度,值得主意的是,最顶的弧度是270度,而地图东方向的弧度为0.第三个参数是需要画的弧度,第四个参数是一个布尔值,表示是否连接圆心,画为扇形,第五个是Paint,即所需要的画笔。

    canvas.drawArc(rect, 270, mEndAngle, true, sectorPaint);

    现在开始吧

    1.首先分析需求,我们的圆形进度总共分为三层,
    最底层是一个大园形,作为底色。
    第二层是一个扇形,大小跟底色一样,用来表示进度。
    第三层是一个小圆形,比背景色小,用于实现弧线的效果。
    
    2.新建一个类叫做CirclePercentView.java 他继承与View类重写他的构造函数,来获取他的自定义属性。
    
        public CirclePercentView(Context context) {
            this(context, null);
        }
    
        public CirclePercentView(Context context, AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public CirclePercentView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            // 获取自定义属性
            TypedArray a=context.obtainStyledAttributes(attrs,R.styleable.CirclePercentView, defStyleAttr, 0);
    
    //获取色带的宽度
            mStripeWidth = a.getDimension(R.styleable.CirclePercentView_stripeWidth, PxUtils.dpToPx(30, context));
            //获取当前的百分比
            mCurPercent = a.getInteger(R.styleable.CirclePercentView_percent, 0);
            //获取小园的颜色
            mSmallColor = a.getColor(R.styleable.CirclePercentView_smallColor,0xffafb4db);
            //获取大圆的颜色
            mBigColor = a.getColor(R.styleable.CirclePercentView_bigColor,0xff6950a1);
            //获取中心文字的大小
            mCenterTextSize = a.getDimensionPixelSize(R.styleable.CirclePercentView_centerTextSize,PxUtils.spToPx(20,context));
            //获取园的半径
            mRadius = a.getDimensionPixelSize(R.styleable.CirclePercentView_radius,PxUtils.dpToPx(100,context));
        }   
    3.获取到了自定义属性以后,我们来测量这个view,告诉系统这个view有多大
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            //获取测量模式
            int widthMode = MeasureSpec.getMode(widthMeasureSpec);
            int heightMode = MeasureSpec.getMode(heightMeasureSpec);
            //获取测量大小
            int widthSize = MeasureSpec.getSize(widthMeasureSpec);
            int heightSize = MeasureSpec.getSize(heightMeasureSpec);
            //如果为确定大小值,则圆的半径为宽度/2
            if (widthMode == MeasureSpec.EXACTLY && heightMode == MeasureSpec.EXACTLY) {
                mRadius = widthSize / 2;
                x = widthSize / 2;
                y = heightSize / 2;
                mWidth = widthSize;
                mHeight = heightSize;
            }
            //如果为wrap_content 那么View大小为圆的半径大小*2
            if(widthMode == MeasureSpec.AT_MOST&&heightMode ==MeasureSpec.AT_MOST){
                mWidth = (int) (mRadius*2);
                mHeight = (int) (mRadius*2);
                x = mRadius;
                y = mRadius;
    
            }
            //设置视图的大小
            setMeasuredDimension(mWidth,mHeight);
        }
    4.测量完成后,我们就要开始画view了。首先画大圆,之后画扇形图,最后画小圆盖住
    就有了圆形进度条的效果,之后把百分比进度文字画在上面就大功告成啦。
    

    具体步骤如图:这里写图片描述这里写图片描述这里写图片描述

    
        @Override
        protected void onDraw(Canvas canvas) {
    
    
            mEndAngle = (int) (mCurPercent * 3.6);
            //绘制大圆
            Paint bigCirclePaint = new Paint();
            //消除锯齿
            bigCirclePaint.setAntiAlias(true);
            bigCirclePaint.setColor(mBigColor);
            //x,y 为圆心坐标 mRadius为半径
            canvas.drawCircle(x, y, mRadius, bigCirclePaint);
    
             //饼状图
            Paint sectorPaint = new Paint();
            sectorPaint.setColor(mSmallColor);
            sectorPaint.setAntiAlias(true);
            RectF rect = new RectF(0, 0, mWidth, mHeight);
            //参数说明见知识补充
            canvas.drawArc(rect, 270, mEndAngle, true, sectorPaint);
    
    
            //绘制小圆
            Paint smallCirclePaint = new Paint();
            smallCirclePaint.setAntiAlias(true);
            smallCirclePaint.setColor(mBigColor);
            //圆心是相同的 不过半径有差别,这个差别就是我们的 色带宽度
            canvas.drawCircle(x, y, mRadius - mStripeWidth, smallCirclePaint);
    
    
            //绘制文本
            Paint textPaint = new Paint();
            String text = mCurPercent + "%";
    
            textPaint.setTextSize(mCenterTextSize);
            //测量字符串长度
            float textLength = textPaint.measureText(text);
    
            textPaint.setColor(Color.WHITE);
            //把文本画在圆心居中
            canvas.drawText(text, x - textLength/2, y, textPaint);
    }
    值得注意的是,在编写的过程中,文字的位置总是不对,但是根据画图计算,
    公式是没错的,最后才发现我先测量了字符串长度,又修改了字体的大小导致偏差。所以一定要注意这些细节,先改变字体大小,再去测量。
    5.添加setPercent方法
    
    public void setPercent(int percent) {
    //百分比不可能超过100 如果超过100则抛出异常 
            if (percent > 100) {
                throw new IllegalArgumentException("percent must less than 100!");
            }
    
            setCurPercent(percent);
    
    
        }
     //内部设置百分比 用于动画效果
        private void setCurPercent(int percent) {
    
            mPercent = percent;
    
            new Thread(new Runnable() {
                @Override
                public void run() {
                    for(int i =0;i<mPercent;i++){
                        try {
                            Thread.sleep(15);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        mCurPercent = i;
                        CirclePercentView.this.postInvalidate();
                    }
                    }
    
            }).start();
    
        }
    这里在setPercent改变的时候,开启一个线程,来不断重绘view,达到一个动画效果。其实view的动画效果就是不停地重绘
    (即执行onDraw(),因为每次执行onDraw()里面的参数会变化,所以会看到不同的画面)。这样便完成了一个轻量级的圆形百分比进度条自定义View。
    

    你可以在https://github.com/githubwing/CirclePercentView克隆到源码。(求star)
    你还可以看本系列第二篇博客:简易折线图,链接:http://blog.csdn.net/wingichoy/article/details/50434634

  • 相关阅读:
    Intellij IDEA 打开文件tab数量限制的调整
    Mysql处理中文乱码的问题
    MIT算法导论笔记
    算法导论-排序(一)-插入排序、归并排序
    leetcode题解:Search for a Range (已排序数组范围查找)
    leetcode 题解:Merge Sorted Array(两个已排序数组归并)
    leetcode题解:Construct Binary Tree from Inorder and Postorder Traversal(根据中序和后序遍历构造二叉树)
    leetcode题解:Construct Binary Tree from Preorder and Inorder Traversal (根据前序和中序遍历构造二叉树)
    c++11 std::prev、std::next、std::advance与auto 使用
    (转)指针的引用(*&)与指针的指针(**)
  • 原文地址:https://www.cnblogs.com/muyuge/p/6333562.html
Copyright © 2020-2023  润新知