首頁 > 軟體

Android自制九宮格解鎖控制元件

2022-03-28 19:00:27

本文範例為大家分享了Android自制九宮格解鎖控制元件的具體程式碼,供大家參考,具體內容如下

前兩天從網上學習了下如何自定義一個九宮格解鎖的控制元件,於是自己根據邏輯寫了一遍,自定義控制元件的程式碼如下:

public class LockedView extends View {
 
    private boolean isFirst = true;//設定第一次載入時為true,後面重新畫圖不再執行
    private int width, height;//獲取控制元件的寬度和高度
    private int offsetX, offsetY;//獲取點座標時的X軸和Y軸的偏移量
    private Point[][] pointList;//每個點的座標存放的陣列
    private int r;//每個圓的半徑
    private Bitmap map1, map2, map3;//3種狀態的bitmap
    private float eventX, eventY;//觸控控制元件時的X座標和Y座標
    private boolean isPressed;//判斷是否觸控著控制元件
    private boolean moveOnPoint;//判斷是否移動到一個點上了
    private boolean isFinish;//判斷手勢是否結束
    private List<Point> list = new ArrayList<>();//存放經過的點的集合
    private Point checkedPoint;
    private Paint paint;//畫筆
    public static final int LOCKED_FIRST=0;//第一次載入acitivity時設定密碼時的返回值
    public static final int LOCKED_TRUE=1;//解鎖成功時的返回值
    public static final int LOCKED_FALSE=2;//解鎖失敗的返回值
    private OnLockedChangedListener onLocked;//介面回撥
 
