最近一直在学自定义view,博大精深,感觉如果向底层看的话,功力不够且时间不允许,所以一直都是停留在怎么实现自定义view上,而为何会这么实现以及差异并没有考虑的很清晰,因为面向对象编程以及封装,都是让我们将功能抽出来使用,而不是去考虑每个功能里是怎么实现的,所以很苦恼,还是先实现吧,至于原理慢慢理解吧!
这是自定义View学习的第三篇,学习主要是在项目的基础上来学,并不是一点点的基础死扣,因为你看懂,敲出来一个自定义view的代码,你就会慢慢有所理解,包括中间的一些计算,所以还是建议大家先去敲代码,自己写不出来,就把别人的代码敲一遍,去理解一遍,比仅仅去看会领悟的更多,这就有点书读百遍,其义自现的意思,哈哈,一点浅见,谁让我笨呢。
今天讲一下自定义刻度尺的实现,咋听刻度尺,难度并不高,但是对于刚刚开始动手敲,摆脱Ctrl+c/Ctrl+z的同学来说还是有些难度的。
看看我的实现效果吧:
横向显示10cm,模拟器暂时找不到横屏,就这样吧!@_@
思路呢?
刚开始敲的时候,先明确思路,磨刀不误砍柴工,进去就敲最后敲得自己也糊涂了!
1:画刻度尺 2:画刻度数 3:画红线标记 4:处理滑动
画刻度尺
canvas.save(); for (int i = min; i < max; i++) { if (i % 10 == 0) { //起点x坐标10像素,画厘米线 canvas.drawLine(10, 0, 10, 72, mLinePaint); String text = i / 10 + ""; Rect rect = new Rect(); float txtWidth = mTextPaint.measureText(text); mTextPaint.getTextBounds(text, 0, text.length(), rect); canvas.drawText(text, 10 - txtWidth / 2, 72 + rect.height() + 10, mTextPaint); } else if (i % 5 == 0) { //每隔0.5cm画间隔线 canvas.drawLine(10, 0, 10, 64, mLinePaint); } else { //画毫米线 canvas.drawLine(10, 0, 10, 48, mLinePaint); } //每隔18像素移动一次,达到画线效果 canvas.translate(18, 0); } canvas.restore();画刻度数
if (i % 10 == 0) { //起点x坐标10像素,画厘米线 canvas.drawLine(10, 0, 10, 72, mLinePaint); //计算刻度数 String text = i / 10 + ""; Rect rect = new Rect(); //获取文本宽度 float txtWidth = mTextPaint.measureText(text); mTextPaint.getTextBounds(text, 0, text.length(), rect); //画字 canvas.drawText(text, 10 - txtWidth / 2, 72 + rect.height() + 10, mTextPaint); }画红线标记
看效果图:
很明显,是一条线,外加一个圆。计算好距离就ok。
//画线 canvas.drawLine(progrees, 0, progrees, 160, mRulerPaint); //线下画圆 canvas.drawCircle(progrees, 170, 10, mRulerPaint); BigDecimal bd = new BigDecimal((progrees - 18) / 180); bd = bd.setScale(1, BigDecimal.ROUND_HALF_UP); //计算刻度数,保留一位小数,单位cm mTextPaint.setTextSize(48); canvas.drawText(bd.floatValue() + "cm", 500, 400, mTextPaint);progrees是手指触摸的x轴坐标,线高160,圆心y轴坐标170,半径10即可。
处理滑动
@Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: isCanMove = true; break; case MotionEvent.ACTION_MOVE: if (!isCanMove) { return false; } //前面0坐标线从10像素开始,故而计算的时候要-10 float x = event.getX() - 10; progrees = x; //刷新 invalidate(); break; } return true; }绘画步骤就是这么多。
完整代码:
package com.example.com.testview; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.RectF; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import java.math.BigDecimal; /** * 刻度尺 * Created by Administrator on 2016/9/14. */ public class ScaleView extends View { private Paint mLinePaint; private Paint mTextPaint; private Paint mRulerPaint; private float progrees = 10; private int max = 101; private int min = 0; private boolean isCanMove; public ScaleView(Context context) { super(context); init(); } public ScaleView(Context context, AttributeSet attrs) { super(context, attrs); init(); } public ScaleView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init() { mLinePaint = new Paint(); mLinePaint.setColor(Color.CYAN); mLinePaint.setAntiAlias(true);//抗锯齿 mLinePaint.setStyle(Paint.Style.STROKE); mLinePaint.setStrokeWidth(4); mTextPaint = new Paint(); mTextPaint.setColor(Color.CYAN); mTextPaint.setAntiAlias(true); mTextPaint.setStyle(Paint.Style.FILL); mTextPaint.setStrokeWidth(2); mTextPaint.setTextSize(48); // mRulerPaint = new Paint(); mRulerPaint.setAntiAlias(true); mRulerPaint.setStyle(Paint.Style.FILL_AND_STROKE); mRulerPaint.setColor(Color.RED); mRulerPaint.setStrokeWidth(4); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); setMeasuredDimension(setMeasureWidth(widthMeasureSpec), setMeasureHeight(heightMeasureSpec)); } private int setMeasureHeight(int spec) { int mode = MeasureSpec.getMode(spec); int size = MeasureSpec.getSize(spec); int result = Integer.MAX_VALUE; switch (mode) { case MeasureSpec.AT_MOST: size = Math.min(result, size); break; case MeasureSpec.EXACTLY: break; default: size = result; break; } return size; } private int setMeasureWidth(int spec) { int mode = MeasureSpec.getMode(spec); int size = MeasureSpec.getSize(spec); int result = Integer.MAX_VALUE; switch (mode) { case MeasureSpec.AT_MOST: size = Math.min(result, size); break; case MeasureSpec.EXACTLY: break; default: size = result; break; } return size; } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.save(); for (int i = min; i < max; i++) { if (i % 10 == 0) { canvas.drawLine(10, 0, 10, 72, mLinePaint); String text = i / 10 + ""; Rect rect = new Rect(); float txtWidth = mTextPaint.measureText(text); mTextPaint.getTextBounds(text, 0, text.length(), rect); canvas.drawText(text, 10 - txtWidth / 2, 72 + rect.height() + 10, mTextPaint); } else if (i % 5 == 0) { canvas.drawLine(10, 0, 10, 64, mLinePaint); } else { canvas.drawLine(10, 0, 10, 48, mLinePaint); } canvas.translate(18, 0); } canvas.restore(); canvas.drawLine(progrees, 0, progrees, 160, mRulerPaint); canvas.drawCircle(progrees, 170, 10, mRulerPaint); BigDecimal bd = new BigDecimal((progrees - 18) / 180); bd = bd.setScale(1, BigDecimal.ROUND_HALF_UP); mTextPaint.setTextSize(48); canvas.drawText(bd.floatValue() + "cm", 500, 400, mTextPaint); } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: isCanMove = true; break; case MotionEvent.ACTION_MOVE: if (!isCanMove) { return false; } float x = event.getX() - 10; progrees = x; invalidate(); break; } return true; } }布局:
<com.example.com.testview.ScaleView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="4dp"/>现在看一下横向效果:
代码就不上传了,上面就是。