最近项目上要把原先发送验证码的地方的图片验证改成滑块验证,于是重新自定义了一个View,先来看一下实现的效果图:
中间那个可以拖动的滑块就是一个自定义View,下面贴上它的实现代码:
package 马赛克.view;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import 马赛克;
import 马赛克.CommonUtil;
/**
* Created by sjb on 2016/6/30.
* 拖动验证的小滑块
*/
public class BlockVerifyView extends View {
/**
* 滑块起点的X坐标
*/
private float mBlockStartX;
/**
* 滑块终点的X坐标
*/
private float mBlockEndX;
/**
* 上一个X坐标
*/
private float mLastX;
/**
* 画笔
*/
private Paint mPaint;
private Paint mBgPaint;
private boolean isSuccess;
private OnVerifyListener listener;
public interface OnVerifyListener {
void success();
void fail();
}
public void setOnVerifyListener(OnVerifyListener onVerifyListener) {
this.listener = onVerifyListener;
}
public BlockVerifyView(Context context) {
super(context);
init();
}
public BlockVerifyView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public BlockVerifyView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
void init() {
mPaint = new Paint();
mPaint.setAntiAlias(true);
mBgPaint = new Paint();
mBgPaint.setAntiAlias(true);
mBgPaint.setColor(getResources().getColor(R.color.sa_light_g));
mBgPaint.setStyle(Paint.Style.STROKE);
}
private int getMeasure(int measure, int defaultSize) {
int result = 300;
int mode = MeasureSpec.getMode(measure);
int size = MeasureSpec.getSize(measure);
if (mode == MeasureSpec.EXACTLY) {
result = size;
} else if (mode == MeasureSpec.AT_MOST) {
result = Math.min(CommonUtil.dip2px(getContext(), defaultSize), size);
}
return result;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getMeasure(widthMeasureSpec, 300), getMeasure(heightMeasureSpec, 40));
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
mBlockStartX = getWidth() * 0.05f;
mBlockEndX = mBlockStartX + getWidth() * 0.15f;
}
@Override
protected void onDraw(Canvas canvas) {
drawText(canvas);
drawLeftArea(canvas);
drawWhiteBlock(canvas);
drawThreeRec(canvas);
if (isSuccess) {
drawSuccess(canvas);
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if (event.getX() > mBlockStartX && event.getX() < mBlockEndX) {
mLastX = event.getX();
return true;
}
break;
case MotionEvent.ACTION_MOVE:
if (event.getY() > 0 && event.getY() < getHeight()) {
if ((mBlockStartX + event.getX() - mLastX) > getWidth() * 0.05f
&& (mBlockStartX + event.getX() - mLastX + getWidth() * 0.15f) < getWidth()) {
mBlockStartX += event.getX() - mLastX;
mBlockEndX = mBlockStartX + getWidth() * 0.15f;
mLastX = event.getX();
if (getWidth() - mBlockEndX < 10) {
isSuccess = true;
} else {
isSuccess = false;
}
invalidate();
return true;
}
}
break;
case MotionEvent.ACTION_UP:
if (listener != null) {
if (getWidth() - mBlockEndX < 10) {
listener.success();
} else {
listener.fail();
}
return true;
}
break;
default:
break;
}
return super.onTouchEvent(event);
}
/**
* 绘制文字
*
* @param canvas
*/
private void drawText(Canvas canvas) {
mPaint.setColor(getResources().getColor(R.color.sa_light_g));
mPaint.setTextAlign(Paint.Align.CENTER);
mPaint.setTextSize(CommonUtil.dip2px(getContext(), 12));
Paint.FontMetricsInt fontMetrics = mPaint.getFontMetricsInt();
int baseline = (getHeight() + 0 - fontMetrics.bottom - fontMetrics.top) / 2;
canvas.drawText("按住滑块拖至最右边", getWidth() * 0.5f, baseline, mPaint);
}
/**
* 绘制左侧已滑过的区域
*/
private void drawLeftArea(Canvas canvas) {
mPaint.setColor(getResources().getColor(R.color.block_bg));
mPaint.setStyle(Paint.Style.FILL);
canvas.drawRect(0, 0, mBlockStartX, getHeight(), mPaint);
}
/**
* 绘制白色小滑块
*/
private void drawWhiteBlock(Canvas canvas) {
mPaint.setColor(getResources().getColor(R.color.white));
mPaint.setStyle(Paint.Style.FILL);
canvas.drawRect(mBlockStartX, 0, mBlockEndX, getHeight(), mPaint);
canvas.drawRect(mBlockStartX - 2, 0, mBlockEndX + 2, getHeight(), mBgPaint);
}
/**
* 绘制滑块上的三条矩形区域
*/
private void drawThreeRec(Canvas canvas) {
mPaint.setColor(getResources().getColor(R.color.block_bg));
mPaint.setStyle(Paint.Style.FILL);
float totalBlockX = mBlockEndX - mBlockStartX;
canvas.drawRect(mBlockStartX + totalBlockX * 0.25f, 10, mBlockStartX + totalBlockX * 0.30f, getHeight() - 10, mPaint);
canvas.drawRect(mBlockStartX + totalBlockX * 0.475f, 10, mBlockStartX + totalBlockX * 0.525f, getHeight() - 10, mPaint);
canvas.drawRect(mBlockStartX + totalBlockX * 0.7f, 10, mBlockStartX + totalBlockX * 0.75f, getHeight() - 10, mPaint);
}
/**
* 验证通过后绘制
* @param canvas
*/
private void drawSuccess(Canvas canvas) {
mPaint.setColor(getResources().getColor(R.color.white));
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(3);
Path path = new Path();
path.moveTo(getWidth() * 0.5f - 20, getHeight() * 0.5f - 10);
path.lineTo(getWidth() * 0.5f, getHeight() * 0.5f + 10);
path.lineTo(getWidth() * 0.5f + 20, getHeight() * 0.5f - 30);
canvas.drawPath(path, mPaint);
}
}
代码不难,控制好坐标就可以了。
其中定义了一个接口OnVerifyListener ,供使用者进行验证通过和失败时的处理,每次手指离开时进行回调。
但是这个View用在这个有键盘弹出的界面还是出现了一点小插曲,下面的这段代码:
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
mBlockStartX = getWidth() * 0.05f;
mBlockEndX = mBlockStartX + getWidth() * 0.15f;
}
刚开始我是写在这里的:
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
mBlockStartX = getWidth() * 0.05f;
mBlockEndX = mBlockStartX + getWidth() * 0.15f;
}
用在这里发现每次键盘弹出或隐藏时,滑块都会回到初始位置,不能停留在上次的绘图状态,这下就尴尬了不是。
于是在这个View的各个方法中打印日志,发现每次键盘弹出或隐藏时,都回调了onLayout
方法,难怪位置又被初始化了……
好的,那就改变策略,在onSizeChanged
中初始化试试,结果只能是完美!
于是一个滑块验证就这样ko了~
欢迎吐槽、拍砖、卖萌……