手把手带你画一个 时尚仪表盘 Android 自定义View

来源:互联网 发布:python 读取文件跳过 编辑:程序博客网 时间:2024/05/16 01:26
标签: 仪表盘Android自定义view
5576人阅读 评论(22)收藏举报
本文章已收录于:
分类:
作者同类文章X


    拿到美工效果图,咱们程序员就得画得一模一样。 为了不被老板喷,只能多练啊。

    听说你觉得前面几篇都so easy,那今天就带你做个相对比较复杂的。


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

    注意:每一篇博客都是建立在之前博客的基础知识上的,如果你刚接触自定义view。可以来说说自定义view简单学习的方式这里看我以前的文章。记录了我学习自定义view的过程,而且前几篇博客或多或少犯了一些错误(重复绘制,onDraw里new对象等等)。这里我并不想改正博文中的错误,因为些错误是大家经常会犯的,后来的博客都有指出这些错误,以及不再犯,这是一个学习的过程。所以我想把错误的经历记录下来。等成为高手 回头看看当年的自己是多么菜。。也会有成就感。。

    今天的效果图如下(左边是ui图 右边是实现图):


    自我感觉总体效果还不错,至少大概画得一样了。上一个动态图:


    其实这个效果实现起来也不是很难,就是计算坐标,弧度之类的可能会比较麻烦,这里分享写这个其中一张手稿,请无视掉很丑的字,其实做自定义view 还是要在纸上多画。所以希望大家也能这么画画,思路会很顺。



    好的了,废话不多说,快开始。

    首先自定义属性  构造函数,测量什么的 你肯定已经很熟练 直接贴代码了,注释写的很清楚

    [java] view plain copy
    print?在CODE上查看代码片派生到我的代码片
    1. public class PanelView extends View {  
    2.     private int mWidth;  
    3.     private int mHeight;  
    4.   
    5.     private int mPercent;  
    6.   
    7.     //刻度宽度  
    8.     private float mTikeWidth;  
    9.   
    10.     //第二个弧的宽度  
    11.     private int mScendArcWidth;  
    12.   
    13.     //最小圆的半径  
    14.     private int mMinCircleRadius;  
    15.   
    16.     //文字矩形的宽  
    17.     private int mRectWidth;  
    18.   
    19.     //文字矩形的高  
    20.     private int mRectHeight;  
    21.   
    22.   
    23.     //文字内容  
    24.     private String mText = "";  
    25.   
    26.     //文字的大小  
    27.     private int mTextSize;  
    28.   
    29.     //设置文字颜色  
    30.     private int mTextColor;  
    31.     private int mArcColor;  
    32.   
    33.     //小圆和指针颜色  
    34.     private int mMinCircleColor;  
    35.   
    36.     //刻度的个数  
    37.     private int mTikeCount;  
    38.   
    39.     private Context mContext;  
    40.   
    41.     public PanelView(Context context) {  
    42.         this(context, null);  
    43.     }  
    44.   
    45.     public PanelView(Context context, AttributeSet attrs) {  
    46.         this(context, attrs, 0);  
    47.     }  
    48.   
    49.     public PanelView(Context context, AttributeSet attrs, int defStyleAttr) {  
    50.         super(context, attrs, defStyleAttr);  
    51.         mContext = context;  
    52.         TypedArray a = context.obtainStyledAttributes(attrs,R.styleable.PanelView,defStyleAttr,0);  
    53.         mArcColor = a.getColor(R.styleable.PanelView_arcColor, Color.parseColor("#5FB1ED"));  
    54.         mMinCircleColor = a.getColor(R.styleable.PanelView_pointerColor,Color.parseColor("#C9DEEE"));  
    55.         mTikeCount = a.getInt(R.styleable.PanelView_tikeCount,12);  
    56.         mTextSize = a.getDimensionPixelSize(PxUtils.spToPx(R.styleable.PanelView_android_textSize,mContext),24);  
    57.         mText = a.getString(R.styleable.PanelView_android_text);  
    58.         mScendArcWidth = 50;  
    59.     }  
    60.   
    61.   
    62.     @Override  
    63.     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
    64.         int widthSize = MeasureSpec.getSize(widthMeasureSpec);  
    65.         int widthMode = MeasureSpec.getMode(widthMeasureSpec);  
    66.         int heightSize = MeasureSpec.getSize(heightMeasureSpec);  
    67.         int heightMode = MeasureSpec.getMode(heightMeasureSpec);  
    68.         if (widthMode == MeasureSpec.EXACTLY) {  
    69.             mWidth = widthSize;  
    70.         }else {  
    71.             mWidth = PxUtils.dpToPx(200,mContext);  
    72.         }  
    73.   
    74.   
    75.         if (heightMode == MeasureSpec.EXACTLY) {  
    76.             mHeight = heightSize;  
    77.         }else {  
    78.             mHeight = PxUtils.dpToPx(200,mContext);  
    79.         }  
    80.         Log.e("wing",mWidth+"");  
    81.         setMeasuredDimension(mWidth, mHeight);  
    82.     }  
    自定义属性attr.xml

    [html] view plain copy
    print?在CODE上查看代码片派生到我的代码片
    1. <?xml version="1.0" encoding="utf-8"?>  
    2. <resources>  
    3.     <declare-styleable name="PanelView">  
    4.         <attr name="arcColor" format="color"/>  
    5.         <attr name="arcWidth" format="dimension"/>  
    6.         <attr name="android:text"/>  
    7.         <attr name="tikeCount" format="integer"/>  
    8.         <attr name="pointerColor" format="color"/>  
    9.         <attr name="android:textSize"/>  
    10.     </declare-styleable>  
    11. </resources>  



    之后来重头戏,也就是绘制。就像画画一样,再复杂的view也是一笔一笔画出来的。所以我们把这个view分解。

    大概分解成如下:1.最外面的弧   2.里面的粗弧   3.中间小圆   4.最小的圆  5.刻度   6.指针  7.矩形  8.文字

    相信让你分开画一定难不倒你。那组合在一起 就是这个view啦。下面开始我们的ondraw()


    按照这个分解来:

    1.绘制最外面的弧   这里需要注意的一点是,如果想让这个圆在view里 记得减去画笔宽度的一半  因为半径是从圆心到画笔宽度的中间算的,所以这里画弧的矩形是  new RectF(strokeWidth, strokeWidth, mWidth - strokeWidth, mHeight - strokeWidth)

    [java] view plain copy
    print?在CODE上查看代码片派生到我的代码片
    1. Paint p = new Paint();   
    2.    int strokeWidth = 3;  
    3.    p.setStrokeWidth(strokeWidth);  
    4.    p.setAntiAlias(true);  
    5.    p.setStyle(Paint.Style.STROKE);  
    6.    p.setColor(mArcColor);  
    7.    //最外面线条  
    8.    canvas.drawArc(new RectF(strokeWidth, strokeWidth, mWidth - strokeWidth, mHeight - strokeWidth), 145250false, p);  

    画出来是这样的效果。



    2.绘制里面的粗弧,这里比较麻烦的就是需要分为四段,看图:


    因为大圆和里面粗弧的长短不一致,这里使用百分比来计算 所以会造成指针偏差,那么这里把 1、2两个部分固定来画,然后是3 充满的部分,用百分比来计算需要画多少度,最后是4 空白的部分。

    首先把粗弧的矩形画出来,这里固定了比大弧半径少50(这里其实可以改进,你可以改成动态的让他更灵活),然后计算出百分比。

    [java] view plain copy
    print?在CODE上查看代码片派生到我的代码片
    1. RectF secondRectF = new RectF(strokeWidth + 50, strokeWidth + 50, mWidth - strokeWidth - 50, mHeight - strokeWidth - 50);  
    2.         float secondRectWidth = mWidth - strokeWidth - 50 - (strokeWidth + 50);  
    3.         float secondRectHeight = mHeight - strokeWidth - 50 - (strokeWidth + 50);  
    4.         float percent = mPercent / 100f;  

    接下来绘制1弧,先算出fill充满部分的度数,因为是突出的,所以如果百分比为0,突出左端为白色 如果不为零,则和充满颜色统一。

    [java] view plain copy
    print?在CODE上查看代码片派生到我的代码片
    1.         //充满的圆弧的度数    -5是大小弧的偏差  
    2.         float fill = 250 * percent ;  
    3.   
    4.         //空的圆弧的度数  
    5.         float empty = 250 - fill;  
    6. //        Log.e("wing", fill + "");  
    7.   
    8.         if(percent==0){  
    9.             p.setColor(Color.WHITE);  
    10.         }  
    11.         //画粗弧突出部分左端  
    12.   
    13.         canvas.drawArc(secondRectF,135,11,false,p);  

    然后绘制2弧 也就是fill充满的弧,

    [java] view plain copy
    print?在CODE上查看代码片派生到我的代码片
    1. canvas.drawArc(secondRectF, 145, fill, false, p);  

    接下来是3弧,也就是empty未充满的弧,是白色的

    [java] view plain copy
    print?在CODE上查看代码片派生到我的代码片
    1. p.setColor(Color.WHITE);  
    2.        //画弧胡的未充满部分  
    3.        canvas.drawArc(secondRectF, 145 + fill, empty, false, p);  

    最后,画出右边突出的4弧, 如果百分比为100 那么和充满的颜色一致,否则为白色

    [java] view plain copy
    print?在CODE上查看代码片派生到我的代码片
    1. //画粗弧突出部分右端  
    2.        if(percent == 1){  
    3.            p.setColor(mArcColor);  
    4.        }  
    5.        canvas.drawArc(secondRectF,144+fill+empty,10,false,p);  

    这样粗弧也就画完了 来看看效果,就画了两条弧线(实际是5条),就成型了。



    3.中间的小圆外圈,他的圆心不用多说 是整个view的中心

    [java] view plain copy
    print?在CODE上查看代码片派生到我的代码片
    1. p.setColor(mArcColor);  
    2.   
    3.   
    4. //绘制小圆外圈  
    5. p.setStrokeWidth(3);  
    6. canvas.drawCircle(mWidth / 2, mHeight / 230, p);  

    4.绘制内圆,圆心一样的,半径和画笔粗度改变一下

    [java] view plain copy
    print?在CODE上查看代码片派生到我的代码片
    1. //绘制小圆内圈  
    2.   
    3. p.setColor(mMinCircleColor);  
    4. p.setStrokeWidth(8);  
    5. mMinCircleRadius = 15;  
    6. canvas.drawCircle(mWidth / 2, mHeight / 2, mMinCircleRadius, p);  


    5.刻度  刻度处理起来可能比较麻烦,用三角函数算坐标啊 循环画出来。。 这里提供一种比较简单的方法:旋转画布。

    首先引入一个概念,什么叫旋转画布呢,就是把你的画布旋转。。经过测试,旋转以后,整个坐标轴都会对应旋转,一张图举例说明下。


    大概就是这个意思,画布旋转之后 坐标系也就旋转了,但是原来的图像还在,所以说你比如这个点 x,y旋转前在这个位置, 那么旋转后就是另外一个位置了,但是他们的坐标是相同的。 所以刻度也可以考这种方法画。我们只要画出最顶端的刻度 然后旋转就可以了。


    绘制第一段刻度, 然后总共是250的弧度  计算出每个刻度的度数     用250除以刻度数mTikeCount,就是每次旋转的度数。接下来把画布逐步旋转,按照原坐标绘制,即可绘制出右半部分刻度。  注意:为了让之后的绘制正常,务必把画布转回原来的位置

    [java] view plain copy
    print?在CODE上查看代码片派生到我的代码片
    1. //绘制刻度!  
    2.   
    3. p.setColor(mArcColor);  
    4. //绘制第一条最上面的刻度  
    5. mTikeWidth = 20;  
    6. p.setStrokeWidth(3);  
    7.   
    8. canvas.drawLine(mWidth / 20, mWidth / 2, mTikeWidth, p);  
    9. //旋转的角度  
    10. float rAngle = 250f / mTikeCount;  
    11. //通过旋转画布 绘制右面的刻度  
    12. for (int i = 0; i < mTikeCount / 2; i++) {  
    13.     canvas.rotate(rAngle, mWidth / 2, mHeight / 2);  
    14.     canvas.drawLine(mWidth / 20, mWidth / 2, mTikeWidth, p);  
    15. }  
    16.   
    17. //现在需要将将画布旋转回来  
    18. canvas.rotate(-rAngle * mTikeCount / 2, mWidth / 2, mHeight / 2);  


    左半部分同理,需要改变的度数为负 就好了

    [java] view plain copy
    print?在CODE上查看代码片派生到我的代码片
    1. //通过旋转画布 绘制左面的刻度  
    2. for (int i = 0; i < mTikeCount / 2; i++) {  
    3.     canvas.rotate(-rAngle, mWidth / 2, mHeight / 2);  
    4.     canvas.drawLine(mWidth / 20, mWidth / 2, mTikeWidth, p);  
    5. }  
    6.   
    7.   
    8. //现在需要将将画布旋转回来  
    9. canvas.rotate(rAngle * mTikeCount / 2, mWidth / 2, mHeight / 2);  


    6.指针   指针的绘制和刻度相似,先算出来百分比所占的度数 然后根据 是否大于50%来旋转画布。

    指针的起终点是 总view高度的一半 粗弧矩形的一半 加上小圆,前面坐标讲解了那么,这个也一样,自己拿起笔算一算。


    注意这里画布旋转我通过计算得出一个公式 250 * percent - 250/2。

    如果小于50% 则为负   如果大于50%则为正,然后进行旋转。

    切忌最后一定要将画布转回来。

    [java] view plain copy
    print?在CODE上查看代码片派生到我的代码片
    1. //绘制指针  
    2.   
    3. p.setColor(mMinCircleColor);  
    4. p.setStrokeWidth(4);  
    5.   
    6.   
    7.   
    8. //按照百分比绘制刻度  
    9. canvas.rotate(( 250 * percent - 250/2), mWidth / 2, mHeight / 2);  
    10.   
    11. canvas.drawLine(mWidth / 2, (mHeight / 2 - secondRectHeight / 2) + mScendArcWidth / 2 + 2, mWidth / 2, mHeight / 2 - mMinCircleRadius, p);  
    12.   
    13. //将画布旋转回来  
    14. canvas.rotate(-( 250 * percent - 250/2), mWidth / 2, mHeight / 2);  


    接下来就是画矩形和文字。没什么好说的了,坐标也是X周围mWidth/2   y轴自己根据圆心微调一个距离

    [java] view plain copy
    print?在CODE上查看代码片派生到我的代码片
    1. //绘制矩形  
    2.     p.setStyle(Paint.Style.FILL);  
    3.     p.setColor(mArcColor);  
    4.     mRectWidth = 60;  
    5.     mRectHeight = 25;  
    6.   
    7.     //文字矩形的最底部坐标  
    8.     float rectBottomY = mHeight/2 + secondRectHeight/3+mRectHeight;  
    9.     canvas.drawRect(mWidth/2-mRectWidth/2,mHeight/2 + secondRectHeight/3,mWidth/2+mRectWidth/2,rectBottomY,p);  
    10.   
    11.   
    12.     p.setTextSize(mTextSize);  
    13.     mTextColor = Color.WHITE;  
    14.     p.setColor(mTextColor);  
    15.     float txtLength = p.measureText(mText);  
    16.     canvas.drawText(mText,(mWidth-txtLength)/2,rectBottomY + 40,p);  
    17.   
    18.     super.onDraw(canvas);  


    这样完成了整个view的绘制。


    下面要做的就是为了方便使用者,提供一些设置属性的方法。

    [java] view plain copy
    print?在CODE上查看代码片派生到我的代码片
    1. /** 
    2.     * 设置百分比 
    3.     * @param percent 
    4.     */  
    5.    public void setPercent(int percent) {  
    6.        mPercent = percent;  
    7.        invalidate();  
    8.    }  
    9.   
    10.    /** 
    11.     * 设置文字 
    12.     * @param text 
    13.     */  
    14.    public void setText(String text){  
    15.        mText = text;  
    16.        invalidate();  
    17.    }  
    18.   
    19.    /** 
    20.     * 设置圆弧颜色 
    21.     * @param color 
    22.     */  
    23.   
    24.    public void setArcColor(int color){  
    25.        mArcColor = color;  
    26.   
    27.        invalidate();  
    28.    }  
    29.   
    30.   
    31.    /** 
    32.     * 设置指针颜色 
    33.     * @param color 
    34.     */  
    35.    public void setPointerColor(int color){  
    36.        mMinCircleColor = color;  
    37.   
    38.        invalidate();  
    39.    }  
    40.   
    41.    /** 
    42.     * 设置文字大小 
    43.     * @param size 
    44.     */  
    45.    public void setTextSize(int size){  
    46.        mTextSize = size;  
    47.   
    48.        invalidate();  
    49.    }  
    50.   
    51.    /** 
    52.     * 设置粗弧的宽度 
    53.     * @param width 
    54.     */  
    55.    public void setArcWidth(int width){  
    56.        mScendArcWidth = width;  
    57.   
    58.        invalidate();  
    59.    }  


    大功告成!!!一个看似复杂的view  经过我们一步一步绘制遍完成了。

    其实技术的养成也是这样,只要一步一步脚踏实地的去练习,我相信总有一天我能成为大神。


    本项目地址 :PanelView   求关注  求评论  求star!!!!!!

    0 0
    原创粉丝点击