Android仿斗鱼滑动登录验证

最近事情不是很多,在家无聊的时候看看直播,总能看到一些新奇的验证登录方式,正好自己最近也要去熟悉一下新的开发工具Android Studio,所以打算自己实现一下.

先看一下效果图:

这里写图片描述


一、确认需求

首先我们确认一下需求,我们分步来看 
1.)需要定义一个阴影部分去遮盖图片,确定一个目标位置. 
2.)需要一个带有边框的滑块内容,初始位置在最左边. 
3.)阴影部分和滑块可以随机旋转,并保持一致. 
4.)需要一个拖拽条使滑块随着拖拽条条拖拽而移动 
5.)判断是否验证成功


二、分析问题

第一点,很简单我们只需要一直带有透明度的阴影图片,随机的去覆盖在图像上就可以实现. 
第二点,需要用到画笔的setXfermode方法来设置图片叠加的显示模式.从而显示出一个带有边框的滑块. 
第三点,需要改变图片的matrix来实现对图像的旋转. 
第四点,安卓系统自带的SeekBar可以实现拖拽条的功能. 
第五点,只需对外提供一个回调接口,来判断是否验证成功即可.


三、代码实现

首先创建一个attr文件来定义一些自定义属性,方便我们使用

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!--滑块的高度-->
    <attr name="unitHeight" format="dimension" />
    <!--滑块的宽度-->
    <attr name="unitWidth" format="dimension" />
    <!--滑块占图片高度的比例-->
    <attr name="unitHeightScale" format="integer" />
    <!--滑块占图片宽度的比例-->
    <attr name="unitWidthScale" format="integer" />
    <!--滑块边框的图片资源-->
    <attr name="unitShadeSrc" format="reference" />
    <!--阴影部分的图片资源-->
    <attr name="unitShowSrc" format="reference" />
    <!--是否需要旋转-->
    <attr name="needRotate" format="boolean" />
    <!--验证时的误差值-->
    <attr name="deviate" format="integer" />
    <declare-styleable name="DouYuView">
        <attr name="unitHeight" />
        <attr name="unitWidth" />
        <attr name="unitHeightScale" />
        <attr name="unitWidthScale" />
        <attr name="unitShadeSrc" />
        <attr name="unitShowSrc" />
        <attr name="needRotate" />
        <attr name="deviate" />
    </declare-styleable>
</resources>
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

然后我们创建一个DouYuView继承自ImageView即可,来定义一下属性

    /**
     * 定义画笔
     */
    private Paint mPaint;
    /**
     * 验证的图像
     */
    private Bitmap mBitmap;
    /**
     * 验证滑块的高
     */
    private int mUintHeight;
    /**
     * 验证滑块的宽
     */
    private int mUintWidth;
    /**
     * 验证滑块宽占用整体图片大小的比例,默认1/5
     */
    private int mUnitWidthScale;
    /**
     * 验证滑块高度占用整体图片大小的比例,默认1/4
     */
    private int mUnitHeightScale;
    /**
     * 随机生成滑块的X坐标
     */
    private int mUnitRandomX;
    /**
     * 随机生成滑块的Y坐标
     */
    private int mUnitRandomY;
    /***
     * 滑块移动的距离
     */
    private float mUnitMoveDistance = 0;
    /***
     * 滑块图像
     */
    private Bitmap mUnitBp;
    /**
     * 验证位置图像
     */
    private Bitmap mShowBp;
    /**
     * 背景阴影图像
     */
    private Bitmap mShadeBp;
    /**
     * 是否需要旋转
     **/
    private boolean needRotate;
    /**
     * 旋转的角度
     */
    private int rotate;
    /**
     * 判断是否完成的偏差量,默认为10
     */
    public int DEFAULT_DEVIATE;
    /**
     * 判断是否重新绘制图像
     */
    private boolean isReSet = true;

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65

准备好这些之后,开始实现具体功能,这次我们打算用星星的图片,来设置阴影和滑块,首先我们准备两张图片,如下图(注意,带边框的图片中间部分,使用白色填充的):

阴影星星 
边框星星

