自定义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()。