    public LockedView(Context context, AttributeSet attrs) {
        super(context, attrs);
 
    }
 
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //判斷是否第一次載入
        if (isFirst) {
            //初始化點座標
            initPoints();
            //初始化畫筆
            initPaint();
            isFirst = false;
        }
        //根據每個點的狀態來畫對應的bitmap
        drawPoints(canvas);
        //畫手勢滑動過程中的線
        drawLines(canvas);
    }
 
    //初始化畫筆
    private void initPaint() {
        paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        //設定畫筆顏色為藍色
        paint.setColor(getResources().getColor(R.color.LockedColor));
        //設定畫筆的寬度為8
        paint.setStrokeWidth(8);
    }
 
    //畫線
    private void drawLines(Canvas canvas) {
        //如果集合有值
        if (list.size() > 0) {
            //獲得起始點的範例
            Point startPoint = list.get(0);
            for (int i = 1; i < list.size(); i++) {
                //獲得停止點的範例
                Point stopPoint = list.get(i);
                //根據起始點座標跟停止點座標來畫線
                canvas.drawLine(startPoint.getX(), startPoint.getY(), stopPoint.getX(), stopPoint.getY(), paint);
                //把停止點賦值給起始點,這樣每次遍歷的時候起始點都是上一個點
                startPoint = stopPoint;
            }
            //如果沒有移動到一個點上
            if (moveOnPoint == false) {
                //則根據最後個點的座標跟當前手勢移動的座標畫線
                canvas.drawLine(startPoint.getX(), startPoint.getY(), eventX, eventY, paint);
            }
 
        }
    }
 
    //設定觸控事件
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        //獲得觸控的X座標
        eventX = event.getX();
        //獲得觸控的Y座標
        eventY = event.getY();
        //每次觸控到離開螢幕之前都預設為沒有完成
        isFinish = false;
        //預設移動到點上了
        moveOnPoint = true;
        //選中的點
        checkedPoint = null;
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                //如果按下,重置下點
                reset();
                //根據觸控的X,Y座標以及圓的半徑來判斷是否觸控到了一個圓上,如果有則返回範例,沒有則返回空
                checkedPoint = checkPoint(eventX, eventY, r);
                if (checkedPoint != null) {
                    //如果範例不為空,則設定選中點的狀態為選中
                    checkedPoint.setState(Point.POINT_XUANZHONG);
                    //第一次按下按到點上時設定為true
                    isPressed = true;
                }
                break;
            case MotionEvent.ACTION_MOVE:
                //如果按下在一個點上,就會執行移動動作的邏輯
                if (isPressed) {
                    //同上
                    checkedPoint = checkPoint(eventX, eventY, r);
                    if (checkedPoint != null) {
                        checkedPoint.setState(Point.POINT_XUANZHONG);
                        //如果範例不為空,則設定移動到了點上
                        moveOnPoint = true;
                    } else {
                        //否則設定沒有移動到點上
                        moveOnPoint = false;
                    }
                }
                break;
            case MotionEvent.ACTION_UP:
                //擡起時,設定第一次按在點上的引數為false以及完成了觸控過程
                isPressed = false;
                isFinish = true;
                break;
            case MotionEvent.ACTION_CANCEL:
                isPressed = false;
                isFinish = true;
                break;
        }
        //如果第一下按在了點上並且沒有完成觸控並且選中的點的範例不為空
        if (isPressed && !isFinish && checkedPoint != null) {
            //判斷這個範例是否在list集合中
            if (isInList(checkedPoint)) {
                //如果在,則設定沒有移動在點上
                moveOnPoint = false;
            } else {
                //否則就新增到集合裡面
                list.add(checkedPoint);
            }
            //如果完成了觸控
        } else if (isFinish) {
            if (list.size() > 0) {
                //如果集合長度為1,則表示只是摸到了一個點,直接重置
                if(list.size()==1){
                    reset();
                    //如果集合長度小於5,則表示密碼太短了不符合要求,把選中的點設定為錯誤狀態,並且通過介面回撥返回資料
                }else if(list.size()<5){
                    errorPoint();
                    if(onLocked!=null){
                        onLocked.onResult("密碼太短");
                    }
                    //如果集合長度滿足要求,則通過介面的返回值,來判斷不同的情況
                }else if(list.size()>=5){
                    StringBuffer buffer=new StringBuffer();
                    for(int i=0;i<list.size();i++){
                        buffer.append(list.get(i).getIndex());
                    }
                    if(onLocked!=null){
                        switch (onLocked.onPassword(buffer.toString())){
                            //第一次開啟activity時,shared裡面沒有值,則把當前的密碼存到shared裡
                            case LOCKED_FIRST:
                                onLocked.onResult("設定密碼成功");
                                reset();
                                break;
                            //如果shared裡面有值,則根據值對比下當前的密碼值,如果一樣則解鎖成功,不一樣則失敗
                            case LOCKED_TRUE:
                                onLocked.onResult("解鎖成功");
                                reset();
                                break;
                            case LOCKED_FALSE:
                                onLocked.onResult("解鎖失敗");
                                errorPoint();
                                break;
                        }
                        //重新呼叫onDraw方法
                        postInvalidate();
                        //此次觸控消費掉
                        return true;
                    }
 
                }
            }
        }
 
        postInvalidate();
        return true;
    }
 
    //設定錯誤的點的狀態
    private void errorPoint() {
        for(int i=0;i<list.size();i++){
            list.get(i).setState(Point.POINT_XUANCUO);
        }
    }
 
    //判斷點是否在集合裡面
    private boolean isInList(Point checkedPoint) {
        return list.contains(checkedPoint);
    }
 
    //根據觸控點的X,Y軸座標以及圓半徑判斷是否觸控到了一個圓
    private Point checkPoint(float eventX, float eventY, int r) {
        for (int i = 0; i < pointList.length; i++) {
            for (int j = 0; j < pointList[i].length; j++) {
                Point point = pointList[i][j];
                double juli = getPointJuli(eventX, eventY, point.getX(), point.getY());
                if (juli < r) {
                    return point;
                }
            }
        }
        return null;
    }
 
    //重置點
    private void reset() {
        for (int i = 0; i < list.size(); i++) {
            list.get(i).setState(Point.POINT_MOREN);
        }
        list.clear();
    }
 
    //獲取兩點之間的距離
    private double getPointJuli(float eventX, float eventY, int x, int y) {
        return Math.sqrt(Math.abs(eventX - x) * Math.abs(eventX - x) + Math.abs(eventY - y) * Math.abs(eventY - y));
    }
 
 
    //根據點的狀態來畫點
    private void drawPoints(Canvas canvas) {
        for (int i = 0; i < pointList.length; i++) {
            for (int j = 0; j < pointList[i].length; j++) {
                Point point = pointList[i][j];
                switch (point.getState()) {
                    case Point.POINT_MOREN:
                        canvas.drawBitmap(map1, point.getX() - r, point.getY() - r, null);
                        break;
                    case Point.POINT_XUANZHONG:
                        canvas.drawBitmap(map2, point.getX() - r, point.getY() - r, null);
                        break;
                    case Point.POINT_XUANCUO:
                        canvas.drawBitmap(map3, point.getX() - r, point.getY() - r, null);
                        break;
                }
            }
        }
    }
 
    //初始化點座標和bitmap
    private void initPoints() {
        //獲得控制元件的寬度
        width = getWidth();
        //獲得控制元件的高度
        height = getHeight();
        //設定X的偏移量為0
        offsetX = 0;
        //設定Y的偏移量為0
        offsetY = 0;
        //如果是豎屏則
        if (width < height) {
            offsetY = (height - width) / 2;
            height = width;
        } else {
            offsetX = (width - height) / 2;
            width = height;
        }
        //建立一個Point陣列存放點的座標
        pointList = new Point[3][3];
        //設定索引,好判斷密碼
        int index=1;
        //遍歷,用演演算法算出每個點的座標
        for (int i = 0; i < pointList.length; i++) {
            for (int j = 0; j < pointList[i].length; j++) {
                pointList[i][j] = new Point(offsetX + width / 4 * (i + 1), offsetY + height / 4 * (j + 1));
                pointList[i][j].setIndex(index);
                index++;
            }
        }
        //設定3個bitmap,分別是預設狀態的,選中狀態的,錯誤狀態的
        map1 = BitmapFactory.decodeResource(getResources(), R.drawable.aa);
        map2 = BitmapFactory.decodeResource(getResources(), R.drawable.bb);
        map3 = BitmapFactory.decodeResource(getResources(), R.drawable.cc);
        //獲得圓的半徑
        r = map1.getWidth() / 2;
    }
 
    public void setOnLockedChangedListener(OnLockedChangedListener onLocked){
        this.onLocked=onLocked;
    }
 
    //設定回撥介面
    public interface OnLockedChangedListener{
        public int onPassword(String password);
        public void onResult(String result);
    }
}