为了使图片不会出现变形等异常,我们需要对图片进行获取及缩放的一些操作,代码如下:

    /**
     * 获取实际显示的图片
     *
     * @return
     */
    public Bitmap getBaseBitmap() {
        Bitmap b = drawableToBitamp(getDrawable());
        float scaleX = 1.0f;
        float scaleY = 1.0f;
        // 如果图片的宽或者高与view的宽高不匹配,计算出需要缩放的比例;缩放后的图片的宽高,一定要大于我们view的宽高;所以我们这里取大值;
        scaleX = getWidth() * 1.0f / b.getWidth();
        scaleY = getHeight() * 1.0f / b.getHeight();
        Matrix matrix = new Matrix();
        matrix.setScale(scaleX, scaleY);
        Bitmap bd = Bitmap.createBitmap(b, 0, 0, b.getWidth(), b.getHeight(),
                matrix, true);
        return bd;
    }

    /**
     * drawable转bitmap
     *
     * @param drawable
     * @return
     */
    private Bitmap drawableToBitamp(Drawable drawable) {
        if (null == drawable) {
            return null;
        }
        if (drawable instanceof BitmapDrawable) {
            BitmapDrawable bd = (BitmapDrawable) drawable;
            return bd.getBitmap();
        }
        int w = drawable.getIntrinsicWidth();
        int h = drawable.getIntrinsicHeight();
        Bitmap bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        drawable.setBounds(0, 0, w, h);
        drawable.draw(canvas);
        return bitmap;
    }

    /**
     * 缩放图片
     *
     * @param bp
     * @param x
     * @param y
     * @return
     */
    public static Bitmap handleBitmap(Bitmap bp, float x, float y) {
        int w = bp.getWidth();
        int h = bp.getHeight();
        float sx = (float) x / w;
        float sy = (float) y / h;
        Matrix matrix = new Matrix();
        matrix.postScale(sx, sy);
        Bitmap resizeBmp = Bitmap.createBitmap(bp, 0, 0, w,
                h, matrix, true);
        return resizeBmp;
    }
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61

然后再去生成阴影图像和滑块

    /**
     * 创建遮挡的图片(阴影部分)
     *
     * @return
     */
    private Bitmap drawTargetBitmap() {
        // 绘制图片
        Bitmap showB;
        if (null != mShowBp) {
            showB = handleBitmap(mShowBp, mUintWidth, mUintHeight);
        } else {
            showB = handleBitmap(BitmapFactory.decodeResource(getResources(), R.mipmap.puzzle_show), mUintWidth, mUintHeight);
        }
        // 如果需要旋转图片,进行旋转,旋转后为了保持和滑块大小一致,需要重新缩放比例
        if (needRotate) {
            showB = handleBitmap(rotateBitmap(rotate, showB), mUintWidth, mUintHeight);
        }
        return showB;
    }

    /**
     * 创建结合的图片(滑块)
     *
     * @param bp
     */
    private Bitmap drawResultBitmap(Bitmap bp) {
        // 绘制图片
        Bitmap shadeB;
        if (null != mShadeBp) {
            shadeB = handleBitmap(mShadeBp, mUintWidth, mUintHeight);
        } else {
            shadeB = handleBitmap(BitmapFactory.decodeResource(getResources(), R.mipmap.puzzle_shade), mUintWidth, mUintHeight);
        }
        // 如果需要旋转图片,进行旋转,旋转后为了和画布大小保持一致,避免出现图像显示不全,需要重新缩放比例
        if (needRotate) {
            shadeB = handleBitmap(rotateBitmap(rotate, shadeB), mUintWidth, mUintHeight);
        }
        Bitmap resultBmp = Bitmap.createBitmap(mUintWidth, mUintHeight,
                Bitmap.Config.ARGB_8888);
        Paint paint = new Paint();
        paint.setAntiAlias(true);
        Canvas canvas = new Canvas(resultBmp);
        canvas.drawBitmap(shadeB, new Rect(0, 0, mUintWidth, mUintHeight),
                new Rect(0, 0, mUintWidth, mUintHeight), paint);
        // 选择交集去上层图片
        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY));
        canvas.drawBitmap(bp, new Rect(0, 0, mUintWidth, mUintHeight),
                new Rect(0, 0, mUintWidth, mUintHeight), paint);
        return resultBmp;
    }

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51

