Android自定义控件----继承View图案解锁4,外部使用(完结)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zhaihaohao1/article/details/82705294

效果图:
入口:
这里写图片描述
设置图案密码:
这里写图片描述
测试图案密码是否正确
这里写图片描述
自定义控件:GestureLock

package com.example.zhh.shoushimima4;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

import java.util.ArrayList;
import java.util.List;

/**
 * 自定义图案解锁控件
 */
public class GestureLock extends View {
    //  存放点对象的数组
    private Point[][] points = new Point[3][3];
    //  判断是否初始化,onDraw方法会被多次调用,我只要初始化一次
    private boolean inited = false;
    //  三种状态下点对应的Bitmap;用于绘制图片
    private Bitmap bitmapPointError;
    private Bitmap bitmapPointNormal;
    private Bitmap bitmapPointPress;
    //  手指点的x,y
    float mouseX, mouseY;
    //  圆圈的半径
    private float bitmapR;
    //  画笔,用来绘制图片
    Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
    //  画笔,绘制正确的线
    Paint pressPaint = new Paint();
    // 画笔,绘制错误的线
    Paint errorPaint = new Paint();
    //  标记当前是否在绘制状态
    private boolean isDraw = false;
    //  选中点的集合
    private ArrayList<Point> pointList = new ArrayList<Point>();
    //  存储点的位置,是0到8的整数值
    private ArrayList<Integer> passList = new ArrayList<Integer>();
    //   绘制完成的监听对象(用来接口传参用的)
    private OnDrawFinishedListener listener;


    /**
     * 构造方法
     *
     * @param context
     */
    public GestureLock(Context context) {
        super(context);
    }

