滑动验证实现方式有很多,这里是自定义View的方式,通过按住验证图片移动到目标阴影位置完成验证
public class SlidingVerificationView extends View {
//原图
private Bitmap bitmap;
//画笔
private Paint paint;
//阴影区域
private RectF shadowRect;
//阴影图
private Bitmap drawBitmap;
//验证图长度
private int drawBitmapLength;
//验证图
private Bitmap verifyBitmap;
private RectF moveRect;
private boolean isMove = false;
//触摸点X坐标
private float mX = 0;
private float mY = 0;
//随机数
private Random random;
//随机坐标
private int x;
private int y;
private int paddingLeft;
private int paddingRight;
private int paddingTop;
private int paddingBottom;
//控件最大宽高
private int maxWidth;
private int maxHeight;
public SlidingVerificationView(Context context) {
this(context, null);
}
public SlidingVerificationView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public SlidingVerificationView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
paddingLeft = getPaddingLeft();
paddingRight = getPaddingRight();
paddingTop = getPaddingTop();
paddingBottom = getPaddingBottom();
paint = new Paint();
paint.setAntiAlias(true);
shadowRect = new RectF();
moveRect = new RectF();
random = new Random();
screenWidth();
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
drawBitmap = null;
verifyBitmap = null;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (bitmap != null) {
if (drawBitmap == null) {
//bitmap宽
int width = getWidth() - paddingLeft - paddingRight;
//bitmap高
int height = getHeight() - paddingTop - paddingBottom;
drawBitmap = Bitmap.createScaledBitmap(bitmap, width, height, false);
if (verifyBitmap == null) {
//bitmap最短边长度的四分之一
drawBitmapLength = Math.min(drawBitmap.getWidth(), drawBitmap.getHeight()) / 4;
//在bitmap上取随即x坐标(drawBitmapLength + paddingLeft到width - drawBitmapLength - paddingRight + paddingLeft),y坐标
x = random.nextInt(width - 2 * drawBitmapLength - paddingRight) + drawBitmapLength + paddingLeft;
y = random.nextInt(height - 2 * drawBitmapLength - paddingBottom) + drawBitmapLength + paddingTop;
//验证的图片
verifyBitmap = Bitmap.createBitmap(drawBitmap, x, y, drawBitmapLength, drawBitmapLength);
}
}
//画完整图片
canvas.drawBitmap(drawBitmap, paddingLeft, paddingTop, paint);
//画上阴影
paint.setColor(Color.parseColor("#55000000"));
shadowRect.set(x + paddingLeft, y + paddingTop, x + drawBitmapLength + paddingLeft, y + drawBitmapLength + paddingTop);
canvas.drawRect(shadowRect, paint);
//画验证图片
paint.setColor(Color.parseColor("#ffffffff"));
canvas.drawBitmap(verifyBitmap, mX, y + paddingTop, paint);
moveRect.set(mX, y, mX + drawBitmapLength, y + drawBitmapLength);
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int measureWidth = MeasureSpec.getSize(widthMeasureSpec);
int measureHeight = MeasureSpec.getSize(heightMeasureSpec);
//当模式是MeasureSpec.AT_MOST时,也就是说用户将布局设置成了wrap_content,我们就需要将大小设定为我们计算的数值
int measureWidthMode = MeasureSpec.getMode(widthMeasureSpec);
int measureHeightMode = MeasureSpec.getMode(heightMeasureSpec);
//当模式不是MeasureSpec.EXACTLY时设置固定大小
setMeasuredDimension((measureWidthMode == MeasureSpec.EXACTLY) ? Math.min(measureWidth, maxWidth) : 600
, (measureHeightMode == MeasureSpec.EXACTLY) ? Math.min(measureHeight, maxHeight) : 400);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
if (moveRect.contains(event.getX(), event.getY())) {
isMove = true;
}
} else if (event.getAction() == MotionEvent.ACTION_MOVE) {
if (isMove) {
//获取点击坐标
mX = event.getX();
mY = event.getY();
invalidate();
return false;
}
} else if (event.getAction() == MotionEvent.ACTION_UP) {
performClick();
return false;
}
return true;
}
@Override
public boolean performClick() {
return super.performClick();
}
/**
* 设置滑动验证停止移动时,是否在验证区域
* 参数range:误差,推荐0.02
*/
public Boolean setTouchUp(double range) {
if (isMove) {
isMove = false;
float trueX = x + paddingLeft;
return mX > trueX * (1 - range) && mX < trueX * (1 + range);
}
return null;
}
/**
* seekBar验证
* @param progress
*/
public void setProgress(float progress){
isMove = true;
mX = getWidth()*progress;
invalidate();
}
/**
* 设置原图
*/
public void setImageBitmap(@DrawableRes int id) {
bitmap = getBitmapResources(getResources(), id, false);
if (drawBitmap != null) {
drawBitmap.recycle();
drawBitmap = null;
verifyBitmap.recycle();
verifyBitmap = null;
mX = 0;
}
invalidate();
}
/**
* 设置原图
*/
public void setImageBitmap(Bitmap bitmap) {
this.bitmap = bitmap;
if (drawBitmap != null) {
drawBitmap.recycle();
drawBitmap = null;
verifyBitmap.recycle();
verifyBitmap = null;
mX = 0;
}
invalidate();
}
/**
* 读取资源文件夹下图片
*
* @param res getResources
* @param id 文件id
* @param isRGB 是否使用RGB_565压缩图片
* @return bitmap
*/
private Bitmap getBitmapResources(Resources res, int id, boolean isRGB) {
InputStream is = res.openRawResource(id);
if (isRGB) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.RGB_565;
return BitmapFactory.decodeStream(new BufferedInputStream(is), null, options);
}
return BitmapFactory.decodeStream(new BufferedInputStream(is));
}
/**
* 获取屏幕宽高
*/
private void screenWidth() {
WindowManager wm = (WindowManager) getContext().getApplicationContext().getSystemService(Context.WINDOW_SERVICE);
if (wm != null) {
Display display = wm.getDefaultDisplay();
Point size = new Point();
display.getSize(size);
maxWidth = size.x;
maxHeight = size.y;
}
}
}
使用非常简单,只需要设置原图即可,会自动截取某一个区域的图片,并在该位置画上阴影,当验证图片移动到该位置完成验证,验证结果可以监听触摸事件获取,如下所示
//设置图片
slidingVerificationView.setImageBitmap(R.drawable.test)
//验证
slidingVerificationView.setOnTouchListener(object :View.OnTouchListener{
override fun onTouch(v: View, event: MotionEvent): Boolean {
if (event.getAction() == MotionEvent.ACTION_UP){
val isSlidingVerification = slidingVerificationView.setTouchUp(0.02)
if (isSlidingVerification != null){
if (isSlidingVerification){
showToast("验证成功")
}else{
showToast("验证失败")
}
}
slidingVerificationView.performClick();
return true
}
return false
}
})
如果想刷新原图,再次调用setImageBitmap即可
//刷新图片
button2.setOnClickListener {
slidingVerificationView.setImageBitmap(R.drawable.test)
}
如果想通过进度条的方式拖动验证图片进行验证,使用如下方法即可
seekBar.max = 100
seekBar.setOnSeekBarChangeListener(object :SeekBar.OnSeekBarChangeListener{
override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
slidingVerificationView.setProgress(progress/100f)
}
override fun onStartTrackingTouch(seekBar: SeekBar?) {
}
override fun onStopTrackingTouch(seekBar: SeekBar?) {
val isSlidingVerification = slidingVerificationView.setTouchUp(0.02)
if (isSlidingVerification != null){
if (isSlidingVerification){
showToast("验证成功")
}else{
showToast("验证失败")
}
}
}
})