验证的设计主要处于安全考虑,防止机器直接进行某种操做,进而不少人大展思路设计出了不少不一样种类的验证码,如下将是经过Android自定义View实现拼图解锁功能。php
从图中大体能够看出并无什么特别难的点,最多也就是如何将这个验证的图给随机的抠出来,这里用到了Xfermode
即图像混合模式来进行图片的裁剪。html
第1、首先要有一张图片做为一个锁子
,如下为例。java
第一步咱们仅仅拿到了图片,紧接着咱们要在锁子
的一个位置中挖出一块做为锁芯
。为了好看,咱们的锁芯应该与上下都有必定的间隔。android
第2、一张图片做为锁芯
的形状,而后随机取一个X坐标做为锁芯
的中心点,并生成锁芯
canvas
其中绿色点为这个图片的中心点,白色为整个锁芯
的大小,黑色的则是锁芯
的形状,剩下的由于是透明的因此显示出了锁子
的内容。安全
第3、两次绘制中经过设置Paint的Xfermode来取得不一样的内容,进而将锁子
、钥匙
、锁芯
进行分离。app
能够说这个应该算是整个拼图解锁功能中最核心的了,不过在Android的绘图中Paint为咱们提供的Xfermode,以此能够轻松解决这个事情,因为Xfermode提供了不少模式并不能一会儿都掌握而且熟练使用,只须要从经常使用的几种着手就能够了。 如下不会赘述,有一个绘制圆角图片案例可作参考,内容简单相信看完就会明白! blog.lost520.cn/study/show-…ide
若是你感兴趣能够参考这位大神的博客:blog.csdn.net/harvic88092…函数
绘制锁子和钥匙的核心代码:布局
/** * 获取锁子或者钥匙 * @param lock 锁子 * @param keyTemp 钥匙模板 * @param keyLocation 钥匙所处锁子的位置 * @param mode 1:锁子,2:钥匙 * @return */
private Bitmap getKeyOrLock(Bitmap lock, Bitmap keyTemp, Rect keyLocation, int mode) {
//根据mode来具体设置Bitmap的大小
int width = lock.getWidth();
int height = lock.getHeight();
if (mode == 2) {
width = keyTemp.getWidth();
height = keyTemp.getHeight();
}
Bitmap result = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);//绘制结果
Canvas canvas = new Canvas(result);//画板
Paint paint = new Paint();//画笔
//根据mode设置不一样的PorterDuffXfermode达到不一样的遮罩效果
if (mode == 1) {//绘制锁子
canvas.drawBitmap(keyTemp, keyLocation.left, keyLocation.top, paint);//绘制DST
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT));
canvas.drawBitmap(lock, 0, 0, paint);//绘制SRC
} else {//绘制钥匙
canvas.drawBitmap(keyTemp, 0, 0, paint);//绘制DST
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(lock, -keyLocation.left, -keyLocation.top, paint);//绘制SRC
}
return result;
}
复制代码
第4、将锁子
(带锁芯的)和钥匙
分别绘制到特定位置。
锁子
直接平铺便可,锁芯
的绘制能够随机进行取值,钥匙
则绘制在最左侧让用户进行滑动。
第5、检测用户拖动,验证钥匙和锁芯中心点是否重合。
在View中重写onTouchEvent
函数来检测用户的拖拽,当用户点中钥匙并锁子进行拖动时,计算钥匙
和锁芯
的中心点是否合并(可加上特定的偏移量),用户释放后则直接回调处理结果。到此五部基本完成了拼图解锁的功能。
拆分锁子和钥匙:
/** * 获取锁子或者钥匙 * * @param lock 锁子 * @param keyTemp 钥匙模板 * @param keyLocation 钥匙所处锁子的位置 * @param mode 1:锁子,2:钥匙 * @return */
private Bitmap getKeyOrLock(Bitmap lock, Bitmap keyTemp, Rect keyLocation, int mode) {
//根据mode来具体设置Bitmap的大小
int width = lock.getWidth();
int height = lock.getHeight();
if (mode == 2) {
width = keyTemp.getWidth();
height = keyTemp.getHeight();
}
Bitmap result = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);//绘制结果
Canvas canvas = new Canvas(result);//画板
Paint paint = new Paint();//画笔
//根据mode设置不一样的PorterDuffXfermode达到不一样的遮罩效果
if (mode == 1) {//绘制锁子
canvas.drawBitmap(keyTemp, keyLocation.left, keyLocation.top, paint);//绘制DST
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT));
canvas.drawBitmap(lock, 0, 0, paint);//绘制SRC
} else {//绘制钥匙
canvas.drawBitmap(keyTemp, 0, 0, paint);//绘制DST
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(lock, -keyLocation.left, -keyLocation.top, paint);//绘制SRC
}
return result;
}
复制代码
绘制特定大小图片:
/** * 将图片绘制为特定大小 * * @param bitmap 图片 * @param width 宽度 * @param height 高度 * @return */
private Bitmap resizeBitmap(Bitmap bitmap, int width, int height) {
Bitmap result = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(result);
Rect rect = new Rect(0, 0, width, height);
canvas.drawBitmap(bitmap, null, rect, null);
return result;
}
复制代码
布局:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context="com.demo.qylost.puzzleunlockdemo.MainActivity">
<com.demo.qylost.puzzleunlockdemo.PuzzleUnlockView android:id="@+id/puzzleUnlockView" android:layout_width="match_parent" android:layout_height="wrap_content" />
<Button android:id="@+id/btnUpdate" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="?selectableItemBackground" android:text="更换图片" android:textColor="@color/colorAccent" />
<Button android:id="@+id/btnRefresh" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="?selectableItemBackground" android:text="刷新" android:textColor="@color/colorAccent" />
<TextView android:id="@+id/txtStatus" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:text="状态:XXX" android:textColor="@color/colorAccent" />
</LinearLayout>
复制代码
后台:
//拿到相关控件
Button btnUpdate = findViewById(R.id.btnUpdate);
Button btnRefresh = findViewById(R.id.btnRefresh);
//自定义的拼图解锁View
final PuzzleUnlockView puzzleUnlockView = findViewById(R.id.puzzleUnlockView);
//显示状态
final TextView txtStatus = findViewById(R.id.txtStatus);
//更换图片
btnUpdate.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
puzzleUnlockView
.setLockBitmap(BitmapFactory.decodeResource(getResources(), R.mipmap.lock2));
}
});
//刷新
btnRefresh.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
puzzleUnlockView.refreshLock();
}
});
//验证回调
puzzleUnlockView.setOnLockResultListener(new PuzzleUnlockView.OnLockResultListener() {
@Override
public void onResult(boolean result) {
if (result) {
txtStatus.setText("状态:Success");
} else {
txtStatus.setText("状态:Failed");
puzzleUnlockView.refreshLock();//刷新
}
}
});
复制代码