    public GestureLock(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public GestureLock(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    /**
     * 绘制
     *
     * @param canvas
     */
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
//      初始化(只能在这里初始化)
        if (!inited) {
            init();
        }
//      绘制点
        drawPoints(canvas);

//      绘制线
//      判断有点时,绘制
        if (pointList.size() > 0) {
            Point a = pointList.get(0);
            for (int i = 1; i < pointList.size(); i++) {
                Point b = pointList.get(i);
//              绘制a到b线
                drawLine(canvas, a, b);
                a = b;
            }
//          最后一个点,和手指按下点的连线
            if (isDraw) {
                drawLine(canvas, a, new Point(mouseX, mouseY));
            }
        }
    }

    /**
     * 初始化方法
     */
    private void init() {
        pressPaint.setColor(Color.YELLOW);
        pressPaint.setStrokeWidth(5);
        errorPaint.setColor(Color.RED);
        errorPaint.setStrokeWidth(5);
//      初始化三种状态对应的位图
        bitmapPointError = BitmapFactory.decodeResource(getResources(), R.drawable.error);
        bitmapPointNormal = BitmapFactory.decodeResource(getResources(), R.drawable.normal);
        bitmapPointPress = BitmapFactory.decodeResource(getResources(), R.drawable.press);
//      计算出图片的半径
        bitmapR = bitmapPointError.getHeight() / 2;
//      手机屏幕的宽
        int width = getWidth();
//      手机屏幕的高度
        int height = getHeight();
//      整个九宫格的偏移量,保证横竖配屏都在中间,求得是宽高差的绝对值,这是个公式
        int offset = Math.abs(width - height) / 2;
//      定义横纵坐标的偏移量
        int offsetX, offsetY;
//      小方格的边长
        int space;
        if (width > height) {
//          横屏状态
            space = height / 4;
            offsetX = offset;
            offsetY = 0;
        } else {
//          竖屏状态
            space = width / 4;
            offsetX = 0;
            offsetY = offset;
        }
//      实例化点对象,并设置点位置,并存入数组
        points[0][0] = new Point(offsetX + space, offsetY + space);
        points[0][1] = new Point(offsetX + space * 2, offsetY + space);
        points[0][2] = new Point(offsetX + space * 3, offsetY + space);

        points[1][0] = new Point(offsetX + space, offsetY + space * 2);
        points[1][1] = new Point(offsetX + space * 2, offsetY + space * 2);
        points[1][2] = new Point(offsetX + space * 3, offsetY + space * 2);

        points[2][0] = new Point(offsetX + space, offsetY + space * 3);
        points[2][1] = new Point(offsetX + space * 2, offsetY + space * 3);
        points[2][2] = new Point(offsetX + space * 3, offsetY + space * 3);
//      防止重复初始化
        inited = true;
    }

    /**
     * 绘制点
     *
     * @param canvas
     */
    private void drawPoints(Canvas canvas) {
        for (int i = 0; i < points.length; i++) {
            for (int j = 0; j < points[i].length; j++) {
                if (points[i][j].state == Point.STATE_NORMAL) {
                    //Normal  正常状态 绘制图片
                    canvas.drawBitmap(bitmapPointNormal, points[i][j].x - bitmapR, points[i][j].y - bitmapR, paint);
                } else if (points[i][j].state == Point.STATE_PRESS) {
                    //Press  按下状态 绘制图片
                    canvas.drawBitmap(bitmapPointPress, points[i][j].x - bitmapR, points[i][j].y - bitmapR, paint);

                } else {
                    //ERROR 错误状态 绘制图片
                    canvas.drawBitmap(bitmapPointError, points[i][j].x - bitmapR, points[i][j].y - bitmapR, paint);

                }
            }
        }
    }

    /**
     * 判断选择的点
     * 就是手指点下的地方属于哪个点
     *
     * @return
     */
    private int[] getSelectedPoint() {
        Point pMouse = new Point(mouseX, mouseY);
        for (int i = 0; i < points.length; i++) {
            for (int j = 0; j < points[i].length; j++) {
//             根据按的位置,判断是否属于这个点,
//             按下的位置的距离小于半径,认为按得就是这个点
//             按下位置的距离距圆心,小于半径,认为点的就是这个点
                if (points[i][j].distance(pMouse) < bitmapR) {
//                  选中点在数组中的位置
                    int[] result = new int[2];
                    result[0] = i;
                    result[1] = j;
                    return result;
                }
            }
        }
        return null;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        mouseX = event.getX();
        mouseY = event.getY();
        int[] ij;
        int i, j;

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
//              将所有得点,设置为初始状态
                resetPoints();
                ij = getSelectedPoint();
//             不是空,说明有选中的点
                if (ij != null) {
//                  标记为绘制状态
                    isDraw = true;
                    i = ij[0];
                    j = ij[1];
//                  选中点标记为按下状态
                    points[i][j].state = Point.STATE_PRESS;
//                  把选中的点放在集合中,就是起点
                    pointList.add(points[i][j]);
//                  二维数组,转化一维数组的公式
                    passList.add(i * 3 + j);

                }
                break;
            case MotionEvent.ACTION_MOVE:
//              判断处于绘制状态
                if (isDraw) {
                    ij = getSelectedPoint();
                    if (ij != null) {
//                      不是空,说明手指又碰到了一个点
                        i = ij[0];
                        j = ij[1];
//                      判断选中的点是否在已经在集合中,如果没有就添加
//                      每个点之后能使用一次
                        if (!pointList.contains(points[i][j])) {
                            points[i][j].state = Point.STATE_PRESS;
                            pointList.add(points[i][j]);
//                          二位数组,转化成一维数组的公式
                            passList.add(i * 3 + j);
                        }
                    }
                }

                break;
            case MotionEvent.ACTION_UP:
//              是否绘制正确
                boolean valid = false;
//              判断是否在绘制状态
                if (listener != null && isDraw) {
                    valid = listener.OnDrawFinished(passList);
                }
//              绘制错误
                if (!valid) {
                    for (Point p : pointList) {
                        p.state = Point.STATE_ERROR;
                    }
                }
//              不在绘制
                isDraw = false;
                break;
        }
        this.postInvalidate();
        return true;
    }

    /**
     * 绘制连线
     *
     * @param canvas
     * @param a
     * @param b
     */
    private void drawLine(Canvas canvas, Point a, Point b) {
//      判断第一个手否为按下状态
        if (a.state == Point.STATE_PRESS) {
            canvas.drawLine(a.x, a.y, b.x, b.y, pressPaint);
        } else if (a.state == Point.STATE_ERROR) {
            canvas.drawLine(a.x, a.y, b.x, b.y, errorPaint);
        }
    }

