引言
***Android的道路上自己摸索前进已有一段时间了,平时看着一些大神写的博客,收益很多,今日起,在学习的同时也通过博客分享一些自己的所见所得。 本片为小弟的处女作,有不对的地方希望猿友们指出,共同交流。。。***
该篇内容主要实现滑动验证开关的功能,表述能力有限,直接上效果图:
实现过程
由于没有阅读过源码,暂不写实现原理,直接从实现步骤开始。源码在最下方。
初始化画笔等基础属性
// 开关控件底部窗体灰色背景的画笔 private Paint mBottomPaint; // 开关控件底部窗体绿色背景的画笔 private Paint mBottomPaint_Left; // 顶部白色滑块的画笔 private Paint mTopPaint; // 提示字体及验证完成的画笔 private Paint mTextPaint; private String textHint = "拖动滑块验证"; private String textCompleted = "验证成功"; private float widthSlide = 0; // 滑块的宽度,设置为view宽的 1/5 private float viewWidth;// 窗体宽度 private float viewHeight;//窗体高度 private float mTextHintWidth;// 提示字体的宽度 private float mTextCompletedWidth;// 验证完成的宽度 private float currentX = 0; // 当前手指所在位置 private boolean isSlidding = false; // 是否正在滑动 private boolean isCompelted = false; // 是否滑动完成 // 自定义滑动完成的回掉接口,处理相应的逻辑 private ScrollCompletedListener scrollCompletedListenner;
/** * 初始化一些基本的属性 */ private void init() { mBottomPaint = new Paint(); mBottomPaint.setAntiAlias(true); mBottomPaint.setColor(Color.GRAY); mTopPaint = new Paint(); mTopPaint.setAntiAlias(true); mTopPaint.setColor(Color.WHITE); mBottomPaint_Left = new Paint(); mBottomPaint_Left.setAntiAlias(true); mBottomPaint_Left.setColor(Color.GREEN); mTextPaint = new TextPaint(); mTextPaint.setColor(Color.BLACK); mTextPaint.setTextSize(45); mTextPaint.setAntiAlias(true); }
重写onMeasure方法 :
测量模式一共分为:EXACTLY, AT_MOST,UNSPECIFIED;
就只能使用默认的模式,对应在界面引用该自定义View时,
宽高只能设置为match_parent或者精确值,
为了能够使用wrap_content,必须重写该方法。
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(measureSize(widthMeasureSpec), measureSize(heightMeasureSpec)); }
/** * 重写该方法 为了测量自定义view的大小,调用时可以使用wrap_content * @param measureSpec * @return */ private int measureSize(int measureSpec) { int size = 0; int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); if (specMode == MeasureSpec.EXACTLY) { size = specSize; } else { size = 200; if (specMode == MeasureSpec.AT_MOST) { size = Math.min(size, specSize); } } return size; }
重写onSizeChanged方法
这个方法在onMeasure方法之后运行,重写该方法的目的是为了测量这个View窗体的宽和高,以及在这个方法中获取要显示文本的宽度。
/** * 为了获取窗体的大小,必须在测量之后才能获取,否则为0 * @param w * @param h * @param oldw * @param oldh */ @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); viewWidth = getWidth();//该自定义view窗体的宽度 viewHeight = getHeight();//窗体的高度 widthSlide = viewWidth/5;// 上方白色滑块的宽度 //提示文字的宽度 mTextHintWidth = mTextPaint.measureText(textHint); mTextCompletedWidth = mTextPaint.measureText(textCompleted);// 验证成功文字的宽度 }
重写onDraw方法
画出要显示的图形
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); // 画出底部的灰色矩形背景 canvas.drawRect(0, 0, viewWidth, viewHeight, mBottomPaint); // 当滑块右侧位置滑动到提示字体左侧时去除掉(不再画出)提示字体 if ((currentX + widthSlide) <= (viewWidth / 2 - mTextHintWidth / 2)) { canvas.drawText(textHint, (viewWidth/2 - mTextHintWidth/2), viewHeight/2 + 15, mTextPaint); } // 根据滑块的坐标位置,画出白色滑块,滑块的坐标根据手触摸到屏幕上坐标位置计算 canvas.drawRect(currentX, 0, (currentX + widthSlide), viewHeight, mTopPaint); // 滑块滑向右侧的过程中,滑块左侧显示出绿色 canvas.drawRect(0, 0, currentX, viewHeight, mBottomPaint_Left); /*当滑块滑动到右侧时,画出滑块上的绿色圆环及验证成功字样 widthSlide:白色滑块的宽度 */ if (isSlidding) { canvas.drawCircle((currentX + widthSlide/2), viewHeight/2, widthSlide/4, mBottomPaint_Left); mTextPaint.setColor(Color.WHITE); mTextPaint.setTextSize(50); canvas.drawText(textCompleted, (viewWidth/2 - mTextCompletedWidth/2), viewHeight/2 + 15, mTextPaint); isCompelted = true; // 设置滑动完成监听 if (scrollCompletedListenner != null) { scrollCompletedListenner.scrollCompleted(); } } }
重写onTouchEvent
当手指滑动时,动态的获取手指的位置,手指滑动到哪里,白色滑块就要在哪个位置显示,也就是说,手指滑动的位置就是白色滑块的左侧顶点坐标。
/** * 滑动时动态画方块 * @param event * @return */ @Override public boolean onTouchEvent(MotionEvent event) { // 加判断是为了滑动完成时 防止滑块回滑 if (!isCompelted) { switch (event.getAction()) { case MotionEvent.ACTION_MOVE: currentX = event.getX() < 0 ? 0 : event.getX(); if (currentX >= (viewWidth - widthSlide)) { currentX = viewWidth - widthSlide; } invalidate(); break; case MotionEvent.ACTION_UP: if (currentX < (viewWidth - widthSlide - 10)) { currentX = 0; isSlidding = false; } else { currentX = viewWidth - widthSlide; isSlidding = true; } invalidate(); break; } } return true; }
声明接口,在滑动完成时进行回掉
/** * 声明接口是为了验证完成时进行回掉 */ public interface ScrollCompletedListener { // 滑动完成 void scrollCompleted(); } /** * 设置监听 * @param scrollCompletedListener */ public void setOnScrollCompletedListener(ScrollCompletedListener scrollCompletedListener) { this.scrollCompletedListenner = scrollCompletedListener; }
完整代码如下
/** * Created on 2017/3/29 and 14:25 * 作者:王金 * 邮箱:1002811532@qq.com */ public class CustomSwitchView extends View { // 开关控件底部窗体灰色背景的画笔 private Paint mBottomPaint; // 开关控件底部窗体绿色背景的画笔 private Paint mBottomPaint_Left; // 顶部白色滑块的画笔 private Paint mTopPaint; // 提示字体及验证完成的画笔 private Paint mTextPaint; private String textHint = "拖动滑块验证"; private String textCompleted = "验证成功"; private float widthSlide = 0; // 滑块的宽度,设置为view宽的 1/5 private float viewWidth;// 窗体宽度 private float viewHeight;//窗体高度 private float mTextHintWidth;// 提示字体的宽度 private float mTextCompletedWidth;// 验证完成的宽度 private float currentX = 0; // 当前手指所在位置 private boolean isSlidding = false; // 是否正在滑动 private boolean isCompelted = false; // 是否滑动完成 // 自定义滑动完成的回掉接口,处理相应的逻辑 private ScrollCompletedListener scrollCompletedListenner; public CustomSwitchView(Context context) { this(context, null); } public CustomSwitchView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public CustomSwitchView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } /** * 初始化一些基本的属性 */ private void init() { mBottomPaint = new Paint(); mBottomPaint.setAntiAlias(true); mBottomPaint.setColor(Color.GRAY); mTopPaint = new Paint(); mTopPaint.setAntiAlias(true); mTopPaint.setColor(Color.WHITE); mBottomPaint_Left = new Paint(); mBottomPaint_Left.setAntiAlias(true); mBottomPaint_Left.setColor(Color.GREEN); mTextPaint = new TextPaint(); mTextPaint.setColor(Color.BLACK); mTextPaint.setTextSize(45); mTextPaint.setAntiAlias(true); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(measureSize(widthMeasureSpec), measureSize(heightMeasureSpec)); } /** * 重写该方法 为了测量自定义view的大小,调用时可以使用wrap_content * @param measureSpec * @return */ private int measureSize(int measureSpec) { int size = 0; int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); if (specMode == MeasureSpec.EXACTLY) { size = specSize; } else { size = 200; if (specMode == MeasureSpec.AT_MOST) { size = Math.min(size, specSize); } } return size; } /** * 为了获取窗体的大小,必须在测量之后才能获取,否则为0 * @param w * @param h * @param oldw * @param oldh */ @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); viewWidth = getWidth(); viewHeight = getHeight(); widthSlide = viewWidth/5; mTextHintWidth = mTextPaint.measureText(textHint); mTextCompletedWidth = mTextPaint.measureText(textCompleted); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); // 画出底部的灰色矩形背景 canvas.drawRect(0, 0, viewWidth, viewHeight, mBottomPaint); // 当滑块右侧位置滑动到提示字体左侧时去除掉(不再画出)提示字体 if ((currentX + widthSlide) <= (viewWidth / 2 - mTextHintWidth / 2)) { canvas.drawText(textHint, (viewWidth/2 - mTextHintWidth/2), viewHeight/2 + 15, mTextPaint); } // 根据滑块的坐标位置,画出白色滑块,滑块的坐标根据手触摸到屏幕上坐标位置计算 canvas.drawRect(currentX, 0, (currentX + widthSlide), viewHeight, mTopPaint); // 滑块滑向右侧的过程中,滑块左侧显示出绿色 canvas.drawRect(0, 0, currentX, viewHeight, mBottomPaint_Left); // 当滑块滑动到右侧时,画出滑块上的绿色圆环及验证成功字样 if (isSlidding) { canvas.drawCircle((currentX + widthSlide/2), viewHeight/2, widthSlide/4, mBottomPaint_Left); mTextPaint.setColor(Color.WHITE); mTextPaint.setTextSize(50); canvas.drawText(textCompleted, (viewWidth/2 - mTextCompletedWidth/2), viewHeight/2 + 15, mTextPaint); isCompelted = true; // 设置滑动完成监听 if (scrollCompletedListenner != null) { scrollCompletedListenner.scrollCompleted(); } } } /** * 滑动时动态画方块 * @param event * @return */ @Override public boolean onTouchEvent(MotionEvent event) { // 加判断是为了滑动完成时 防止滑块回滑 if (!isCompelted) { switch (event.getAction()) { case MotionEvent.ACTION_MOVE: currentX = event.getX() < 0 ? 0 : event.getX(); if (currentX >= (viewWidth - widthSlide)) { currentX = viewWidth - widthSlide; } invalidate(); break; case MotionEvent.ACTION_UP: if (currentX < (viewWidth - widthSlide - 10)) { currentX = 0; isSlidding = false; } else { currentX = viewWidth - widthSlide; isSlidding = true; } invalidate(); break; } } return true; } /** * 声明接口是为了验证完成时进行回掉 */ public interface ScrollCompletedListener { // 滑动完成 void scrollCompleted(); } /** * 设置监听 * @param scrollCompletedListener */ public void setOnScrollCompletedListener(ScrollCompletedListener scrollCompletedListener) { this.scrollCompletedListenner = scrollCompletedListener; } }
作者:piaomiao8179