Android-手势密码

2018-11-26  本文已影响0人  哎呦呦胖子斌

效果图

手势密码绘制界面(绘制上方的9个提示图标和9个宫格密码图标)
设置手势密码(监听手势的输入,TouchEvent的事件处理,获取输入的手势密码,同时显示在上方的提示区域)
再绘制一次,两次密码不一致提示界面(在实现的时候,错误提示文字增加了“左右晃动的动画”,错误路径颜色标记为红色)

实现思路

1. 正上方的提示区域,用一个类(LockIndicator.java)来实现,自定义view来绘制9个提示图标;

2. 手势密码绘制区域,用一个类(GestureContentView.java)来实现,它继承ViewGroup,添加9个ImageView来表示图标,在onLayout()方法中设置它们的位置;

3. 手势路径绘制,用一个类(GestureDrawline.java)来实现,复写onTouchEvent()方法,在这个方法里监听TouchEvent事件:ACTION_DOWN、ACTION_MOVE、ACTION_UP事件,来绘制手势连接不同点之间的路径;

4. 9个点的对象,用一个类(GesturePoint.java)来实现,保存它的位置、状态、背景图片等相关信息;

5. 手势密码的获取,判断手指当前的位置,根据滑动路径经过的点,按顺序保存绘制的点的顺序(这里点的顺序从上到下分别是1,2,3,4,5,6,7,8,9),不能有重复的点。

诶嘿,故事的发展是这样的~

手势设置Activity(GestureEditActivity)

/**
 *
 * 手势密码设置界面
 *
 */
public class GestureEditActivity extends SlidingActivity implements View.OnClickListener {
    private TextView mTextTitle;
    private TextView mTextCancel;
    private LockIndicator mLockIndicator;
    private TextView mTextTip;
    private FrameLayout mGestureContainer;
    private GestureContentView mGestureContentView;
    private TextView mTextReset; 
    private boolean mIsFirstInput = true;
    private String mFirstPassword = null; 

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_gesture_edit);
        setUpViews();
        setUpListeners();
    }

    private void setUpViews() {
        mTextTitle = (TextView) findViewById(R.id.text_title);
        mTextCancel = (TextView) findViewById(R.id.text_cancel);
        mTextReset = (TextView) findViewById(R.id.text_reset);
        mTextReset.setClickable(false);
//这个地方就是上面那个框框的自定义
        mLockIndicator = (LockIndicator) findViewById(R.id.lock_indicator);
        mTextTip = (TextView) findViewById(R.id.text_tip);
        mGestureContainer = (FrameLayout) findViewById(R.id.gesture_container);
        // 初始化一个显示各个点的viewGroup,这个地方就是手势密码容器类,它继承自viewGroup,并包含9个imageview
        mGestureContentView = new GestureContentView(this, false, "", new GestureDrawline.GestureCallBack() {
            @Override
            public void onGestureCodeInput(String inputCode) {
                if (!isInputPassValidate(inputCode)) {
                    mTextTip.setText(Html.fromHtml("<font color='#c70c1e'>最少链接4个点, 请重新输入</font>"));
                    mGestureContentView.clearDrawlineState(0L);
                    return;
                }
                if (mIsFirstInput) {
                    mFirstPassword = inputCode;
                    updateCodeList(inputCode);
                    mGestureContentView.clearDrawlineState(0L);
                    mTextReset.setClickable(true);
                    mTextReset.setText(getString(R.string.reset_gesture_code));
                } else {
                    if (inputCode.equals(mFirstPassword)) {
                        Toast.makeText(GestureEditActivity.this, "设置成功", Toast.LENGTH_SHORT).show();
                        mGestureContentView.clearDrawlineState(0L);
                        SlidingActivity.password = inputCode;
                        GestureEditActivity.this.finish();
                    } else {
                        mTextTip.setText(Html.fromHtml("<font color='#c70c1e'>与上一次绘制不一致,请重新绘制</font>"));
                        // 左右移动动画
                        Animation shakeAnimation = AnimationUtils.loadAnimation(GestureEditActivity.this, R.anim.shake);
                        mTextTip.startAnimation(shakeAnimation);
                        // 保持绘制的线,1.5秒后清除
                        mGestureContentView.clearDrawlineState(1300L);
                    }
                }
                mIsFirstInput = false;
            }

            @Override
            public void checkedSuccess() {

            }

            @Override
            public void checkedFail() {

            }
        });
        // 设置手势解锁显示到哪个布局里面
        mGestureContentView.setParentView(mGestureContainer);
        updateCodeList("");
    }

    private void setUpListeners() {
        mTextCancel.setOnClickListener(this);
        mTextReset.setOnClickListener(this);
    }

    private void updateCodeList(String inputCode) {
        // 更新选择的图案,这个地方就是将密码code更新到上面的小图标上(这个地方只是将)
        Log.e("ddd",inputCode);
        mLockIndicator.setPath(inputCode);
    }

    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.text_cancel:
                this.finish();
                break;
            case R.id.text_reset:
                mIsFirstInput = true;
                updateCodeList("");
                mTextTip.setText(getString(R.string.set_gesture_pattern));
                break;
            default:
                break;
        }
    }

    private boolean isInputPassValidate(String inputPassword) {
        if (TextUtils.isEmpty(inputPassword) || inputPassword.length() < 4) {
            return false;
        }
        return true;
    }

}

        啊哈,问题是不是来了,就是GestureContentView和LockIndicator是咋画的呢,是哈,我也不知道是怎么画出来的,那么就看人家是怎么画出来的吧好伐。。。