准备好这些之后,还需要随机生成一个显示位置和对图像进行旋转的方法就可以在画布上去画出这些内容,代码如下:

    /**
     * 随机生成生成滑块的XY坐标
     */
    private void initUnitXY() {
        mUnitRandomX = (int) (Math.random() * (mBitmap.getWidth() - mUintWidth));
        mUnitRandomY = (int) (Math.random() * (mBitmap.getHeight() - mUintHeight));
        // 防止生成的位置距离太近
        if (mUnitRandomX <= mBitmap.getWidth() / 2) {
            mUnitRandomX = mUnitRandomX + mBitmap.getWidth() / 4;
        }
        // 防止生成的X坐标截图时导致异常
        if (mUnitRandomX + mUintWidth > getWidth()) {
            initUnitXY();
            return;
        }
    }

    /**
     * 旋转图片
     *
     * @param degree
     * @param bitmap
     * @return
     */
    public Bitmap rotateBitmap(int degree, Bitmap bitmap) {
        Matrix matrix = new Matrix();
        matrix.postRotate(degree);
        Bitmap bm = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(),
                bitmap.getHeight(), matrix, true);
        return bm;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (isReSet) {
            mBitmap = getBaseBitmap();
            if (0 == mUintWidth) {
                mUintWidth = mBitmap.getWidth() / mUnitWidthScale;
            }
            if (0 == mUintHeight) {
                mUintHeight = mBitmap.getHeight() / mUnitHeightScale;
            }
            initUnitXY();
            mUnitBp = Bitmap.createBitmap(mBitmap, mUnitRandomX, mUnitRandomY, mUintWidth, mUintHeight);
        }
        isReSet = false;
        canvas.drawBitmap(drawTargetBitmap(), mUnitRandomX, mUnitRandomY, mPaint);
        canvas.drawBitmap(drawResultBitmap(mUnitBp), mUnitMoveDistance, mUnitRandomY, mPaint);
    }

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51

我们还需要对控件进行重置、控制滑块移动、提供回调方法判断验证是否成功等方法.

    /**
     * 重置
     */
    public void reSet() {
        isReSet = true;
        mUnitMoveDistance = 0;
        if (needRotate) {
            rotate = (int) (Math.random() * 3) * 90;
        } else {
            rotate = 0;
        }
        invalidate();
    }

    /**
     * 获取每次滑动的平均偏移值
     *
     * @return
     */
    public float getAverageDistance(int max) {
        return (float) (mBitmap.getWidth() - mUintWidth) / max;
    }


    /**
     * 滑块移动距离
     *
     * @param distance
     */
    public void setUnitMoveDistance(float distance) {
        mUnitMoveDistance = distance;
        // 防止滑块滑出图片
        if (mUnitMoveDistance > mBitmap.getWidth() - mUintWidth) {
            mUnitMoveDistance = mBitmap.getWidth() - mUintWidth;
        }
        invalidate();

    }
        /**
     * 拼图成功的回调
     **/
    interface onPuzzleListener {
        public void onSuccess();

        public void onFail();
    }

    /**
     * 回调
     */
    private onPuzzleListener mlistener;

    /**
     * 设置回调
     *
     * @param listener
     */
    public void setPuzzleListener(onPuzzleListener listener) {
        this.mlistener = listener;
    }
        /**
     * 验证是否拼接成功
     */
    public void testPuzzle() {
        if (Math.abs(mUnitMoveDistance - mUnitRandomX) <= DEFAULT_DEVIATE) {
            if (null != mlistener) {
                mlistener.onSuccess();
            }
        } else {
            if (null != mlistener) {
                mlistener.onFail();
            }
        }
    }
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74

基本上我们需要的功能就实现了.


四、完整的代码及使用

DouYuView:

package com.example.junweiliu.douyutest;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Log;
import android.widget.ImageView;

/**
 * Created by junweiliu on 16/4/26.
 */
