自定义view(结合刻度盘学习)

 先上效果图

 

一、View的测量(刻度盘的大小测量)

  在现实生活中,我们如果要去画一个图形,那么便要知道它的大小和位置。所以android绘图时需要我们对view进行测量。android为我们提供了onMeasure()方法来帮助我们去测量一个view,我们只需要重写onMeasur()方法,将我们测算长宽设置给setMeasuredDimension()。

     @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //根据模式测算长宽
        ...

        setMeasuredDimension(width,height);
    }

  我们首先测量我们最开始弧形的大小。即图中的样例。

   

  @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //根据模式测算长宽,这里获取的尺寸便是在xml文件中指定的尺寸
        int width = MeasureSpec.getSize(widthMeasureSpec);
        int height = MeasureSpec.getSize(heightMeasureSpec);
        //以最小值为刻度区域正方形的长
        len = Math.min(width, height);
        //确定圆弧所在的矩形区域
        oval = new RectF(0, 0, len, len);
        radius = len/2;
        //设置测量高度和宽度
        setMeasuredDimension(len,len);
    }

二、View的绘制(简单绘制出我们需要的弧形)

 测算完成后我们需要将他在画面上显示出来,android为我们提供了onDraw()方法,来绘制view。


@Override
protected void onDraw(Canvas canvas) { super.onDraw(canvas);
     //弧形的绘画查看api canvas.drawArc(oval,startAngle,sweepAngle,useCenter,paint); }

不了解canvas的可以去看看canvas的绘制API

现在在布局文件中设置后,就可以看见我们最开始的view了。

三、完善我们的绘制,为仪表盘添加刻度。

    private void drawViewLine(Canvas canvas) {
        canvas.save();
        //移动canvas
        canvas.translate(radius,radius);
        //旋转canvas
        canvas.rotate(30);
        //普通刻度
        Paint linePatin=new Paint();
        //设置普通刻度画笔颜色
        linePatin.setColor(Color.WHITE);
        //线宽
        linePatin.setStrokeWidth(2);
        //设置画笔抗锯齿
        linePatin.setAntiAlias(true);
       /* //画一条刻度线
        canvas.drawLine(0,radius,0,radius-40,linePatin);*/
        //画101条刻度线
        //确定每次旋转的角度
        float rotateAngle=sweepAngle/99;
        //绘制需要有颜色部分的画笔
        Paint targetLinePatin=new Paint();
        targetLinePatin.setColor(Color.GREEN);
        targetLinePatin.setStrokeWidth(2);
        targetLinePatin.setAntiAlias(true);
        //记录已经绘制过的有色部分范围(角度float)
        float hasDraw=0;
        for(int i=0;i<100;i++){
            if (hasDraw <= targetAngle && targetAngle != 0) {
                //计算已经绘制的比例
                canvas.drawLine(0, radius, 0, radius - 40, targetLinePatin);
            } else {
                canvas.drawLine(0,radius,0,radius-40,linePatin);
            }
            hasDraw += rotateAngle;
            canvas.rotate(rotateAngle);
        }

        //操作完成后恢复状态
        canvas.restore();
    }

 如图

 

四、完善onMeasure()测量

  我们先看看在布局中的CircleView

  原先

<com.example.tyr.circleviewtest.CircleView
android:layout_width="match_parent"
android:layout_height="match_parent"
/>

  如图

  

  改成Button

  <Button
           android:layout_width="match_parent"
           android:layout_height="match_parent"
         />

  现在,我么修改CircleView改为wrap_content,如图

  可以发现没有改变

  对比button的wrap_content可以发现如图,

   

  所以我们需要在onMeasure()方法中,对view的模式进行区别测量。

五、MeasureSpec类

  android系统为我们提供了一个短小精悍的类MeasureSpec。它是一个32位的int值,高两位为测量模式,低30位为测量大小。

  测量模式分为三种:

  1、EXACTLY--系统的默认模式。

      精确模式,当我们将width和height指定100dp、200dp等精确值时或是match_parent属性时系统使用EXACTLY模式

  2、AT_MOST

    最大值模式,当height和width属性指定为wrap_content时,控件大小随子控件或内容的变化而变化,控件大小只要不超过父控件允许的最大尺寸即可。

  3、UNSPECIFIED

      不指定大小测量模式,view想多大就多大,通常在绘制自定义view时才会用。

   将onMeasure()方法进行修改,适应wrap_content

 @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //根据模式测算长宽
        int width = measureSize(widthMeasureSpec);
        int height = measureSize(heightMeasureSpec);
        //以最小值为刻度区域正方形的长
        len = Math.min(width, height);
        //确定圆弧所在的矩形区域
        oval = new RectF(0, 0, len, len);
        radius = len/2;
        //设置测量高度和宽度
        setMeasuredDimension(len, len);
    }

    private int measureSize(int measureSpec){
     //差不多固定的格式
int reslut = 0; int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec);    if (specMode==MeasureSpec.EXACTLY){ reslut = specSize; }else{ reslut = 200; if (specMode==MeasureSpec.AT_MOST){ reslut = Math.min(reslut,specSize); } } return reslut; }

    

六、自定义属性

  我们在xml文件中使用的时候往往会使用一些属性类似width和height等等,那么我们便需要为我们的刻度盘添加新的属性。

<resources> 
    <declare-styleable name="CircleView">
        <attr name="pointDisArc" format="dimension"></attr>
        <attr name="textSize" format="dimension"></attr>
    </declare-styleable>
</resources>

     在构造方法中处理

 public CircleView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        parseAttr(context, attrs, defStyleAttr);
        initPaint();
    }

    private void parseAttr(Context context, AttributeSet attrs, int defStyleAttr) {
        TypedArray typedArray = context.obtainStyledAttributes(attrs,R.styleable.CircleView,defStyleAttr,0);
        pDisArc = typedArray.getDimensionPixelSize(R.styleable.CircleView_pointDisArc,dp2px(context,20));
        textSize = typedArray.getDimensionPixelSize(R.styleable.CircleView_textSize,dp2px(context,20));
        typedArray.recycle();
    }
  <com.example.tyr.circleviewtest.CircleView
           android:id="@+id/cicleview"
           android:layout_width="match_parent"
           android:layout_height="match_parent"
           circle:textSize="20sp"
           circle:pointDisArc="20dp"
         />

 到目前为止view的自定义差不多完成了,也可以将刻度盘设计的更加精巧一些,下一节开始viewgroup的绘制,涉及到新的方法onLayout()。

  DEMO

posted @ 2017-08-29 11:41  YRLeaner  阅读(582)  评论(0编辑  收藏  举报