/**
 * 手势密码容器类
 *
 */
public class GestureContentView extends ViewGroup {

    private int baseNum = 6;

    private int[] screenDispaly;

    /**
     * 每个点区域的宽度
     */
    private int blockWidth;
    /**
     * 声明一个集合用来封装坐标集合
     */
//这个类里面表示各个点的属性,以及对应不同状态下点的图标
    private List<GesturePoint> list;
    private Context context;
    private boolean isVerify;
    private GestureDrawline gestureDrawline;

    /**
     * 包含9个ImageView的容器,初始化
     * @param context
     * @param isVerify 是否为校验手势密码
     * @param passWord 用户传入密码
     * @param callBack 手势绘制完毕的回调
     */
    public GestureContentView(Context context, boolean isVerify, String passWord, GestureDrawline.GestureCallBack callBack) {
        super(context);
        screenDispaly = AppUtil.getScreenDispaly(context);
        blockWidth = screenDispaly[0]/3;
        this.list = new ArrayList<GesturePoint>();
        this.context = context;
        this.isVerify = isVerify;
        // 添加9个图标
        addChild();
        // 初始化一个可以画线的view
        gestureDrawline = new GestureDrawline(context, list, isVerify, passWord, callBack);
    }

    private void addChild(){
        for (int i = 0; i < 9; i++) {
            ImageView image = new ImageView(context);
            image.setBackgroundResource(R.drawable.gesture_node_normal);
            this.addView(image);
            invalidate();
            // 第几行
            int row = i / 3;
            // 第几列
            int col = i % 3;
            // 定义点的每个属性
            int leftX = col*blockWidth+blockWidth/baseNum;
            int topY = row*blockWidth+blockWidth/baseNum;
            int rightX = col*blockWidth+blockWidth-blockWidth/baseNum;
            int bottomY = row*blockWidth+blockWidth-blockWidth/baseNum;
            GesturePoint p = new GesturePoint(leftX, rightX, topY, bottomY, image,i+1);
            this.list.add(p);
        }
    }

