美文网首页
Android-手势密码

Android-手势密码

作者: 哎呦呦胖子斌 | 来源:发表于2018-11-26 13:17 被阅读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();
        }
    
    }
    

    相关文章

      网友评论

          本文标题:Android-手势密码

          本文链接:https://www.haomeiwen.com/subject/svqbqqtx.html