驗證的設計主要處於安全考慮,防止機器直接進行某種操做,進而不少人大展思路設計出了不少不一樣種類的驗證碼,如下將是經過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();//刷新
}
}
});
複製代碼