public class DouYuView extends ImageView {
    /**
     * 定义画笔
     */
    private Paint mPaint;
    /**
     * 验证的图像
     */
    private Bitmap mBitmap;
    /**
     * 验证滑块的高
     */
    private int mUintHeight;
    /**
     * 验证滑块的宽
     */
    private int mUintWidth;
    /**
     * 验证滑块宽占用整体图片大小的比例,默认1/5
     */
    private int mUnitWidthScale;
    /**
     * 验证滑块高度占用整体图片大小的比例,默认1/4
     */
    private int mUnitHeightScale;
    /**
     * 随机生成滑块的X坐标
     */
    private int mUnitRandomX;
    /**
     * 随机生成滑块的Y坐标
     */
    private int mUnitRandomY;
    /***
     * 滑块移动的距离
     */
    private float mUnitMoveDistance = 0;
    /***
     * 滑块图像
     */
    private Bitmap mUnitBp;
    /**
     * 验证位置图像
     */
    private Bitmap mShowBp;
    /**
     * 背景阴影图像
     */
    private Bitmap mShadeBp;
    /**
     * 是否需要旋转
     **/
    private boolean needRotate;
    /**
     * 旋转的角度
     */
    private int rotate;
    /**
     * 判断是否完成的偏差量,默认为10
     */
    public int DEFAULT_DEVIATE;
    /**
     * 判断是否重新绘制图像
     */
    private boolean isReSet = true;

    /**
     * 拼图成功的回调
     **/
    interface onPuzzleListener {
        public void onSuccess();

        public void onFail();
    }

    /**
     * 回调
     */
    private onPuzzleListener mlistener;

    /**
     * 设置回调
     *
     * @param listener
     */
    public void setPuzzleListener(onPuzzleListener listener) {
        this.mlistener = listener;
    }

    public DouYuView(Context context) {
        this(context, null);
    }

