Android 手势解锁

今天老板提出新项目要使用手势解锁,虽然以前用别人的代码实现过,但随着时间的推移我想自己写一个。

以下是效果图



有点粗糙,但勉强能用,下面附上代码,留作笔记。望各位大神指点。

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

import com.***.***.interfaces.GrapNotif;

/**
 * Created by 郭月森 on 2018/6/22.
 */

public class GraphicUnlocking extends View implements View.OnTouchListener{
    //密码记录
    private StringBuffer pass = new StringBuffer();
    //坐标记录
    private int [][] coordinate = new int[9][2];
    //结尾直线点记录
    private float startX=0;
    private float startY=0;
    private float endX=0;
    private float endY=0;
    //对外接口
    private GrapNotif notif = null;
    //默认颜色
    private static int DEFAULT_COLOR = Color.parseColor("#cccccc");
    //选中颜色
    private static int PITCH_ON_COLOR = Color.parseColor("#0088ff");

    public GraphicUnlocking(Context context) {
        super(context);
        setOnTouchListener(this);
    }

    public GraphicUnlocking(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        setOnTouchListener(this);
    }

    public GraphicUnlocking(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        setOnTouchListener(this);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //初始化所有点的坐标
        getCoordinate();
        //绘出所有点
        for(int[] i: coordinate){
        Paint p = new Paint();
            //设置画笔宽度
        p.setStrokeWidth(4);
        //抗锯齿
        p.setAntiAlias(true);
        p.setColor(DEFAULT_COLOR);
            //设置画圆圈
        p.setStyle(Paint.Style.STROKE);
            //绘画
        canvas.drawCircle(i[0],i[1],60,p);
        }
        //创建选中状态的画笔
        Paint p0 = new Paint();
        p0.setColor(PITCH_ON_COLOR);
        p0.setStrokeWidth(10);
        p0.setAntiAlias(true);
        for (int j = 0;j<pass.length();j++){
            //画出选中的圆
            canvas.drawCircle(coordinate[pass.charAt(j)-'0'][0],coordinate[pass.charAt(j)-'0'][1],48,p0);
            //判断是否需要连接线
            if (j>0){
                //画出连接线
                canvas.drawLine(coordinate[pass.charAt(j-1)-'0'][0],coordinate[pass.charAt(j-1)-'0'][1],
        coordinate[pass.charAt(j)-'0'][0],coordinate[pass.charAt(j)-'0'][1],p0);
            }
        }
        if (startX!=0&&endX!=0){
            //画出最后一根线
            canvas.drawLine(startX,startY,endX,endY,p0);
        }
    }

    /**
     * 计算获取所有点的坐标
     */
    private void getCoordinate(){
        int w = getWidth();
        int h = getHeight();
        //得到点之间的距离
        int c = w/4;
        //得到点与高之间的差(这里只实现了竖屏,高比宽大,所以是以宽为基准,使9个点在布局的中心)
        int x = (h-w)/2;
        //得到每个点的XY坐标
        for (int i = 0;i<9;i++){
            coordinate[i][0] = c+((i%3)*c);
            coordinate[i][1] = c+x+((i/3)*c);
        }
    }
    //监听滑动操作
    @Override
    public boolean onTouch(View view, MotionEvent motionEvent) {
        switch (motionEvent.getAction()){
            //手指按下
            case MotionEvent.ACTION_DOWN:
                //清空数据
                cliar();
                //获取按下位置有效性或按下的点
                int p = getPoint(motionEvent.getX(),motionEvent.getY());
                //判断有效性
                    if (p != -1) {
                        //保存直线的起点
                        startX = coordinate[p][0];
                        startY = coordinate[p][1];
                        //保存点
                        pass.append(p);
                        //刷新界面
                        invalidate();
                        if (notif!=null){
                            //通知activity
                            notif.Start(p);
                        }
                    }else {
                        invalidate();
                        return false;
                    }
                break;
            case MotionEvent.ACTION_MOVE://手指滑动
                //获取手指当前位置或当前经过的点
                int pm = getPoint(motionEvent.getX(),motionEvent.getY());
                //判断是否进过了点
                if (pm != -1){
                    //更新直线起点坐标
                    startX =coordinate[pm][0];
                    startY = coordinate[pm][1];
                    //保存点
                    pass.append(pm);
                    if (notif!=null){
                        //通知activity
                        notif.Move(pm,pass.toString());
                    }
                }else {
                    //更新直线结束点坐标
                    endX = motionEvent.getX();
                    endY = motionEvent.getY();
                }
                invalidate();
                break;
            case MotionEvent.ACTION_UP:
                //重置直线坐标但不取消已绘制的图形
                startX = 0;
                startY = 0;
                endX = 0;
                endY = 0;
                if (notif!=null){
                    //通知activity最终密码
                    notif.Stop(pass.toString());
                }
                invalidate();
                break;
        }
        return true;
    }

    /**
     * 将密码归零及重置直线坐标
     */
    public void cliar(){
        pass.setLength(0);
        startX = 0;
        startY = 0;
        endX = 0;
        endY = 0;
        invalidate();
    }
    /**
     * 判断坐标有效性
     * @param x
     * @param y
     * @return为-1时无效,其它为点
     */
    private int getPoint(float x,float y){
        int back = -1;
        //遍历所有坐标
        for (int i = 0;i<coordinate.length;i++){
            //判断坐标是否有效,30为有效范围,可更改
            if (Math.abs(x-coordinate[i][0])<30&&Math.abs(y-coordinate[i][1])<30){
            back = i;
            break;
            }
        }
        //遍历密码
        for (int k = 0;k<pass.length();k++){
            int j = pass.charAt(k)-'0';
            //如果密码已经存在则无效
            back = back==j?-1:back;
        }
        return back;
    }

    /**
     * 设置通知接口
     * @param notif
     */
    public void setNotif(GrapNotif notif) {
        this.notif = notif;
    }
}
以上是布局的实现设置密码和校验密码的逻辑需要单独在activity里面做。

接下来是使用方法

<com.*****.view.GraphicUnlocking
    
android:id="@+id/test_grap"
android:layout_width="match_parent"
android:layout_height="match_parent" />
直接在xml中使用就可以了,加上id就可以在activity里操作该布局(例如传入通知接口)

下面是个人定义的接口

/**
 * Created by 郭月森 on 2018/6/25.
 */

public interface GrapNotif {
    /**
     * 开始
     * @param p 第一个点
     */
    public void Start(int p);

    /**
     * 移动时经过有效点
     * @param p 经过的点
     * @param pass 已经输入的密码
     */
    public void Move(int p, String pass);

    /**
     * 滑动结束
     * @param pass 滑动密码
     */
    public void Stop(String pass);
}
这个布局就一个类和一个接口,基本满足手势解锁的需要。剩下的按照个人需求添加吧!



  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值