    /**
     * 设置点的初始状态
     * 手滑动一次后,重新绘制
     * 重新绘制点
     */
    public void resetPoints() {
//      清空集合
        passList.clear();
//      清空选中点的集合
        pointList.clear();
        for (int i = 0; i < points.length; i++) {
            for (int j = 0; j < points[i].length; j++) {
//              把所有的点的状态设置为,未点击之前的状态
                points[i][j].state = Point.STATE_NORMAL;
            }
        }
//      重绘
        this.postInvalidate();
    }

    /**
     * 绘制完成的方法
     * @param listener
     */
    public void setOnDrawFinishedListener(OnDrawFinishedListener listener) {
        this.listener = listener;
    }

    /**
     * 绘制完成的监听
     */
    public interface OnDrawFinishedListener {
        //      绘制图案是否正确
//      true 正确
//      false 错误
        boolean OnDrawFinished(List<Integer> passList);
    }

}

实体类Point

package com.example.zhh.shoushimima4;

/**
 * Created by 泽群 on 2015/6/20.
 * 保存9个点
 */
public class Point {
    //  正常状态
    public static int STATE_NORMAL = 0;
    //  按下状态
    public static int STATE_PRESS = 1;
    //  错误状态
    public static int STATE_ERROR = 2;
    //  当前点的x值
    float x;
    //  当前点的y值
    float y;
    //  保存当前点的状态
    int state = STATE_NORMAL;

    //  构造函数,构造点
    public Point(float x, float y) {
        this.x = x;
        this.y = y;
    }

    /**
     * 计算距离的方法
     * @param a
     * @return
     */
    public float distance(Point a) {
//      计算距离的公式
        float distance = (float) Math.sqrt((x - a.x) * (x - a.x) + (y - a.y) * (y - a.y));
        return distance;
    }

}

入口 MainActivity

package com.example.zhh.shoushimima4;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

public class MainActivity extends Activity {
    private Context context;
//  设置按钮
    private Button btnSheZhi;
//  测试按钮
    private Button btnCeShi;

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

    /**
     * 初始化控件
     */
    private void initView() {
        context = MainActivity.this;
        btnSheZhi = (Button) findViewById(R.id.btnSheZhi);
        btnCeShi = (Button) findViewById(R.id.btnCeShi);
    }

    /**
     * 点击事件
     */
    private void initClick(){
        btnSheZhi.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(context, SettingActivity.class);
                startActivity(intent);
            }
        });
        btnCeShi.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(context, LockActivity.class);
                startActivity(intent);

            }
        });
    }


}

布局 activity_main

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity"
    android:orientation="vertical"
    android:gravity="center"
    >

   <LinearLayout
       android:layout_width="200dp"
       android:layout_height="wrap_content"
       android:orientation="vertical"
       android:layout_gravity="center_vertical|center_horizontal"

       >
       <Button
           android:id="@+id/btnSheZhi"
           android:layout_width="match_parent"
           android:layout_height="wrap_content"
           android:text="设置图案密码"
           >
       </Button>
       <Button
           android:id="@+id/btnCeShi"
           android:layout_width="match_parent"
           android:layout_height="wrap_content"
           android:text="测试图案密码"
           android:layout_marginTop="50dp"
           />

   </LinearLayout>


    <!--<com.example.zhh.shoushimima4.GestureLock-->
        <!--android:layout_width="wrap_content"-->
        <!--android:layout_height="wrap_content">-->

    <!--</com.example.zhh.shoushimima4.GestureLock>-->

</LinearLayout>

设置图案密码: SettingActivity

package com.example.zhh.shoushimima4;

import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

import java.util.List;

/**
 * 设置页面
 */
public class SettingActivity extends Activity {
    private Context context;
//  绘制点的集合
    private List<Integer> passList;
//  图案控件
    private GestureLock gestureLock;
//  重置
    private Button btnReset;
//  保存
    private Button btnSave;

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

    }

    /**
     * 初始化控件
     */
    private void initView() {
        context= SettingActivity.this;
        gestureLock = (GestureLock) findViewById(R.id.gestureLock);
        btnReset = (Button) findViewById(R.id.btnReset);
        btnSave = (Button) findViewById(R.id.btnSave);
    }

    /**
     * 控件点击事件
     */
    private void initClick() {
//      绘制是否结束
        gestureLock.setOnDrawFinishedListener(new GestureLock.OnDrawFinishedListener() {
            @Override
            public boolean OnDrawFinished(List<Integer> passList) {
//              绘制结束时,判断绘制的点小于3,则提示不能少于3个点
//              大于3 则说明绘制成功,把绘制的点的list,提成全局变量
                if (passList.size() < 3) {
                    Toast.makeText(SettingActivity.this, "密码不能少于3个点", Toast.LENGTH_SHORT).show();
                    return false;
                } else {
                    SettingActivity.this.passList = passList;
                    return true;
                }
            }
        });
//      重置
        btnReset.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
//             重新设置点
                gestureLock.resetPoints();
            }
        });