    public DouYuView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public DouYuView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.DouYuView);
        mUintWidth = ta.getDimensionPixelOffset(R.styleable.DouYuView_unitHeight, 0);
        mUintHeight = ta.getDimensionPixelOffset(R.styleable.DouYuView_unitHeight, 0);
        mUnitHeightScale = ta.getInteger(R.styleable.DouYuView_unitHeightScale, 4);
        mUnitWidthScale = ta.getInteger(R.styleable.DouYuView_unitWidthScale, 5);
        Drawable showBp = ta.getDrawable(R.styleable.DouYuView_unitShowSrc);
        mShowBp = drawableToBitamp(showBp);
        Drawable shadeBp = ta.getDrawable(R.styleable.DouYuView_unitShadeSrc);
        mShadeBp = drawableToBitamp(shadeBp);
        needRotate = ta.getBoolean(R.styleable.DouYuView_needRotate, true);
        DEFAULT_DEVIATE = ta.getInteger(R.styleable.DouYuView_deviate, 10);
        ta.recycle();
        // 初始化
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        if (needRotate) {
            rotate = (int) (Math.random() * 3) * 90;
        } else {
            rotate = 0;
        }
    }


    /**
     * 随机生成生成滑块的XY坐标
     */
    private void initUnitXY() {
        mUnitRandomX = (int) (Math.random() * (mBitmap.getWidth() - mUintWidth));
        mUnitRandomY = (int) (Math.random() * (mBitmap.getHeight() - mUintHeight));
        // 防止生成的位置距离太近
        if (mUnitRandomX <= mBitmap.getWidth() / 2) {
            mUnitRandomX = mUnitRandomX + mBitmap.getWidth() / 4;
        }
        // 防止生成的X坐标截图时导致异常
        if (mUnitRandomX + mUintWidth > getWidth()) {
            initUnitXY();
            return;
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (isReSet) {
            mBitmap = getBaseBitmap();
            if (0 == mUintWidth) {
                mUintWidth = mBitmap.getWidth() / mUnitWidthScale;
            }
            if (0 == mUintHeight) {
                mUintHeight = mBitmap.getHeight() / mUnitHeightScale;
            }
            initUnitXY();
            mUnitBp = Bitmap.createBitmap(mBitmap, mUnitRandomX, mUnitRandomY, mUintWidth, mUintHeight);
        }
        isReSet = false;
        canvas.drawBitmap(drawTargetBitmap(), mUnitRandomX, mUnitRandomY, mPaint);
        canvas.drawBitmap(drawResultBitmap(mUnitBp), mUnitMoveDistance, mUnitRandomY, mPaint);
    }

    /**
     * 重置
     */
    public void reSet() {
        isReSet = true;
        mUnitMoveDistance = 0;
        if (needRotate) {
            rotate = (int) (Math.random() * 3) * 90;
        } else {
            rotate = 0;
        }
        invalidate();
    }

    /**
     * 获取每次滑动的平均偏移值
     *
     * @return
     */
    public float getAverageDistance(int max) {
        return (float) (mBitmap.getWidth() - mUintWidth) / max;
    }


    /**
     * 滑块移动距离
     *
     * @param distance
     */
    public void setUnitMoveDistance(float distance) {
        mUnitMoveDistance = distance;
        // 防止滑块滑出图片
        if (mUnitMoveDistance > mBitmap.getWidth() - mUintWidth) {
            mUnitMoveDistance = mBitmap.getWidth() - mUintWidth;
        }
        invalidate();

    }

    /**
     * 验证是否拼接成功
     */
    public void testPuzzle() {
        if (Math.abs(mUnitMoveDistance - mUnitRandomX) <= DEFAULT_DEVIATE) {
            if (null != mlistener) {
                mlistener.onSuccess();
            }
        } else {
            if (null != mlistener) {
                mlistener.onFail();
            }
        }
    }

    /**
     * 创建遮挡的图片(阴影部分)
     *
     * @return
     */
    private Bitmap drawTargetBitmap() {
        // 绘制图片
        Bitmap showB;
        if (null != mShowBp) {
            showB = handleBitmap(mShowBp, mUintWidth, mUintHeight);
        } else {
            showB = handleBitmap(BitmapFactory.decodeResource(getResources(), R.mipmap.puzzle_show), mUintWidth, mUintHeight);
        }
        // 如果需要旋转图片,进行旋转,旋转后为了保持和滑块大小一致,需要重新缩放比例
        if (needRotate) {
            showB = handleBitmap(rotateBitmap(rotate, showB), mUintWidth, mUintHeight);
        }
        return showB;
    }

    /**
     * 创建结合的图片(滑块)
     *
     * @param bp
     */
    private Bitmap drawResultBitmap(Bitmap bp) {
        // 绘制图片
        Bitmap shadeB;
        if (null != mShadeBp) {
            shadeB = handleBitmap(mShadeBp, mUintWidth, mUintHeight);
        } else {
            shadeB = handleBitmap(BitmapFactory.decodeResource(getResources(), R.mipmap.puzzle_shade), mUintWidth, mUintHeight);
        }
        // 如果需要旋转图片,进行旋转,旋转后为了和画布大小保持一致,避免出现图像显示不全,需要重新缩放比例
        if (needRotate) {
            shadeB = handleBitmap(rotateBitmap(rotate, shadeB), mUintWidth, mUintHeight);
        }
        Bitmap resultBmp = Bitmap.createBitmap(mUintWidth, mUintHeight,
                Bitmap.Config.ARGB_8888);
        Paint paint = new Paint();
        paint.setAntiAlias(true);
        Canvas canvas = new Canvas(resultBmp);
        canvas.drawBitmap(shadeB, new Rect(0, 0, mUintWidth, mUintHeight),
                new Rect(0, 0, mUintWidth, mUintHeight), paint);
        // 选择交集去上层图片
        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY));
        canvas.drawBitmap(bp, new Rect(0, 0, mUintWidth, mUintHeight),
                new Rect(0, 0, mUintWidth, mUintHeight), paint);
        return resultBmp;
    }

    /**
     * 获取实际显示的图片
     *
     * @return
     */
    public Bitmap getBaseBitmap() {
        Bitmap b = drawableToBitamp(getDrawable());
        float scaleX = 1.0f;
        float scaleY = 1.0f;
        // 如果图片的宽或者高与view的宽高不匹配,计算出需要缩放的比例;缩放后的图片的宽高,一定要大于我们view的宽高;所以我们这里取大值;
        scaleX = getWidth() * 1.0f / b.getWidth();
        scaleY = getHeight() * 1.0f / b.getHeight();
        Matrix matrix = new Matrix();
        matrix.setScale(scaleX, scaleY);
        Bitmap bd = Bitmap.createBitmap(b, 0, 0, b.getWidth(), b.getHeight(),
                matrix, true);
        return bd;
    }

    /**
     * drawable转bitmap
     *
     * @param drawable
     * @return
     */
    private Bitmap drawableToBitamp(Drawable drawable) {
        if (null == drawable) {
            return null;
        }
        if (drawable instanceof BitmapDrawable) {
            BitmapDrawable bd = (BitmapDrawable) drawable;
            return bd.getBitmap();
        }
        int w = drawable.getIntrinsicWidth();
        int h = drawable.getIntrinsicHeight();
        Bitmap bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        drawable.setBounds(0, 0, w, h);
        drawable.draw(canvas);
        return bitmap;
    }

    /**
     * 缩放图片
     *
     * @param bp
     * @param x
     * @param y
     * @return
     */
    public static Bitmap handleBitmap(Bitmap bp, float x, float y) {
        int w = bp.getWidth();
        int h = bp.getHeight();
        float sx = (float) x / w;
        float sy = (float) y / h;
        Matrix matrix = new Matrix();
        matrix.postScale(sx, sy);
        Bitmap resizeBmp = Bitmap.createBitmap(bp, 0, 0, w,
                h, matrix, true);
        return resizeBmp;
    }

    /**
     * 旋转图片
     *
     * @param degree
     * @param bitmap
     * @return
     */
    public Bitmap rotateBitmap(int degree, Bitmap bitmap) {
        Matrix matrix = new Matrix();
        matrix.postRotate(degree);
        Bitmap bm = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(),
                bitmap.getHeight(), matrix, true);
        return bm;
    }
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298
  • 299
  • 300
  • 301
  • 302
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309
  • 310
  • 311
  • 312
  • 313
  • 314
  • 315
  • 316
  • 317
  • 318
  • 319
  • 320
  • 321
  • 322
  • 323
  • 324
  • 325
  • 326
  • 327
  • 328
  • 329
  • 330
  • 331
  • 332
  • 333
  • 334
  • 335
  • 336
  • 337
  • 338
  • 339
  • 340
  • 341
  • 342
  • 343
  • 344
  • 345
  • 346
  • 347
  • 348
  • 349
  • 350
  • 351
  • 352
  • 353
  • 354
  • 355
  • 356
  • 357
  • 358
  • 359
  • 360
  • 361
  • 362
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298
  • 299
  • 300
  • 301
  • 302
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309
  • 310
  • 311
  • 312
  • 313
  • 314
  • 315
  • 316
  • 317
  • 318
  • 319
  • 320
  • 321
  • 322
  • 323
  • 324
  • 325
  • 326
  • 327
  • 328
  • 329
  • 330
  • 331
  • 332
  • 333
  • 334
  • 335
  • 336
  • 337
  • 338
  • 339
  • 340
  • 341
  • 342
  • 343
  • 344
  • 345
  • 346
  • 347
  • 348
  • 349
  • 350
  • 351
  • 352
  • 353
  • 354
  • 355
  • 356
  • 357
  • 358
  • 359
  • 360
  • 361
  • 362

