Android實現拼圖解鎖

QyLost發表於2019-08-31

前言

驗證的設計主要處於安全考慮,防止機器直接進行某種操作,進而很多人大展思路設計出了很多不同種類的驗證碼,以下將是通過Android自定義View實現拼圖解鎖功能。

效果圖

t.gif

從圖中大致可以看出並沒有什麼特別難的點,最多也就是如何將這個驗證的圖給隨機的摳出來,這裡用到了Xfermode即影像混合模式來進行圖片的裁剪。

核心思路

第一、首先要有一張圖片作為一個鎖子,以下為例。

第一步我們僅僅拿到了圖片,緊接著我們要在鎖子的一個位置中挖出一塊作為鎖芯。為了好看,我們的鎖芯應該與上下都有一定的間隔。

image.png

第二、一張圖片作為鎖芯的形狀,然後隨機取一個X座標作為鎖芯的中心點,並生成鎖芯

其中綠色點為這個圖片的中心點,白色為整個鎖芯的大小,黑色的則是鎖芯的形狀,剩下的因為是透明的所以顯示出了鎖子的內容。

image.png

第三、兩次繪製中通過設定Paint的Xfermode來取得不同的內容,進而將鎖子鑰匙鎖芯進行分離。

可以說這個應該算是整個拼圖解鎖功能中最核心的了,不過在Android的繪圖中Paint為我們提供的Xfermode,以此可以輕鬆解決這個事情,由於Xfermode提供了很多模式並不能一下子都掌握並且熟練使用,只需要從常用的幾種著手就可以了。 以下不會贅述,有一個繪製圓角圖片案例可做參考,內容簡單相信看完就會明白! blog.lost520.cn/study/show-…

如果你感興趣可以參考這位大神的部落格: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;
}
複製程式碼

image.png

第四、將鎖子(帶鎖芯的)和鑰匙分別繪製到特定位置。

鎖子直接平鋪即可,鎖芯的繪製可以隨機進行取值,鑰匙則繪製在最左側讓使用者進行滑動。

image.png

第五、檢測使用者拖動,驗證鑰匙和鎖芯中心點是否重合。

在View中重寫onTouchEvent函式來檢測使用者的拖拽,當使用者點中鑰匙並鎖子進行拖動時,計算鑰匙鎖芯的中心點是否合併(可加上特定的偏移量),使用者釋放後則直接回撥處理結果。到此五部基本完成了拼圖解鎖的功能。

image.png

核心程式碼

拆分鎖子和鑰匙:

/**
 * 獲取鎖子或者鑰匙
 *
 * @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();//重新整理
        }
    }
});
複製程式碼

原始碼+素材

原始碼+素材.zip

相關文章