activity程式碼:

public class LockedActivity extends Activity {
 
    private LockedView lockedView;
    private TextView textView;
    private SharedPreferences preferences;
    private String pass;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_locked);
        //初始化控制元件
        lockedView= (LockedView) findViewById(R.id.lockedView);
        textView= (TextView) findViewById(R.id.textView);
        //獲得shared
        preferences=getSharedPreferences("Locked",MODE_PRIVATE);
        //根據儲存在shared裡的鍵獲得密碼值
        pass=preferences.getString("password","");
        //如果沒有代表第一次啟動activity,則textview設定文字為情設定密碼,如果不是第一次啟動,則設定文字為請解鎖
        if(pass.equals("")){
            textView.setText("請設定密碼");
        }else{
            textView.setText("請解鎖");
        }
        lockedView.setOnLockedChangedListener(new LockedView.OnLockedChangedListener() {
            @Override
            public int onPassword(String password) {
                //從shared裡取值
                pass=preferences.getString("password","");
                //如果值為空,則把介面返回的password存入shared裡
                if(pass.equals("")){
                    SharedPreferences.Editor editor=preferences.edit();
                    editor.putString("password",password);
                    editor.commit();
                    textView.setText("請解鎖");
                    return 0;
                }else if(pass.equals(password)){
                    //如果匹配了密碼,則返回數位1代表解鎖成功
                    return 1;
                }
                //如果不匹配密碼返回2
                return 2;
            }
 
            @Override
            public void onResult(String result) {
                //根據密碼匹配情況再從介面獲得要toast的資料
                Toast.makeText(LockedActivity.this,result,Toast.LENGTH_SHORT).show();
                //資料是解鎖成功,則跳轉activity
                if(result.equals("解鎖成功")){
                    Intent intent=new Intent(LockedActivity.this,MainActivity.class);
                    startActivity(intent);
                }
            }
        });
    }
}

佈局程式碼如下:

<?xml version="1.0" encoding="utf-8"?>
<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">
 
    <com.example.chaohengdai.test922.LockedView
        android:id="@+id/lockedView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
    <TextView
        android:id="@+id/textView"
        android:textSize="20sp"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="50dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
</RelativeLayout>

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援it145.com。


IT145.com E-mail:sddin#qq.com