MainActivity:

package com.example.junweiliu.douyutest;

import android.app.Activity;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.widget.SeekBar;
import android.widget.Toast;

public class MainActivity extends Activity {

    /**
     * 滑块
     */
    private SeekBar mSeekBar;
    /**
     * 自定义的控件
     */
    private DouYuView mDY;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
    }

    private void initView() {
        mDY = (DouYuView) findViewById(R.id.dy_v);
        mSeekBar = (SeekBar) findViewById(R.id.sb_dy);
        mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
//                Log.e("main", "当前位置" + i);
                mDY.setUnitMoveDistance(mDY.getAverageDistance(seekBar.getMax()) * i);
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {

            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {
                mDY.testPuzzle();
            }
        });
        mDY.setPuzzleListener(new DouYuView.onPuzzleListener() {
            @Override
            public void onSuccess() {
//                mSeekBar.setEnabled(false);
                Toast.makeText(MainActivity.this, "验证成功", Toast.LENGTH_SHORT).show();
                mSeekBar.setProgress(0);
                mDY.reSet();
            }

            @Override
            public void onFail() {
                Toast.makeText(MainActivity.this, "验证失败", Toast.LENGTH_SHORT).show();
                mSeekBar.setProgress(0);
            }
        });
    }
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65

xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:dy="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:gravity="center"
    android:orientation="vertical"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.example.junweiliu.douyutest.MainActivity">

    <com.example.junweiliu.douyutest.DouYuView
        android:id="@+id/dy_v"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="10dp"
        android:src="@mipmap/test"
        dy:unitHeight="60dp"
        dy:unitWidth="80dp"
        dy:unitShowSrc="@mipmap/star_show"
        dy:unitShadeSrc="@mipmap/star_shade"
        dy:needRotate="true" />

    <SeekBar
        android:id="@+id/sb_dy"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:max="100" />
</LinearLayout>
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

实现的效果图如下:

这里写图片描述

下载地址

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值