    public void setParentView(ViewGroup parent){
        // 得到屏幕的宽度
        int width = screenDispaly[0];
        LayoutParams layoutParams = new LayoutParams(width, width);
        this.setLayoutParams(layoutParams);
        gestureDrawline.setLayoutParams(layoutParams);
        parent.addView(gestureDrawline);
        parent.addView(this);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        for (int i = 0; i < getChildCount(); i++) {
            //第几行
            int row = i/3;
            //第几列
            int col = i%3;
            View v = getChildAt(i);
            v.layout(col*blockWidth+blockWidth/baseNum, row*blockWidth+blockWidth/baseNum,
                    col*blockWidth+blockWidth-blockWidth/baseNum, row*blockWidth+blockWidth-blockWidth/baseNum);
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        // 遍历设置每个子view的大小
        for (int i = 0; i < getChildCount(); i++) {
            View v = getChildAt(i);
            v.measure(widthMeasureSpec, heightMeasureSpec);
        }
    }

    /**
     * 保留路径delayTime时间长
     * @param delayTime
     */
    public void clearDrawlineState(long delayTime) {
        gestureDrawline.clearDrawlineState(delayTime);
    }

}

        这个里面就包含了各个点的属性类GesturePoint,以及划线的类GestureDrawline。GesturePoint就有点的坐标和图片样式等属性,很简单,不贴了,GestureDrawline设置了画笔的各种属性,以及不同手势下画笔的路径,同样不想贴了。。。我们来看一下LockIndicator的代码。其实这个小图标的绘制是根据code码来判断图标状态,并没有路径的绘制。(后面又找了一个上面的小图标也能绘制线路的demo,等有时间了再整理起来)

/**
 *
 * 手势密码图案提示
 * @author wulianghuan
 *
 */
public class LockIndicator extends View {
    private int numRow = 3; // 行
    private int numColum = 3; // 列
    private int patternWidth = 40;
    private int patternHeight = 40;
    private int f = 5;
    private int g = 5;
    private int strokeWidth = 3;
    private Paint paint = null;
    private Drawable patternNoraml = null;
    private Drawable patternPressed = null;
    private String lockPassStr; // 手势密码

    public LockIndicator(Context paramContext) {
        super(paramContext);
    }

    public LockIndicator(Context paramContext, AttributeSet paramAttributeSet) {
        super(paramContext, paramAttributeSet, 0);
        paint = new Paint();
        paint.setAntiAlias(true);
        paint.setStrokeWidth(strokeWidth);
        paint.setStyle(Paint.Style.STROKE);
        patternNoraml = getResources().getDrawable(R.drawable.lock_pattern_node_normal);
        patternPressed = getResources().getDrawable(R.drawable.lock_pattern_node_pressed);
        if (patternPressed != null) {
            patternWidth = patternPressed.getIntrinsicWidth();
            patternHeight = patternPressed.getIntrinsicHeight();
            this.f = (patternWidth / 4);
            this.g = (patternHeight / 4);
            patternPressed.setBounds(0, 0, patternWidth, patternHeight);
            patternNoraml.setBounds(0, 0, patternWidth, patternHeight);
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        if ((patternPressed == null) || (patternNoraml == null)) {
            return;
        }
        // 绘制3*3的图标
        for (int i = 0; i < numRow; i++) {
            for (int j = 0; j < numColum; j++) {
                paint.setColor(0xff0000);
                int i1 = j * patternHeight + j * this.g;
                int i2 = i * patternWidth + i * this.f;
                canvas.save();
                canvas.translate(i1, i2);
                String curNum = String.valueOf(numColum * i + (j + 1));
                if (!TextUtils.isEmpty(lockPassStr)) {
                    if (lockPassStr.indexOf(curNum) == -1) {
                        // 未选中
                        patternNoraml.draw(canvas);
                    } else {
                        // 被选中
                        patternPressed.draw(canvas);
                    }
                } else {
                    // 重置状态
                    patternNoraml.draw(canvas);
                }
                canvas.restore();
            }
        }
    }

    @Override
    protected void onMeasure(int paramInt1, int paramInt2) {
        if (patternPressed != null)
            setMeasuredDimension(numColum * patternHeight + this.g
                    * (-1 + numColum), numRow * patternWidth + this.f
                    * (-1 + numRow));
    }

    /**
     * 请求重新绘制
     * @param paramString 手势密码字符序列
     */
    public void setPath(String paramString) {
        lockPassStr = paramString;
        invalidate();
    }

}
上一篇下一篇

猜你喜欢

热点阅读