先上效果图

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

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

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

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

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

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

  @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了。

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

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

    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();
}

 如图

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

四、完善onMeasure()测量

  我们先看看在布局中的CircleView

  原先

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

  如图

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

  改成Button

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

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

  可以发现没有改变

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

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

  所以我们需要在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;
}

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

六、自定义属性

  我们在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

05-07 15:14