//      保存
        btnSave.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
//               passList 集合转成字符串,并保存
                if (passList != null) {
                    StringBuilder sb = new StringBuilder();
                    for (Integer i : passList) {
                        sb.append(i);
                    }
                    SharedPreferences sp = SettingActivity.this.getSharedPreferences("password", Context.MODE_PRIVATE);
                    SharedPreferences.Editor editor = sp.edit();
                    editor.putString("password", sb.toString());
                    editor.commit();

                    Toast.makeText(SettingActivity.this, "保存完成", Toast.LENGTH_SHORT).show();
                    finish();
                }
            }
        });
    }


}

布局文件:activity_setting

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"

    tools:context="com.example.zhh.shoushimima4.SettingActivity">


    <com.example.zhh.shoushimima4.GestureLock
        android:id="@+id/gestureLock"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />


    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_alignParentEnd="true"
        android:layout_alignParentRight="true"
        android:orientation="horizontal">

        <Button
            android:id="@+id/btnReset"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="bottom"
            android:layout_weight="1"
            android:text="重置密码" />

        <Button
            android:id="@+id/btnSave"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="bottom"
            android:layout_weight="1"
            android:text="保存密码" />
    </LinearLayout>
</RelativeLayout>

测试图案密码:LockActivity

package com.example.zhh.shoushimima4;

import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.widget.Toast;

import java.util.List;

/**
 * 测试页面
 */
public class LockActivity extends Activity {
//  图案控件
    private GestureLock gestureLock;
//  拿到保存的密码
    private String password;

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

    /**
     * 初始化控件
     */
    private void initView() {
        gestureLock = (GestureLock) findViewById(R.id.gestureLock);
//      拿到保存的密码
        SharedPreferences sp = getSharedPreferences("password", Context.MODE_PRIVATE);
        password = sp.getString("password", "");
    }

    /**
     * 事件
     */
    private void initOnclick() {
        //      绘制结束的事件
        gestureLock.setOnDrawFinishedListener(new GestureLock.OnDrawFinishedListener() {
            @Override
            public boolean OnDrawFinished(List<Integer> passList) {
//              拿到绘制的点的list,并和保存的对比,如果一致则,正确,否则错误
                StringBuilder sb = new StringBuilder();
                for (Integer i : passList) {
                    sb.append(i);
                }
                if (sb.toString().equals(password)) {
                    Toast.makeText(LockActivity.this, "正确", Toast.LENGTH_SHORT).show();
                    fuwei();
                    return true;
                } else {
                    Toast.makeText(LockActivity.this, "错误", Toast.LENGTH_SHORT).show();
                    fuwei();
                    return false;
                }
            }
        });
    }

    /**
     * 开线程,停留1秒,复位图案
     */
    private void fuwei(){
        //                  开线程,停留1秒,然后发消息给handler,复位图案
        new Thread(){
            @Override
            public void run(){
                try {
                    Thread.sleep(1000);
                    handler.sendEmptyMessage(1);
                } catch (Exception e) {
                    e.printStackTrace();
                }

            }
        }.start();
    }

    /**
     * 接收复位消息
     */
    private Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg){
//          复位
            gestureLock.resetPoints();
        }
    };

}

布局文件activity_lock

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.zhh.shoushimima4.LockActivity">


    <com.example.zhh.shoushimima4.GestureLock
        android:id="@+id/gestureLock"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</RelativeLayout>

源码下载:
https://download.csdn.net/download/zhaihaohao1/10667418
参考视频:
https://www.jikexueyuan.com/course/1592.html

猜你喜欢

转载自blog.csdn.net/zhaihaohao1/article/details/82705294