Android实现拼图解锁

DarwinRoy 发布于14天前
0 条问题

前言

验证的设计主要处于安全考虑,防止机器直接进行某种操作,进而很多人大展思路设计出了很多不同种类的验证码,以下将是通过Android自定义View实现拼图解锁功能。

效果图

Android实现拼图解锁

从图中大致可以看出并没有什么特别难的点,最多也就是如何将这个验证的图给随机的抠出来,这里用到了 Xfermode 即图像混合模式来进行图片的裁剪。

核心思路

第一、首先要有一张图片作为一个 锁子 ,以下为例。

第一步我们仅仅拿到了图片,紧接着我们要在 锁子 的一个位置中挖出一块作为 锁芯 。为了好看,我们的锁芯应该与上下都有一定的间隔。

Android实现拼图解锁

第二、一张图片作为 锁芯 的形状,然后随机取一个X坐标作为 锁芯 的中心点,并生成 锁芯

其中绿色点为这个图片的中心点,白色为整个 锁芯 的大小,黑色的则是 锁芯 的形状,剩下的因为是透明的所以显示出了 锁子 的内容。

Android实现拼图解锁

第三、两次绘制中通过设置Paint的Xfermode来取得不同的内容,进而将 锁子 、 钥匙 、 锁芯 进行分离。

可以说这个应该算是整个拼图解锁功能中最核心的了,不过在Android的绘图中Paint为我们提供的Xfermode,以此可以轻松解决这个事情,由于Xfermode提供了很多模式并不能一下子都掌握并且熟练使用,只需要从常用的几种着手就可以了。 以下不会赘述,有一个绘制圆角图片案例可做参考,内容简单相信看完就会明白!

https://blog.lost520.cn/study/show-37.html

如果你感兴趣可以参考这位大神的博客: https://blog.csdn.net/harvic880925/article/details/51264653

绘制锁子和钥匙的核心代码:

/**
 * 获取锁子或者钥匙
 * @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;
}

Android实现拼图解锁

第四、将 锁子 (带锁芯的)和 钥匙 分别绘制到特定位置。

锁子 直接平铺即可, 锁芯 的绘制可以随机进行取值, 钥匙 则绘制在最左侧让用户进行滑动。

Android实现拼图解锁

第五、检测用户拖动,验证钥匙和锁芯中心点是否重合。

在View中重写 onTouchEvent 函数来检测用户的拖拽,当用户点中钥匙并锁子进行拖动时,计算 钥匙 和 锁芯 的中心点是否合并(可加上特定的偏移量),用户释放后则直接回调处理结果。到此五部基本完成了拼图解锁的功能。

Android实现拼图解锁

核心代码

拆分锁子和钥匙:

/**
 * 获取锁子或者钥匙
 *
 * @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

查看原文: Android实现拼图解锁

  • biggorilla
  • beautifulgoose
  • orangekoala
需要 登录 后回复方可回复, 如果你还没有账号你可以 注册 一个帐号。