Android自制九宮格解鎖控件
更新時間:2022年03月28日 17:04:30 作者:Daisuki_ch
這篇文章主要為大家詳細介紹了Android自制九宮格解鎖控件的方法,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
本文實例為大家分享了Android自制九宮格解鎖控件的具體代碼,供大家參考,具體內(nèi)容如下
前兩天從網(wǎng)上學習了下如何自定義一個九宮格解鎖的控件,于是自己根據(jù)邏輯寫了一遍,自定義控件的代碼如下:
public class LockedView extends View {
?
? ? private boolean isFirst = true;//設置第一次加載時為true,后面重新畫圖不再執(zhí)行
? ? private int width, height;//獲取控件的寬度和高度
? ? private int offsetX, offsetY;//獲取點坐標時的X軸和Y軸的偏移量
? ? private Point[][] pointList;//每個點的坐標存放的數(shù)組
? ? private int r;//每個圓的半徑
? ? private Bitmap map1, map2, map3;//3種狀態(tài)的bitmap
? ? private float eventX, eventY;//觸摸控件時的X坐標和Y坐標
? ? private boolean isPressed;//判斷是否觸摸著控件
? ? private boolean moveOnPoint;//判斷是否移動到一個點上了
? ? private boolean isFinish;//判斷手勢是否結束
? ? private List<Point> list = new ArrayList<>();//存放經(jīng)過的點的集合
? ? 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;//接口回調(diào)
?
? ? public LockedView(Context context, AttributeSet attrs) {
? ? ? ? super(context, attrs);
?
? ? }
?
? ? @Override
? ? protected void onDraw(Canvas canvas) {
? ? ? ? super.onDraw(canvas);
? ? ? ? //判斷是否第一次加載
? ? ? ? if (isFirst) {
? ? ? ? ? ? //初始化點坐標
? ? ? ? ? ? initPoints();
? ? ? ? ? ? //初始化畫筆
? ? ? ? ? ? initPaint();
? ? ? ? ? ? isFirst = false;
? ? ? ? }
? ? ? ? //根據(jù)每個點的狀態(tài)來畫對應的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);
? ? ? ? ? ? ? ? //根據(jù)起始點坐標跟停止點坐標來畫線
? ? ? ? ? ? ? ? canvas.drawLine(startPoint.getX(), startPoint.getY(), stopPoint.getX(), stopPoint.getY(), paint);
? ? ? ? ? ? ? ? //把停止點賦值給起始點,這樣每次遍歷的時候起始點都是上一個點
? ? ? ? ? ? ? ? startPoint = stopPoint;
? ? ? ? ? ? }
? ? ? ? ? ? //如果沒有移動到一個點上
? ? ? ? ? ? if (moveOnPoint == false) {
? ? ? ? ? ? ? ? //則根據(jù)最后個點的坐標跟當前手勢移動的坐標畫線
? ? ? ? ? ? ? ? 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();
? ? ? ? ? ? ? ? //根據(jù)觸摸的X,Y坐標以及圓的半徑來判斷是否觸摸到了一個圓上,如果有則返回實例,沒有則返回空
? ? ? ? ? ? ? ? checkedPoint = checkPoint(eventX, eventY, r);
? ? ? ? ? ? ? ? if (checkedPoint != null) {
? ? ? ? ? ? ? ? ? ? //如果實例不為空,則設置選中點的狀態(tài)為選中
? ? ? ? ? ? ? ? ? ? checkedPoint.setState(Point.POINT_XUANZHONG);
? ? ? ? ? ? ? ? ? ? //第一次按下按到點上時設置為true
? ? ? ? ? ? ? ? ? ? isPressed = true;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? break;
? ? ? ? ? ? case MotionEvent.ACTION_MOVE:
? ? ? ? ? ? ? ? //如果按下在一個點上,就會執(zhí)行移動動作的邏輯
? ? ? ? ? ? ? ? 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:
? ? ? ? ? ? ? ? //抬起時,設置第一次按在點上的參數(shù)為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,則表示密碼太短了不符合要求,把選中的點設置為錯誤狀態(tài),并且通過接口回調(diào)返回數(shù)據(jù)
? ? ? ? ? ? ? ? }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里面有值,則根據(jù)值對比下當前的密碼值,如果一樣則解鎖成功,不一樣則失敗
? ? ? ? ? ? ? ? ? ? ? ? ? ? case LOCKED_TRUE:
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? onLocked.onResult("解鎖成功");
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? reset();
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? break;
? ? ? ? ? ? ? ? ? ? ? ? ? ? case LOCKED_FALSE:
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? onLocked.onResult("解鎖失敗");
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? errorPoint();
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? break;
? ? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? ? ? //重新調(diào)用onDraw方法
? ? ? ? ? ? ? ? ? ? ? ? postInvalidate();
? ? ? ? ? ? ? ? ? ? ? ? //此次觸摸消費掉
? ? ? ? ? ? ? ? ? ? ? ? return true;
? ? ? ? ? ? ? ? ? ? }
?
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? }
?
? ? ? ? postInvalidate();
? ? ? ? return true;
? ? }
?
? ? //設置錯誤的點的狀態(tài)
? ? 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);
? ? }
?
? ? //根據(jù)觸摸點的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));
? ? }
?
?
? ? //根據(jù)點的狀態(tài)來畫點
? ? 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;
? ? ? ? }
? ? ? ? //創(chuàng)建一個Point數(shù)組存放點的坐標
? ? ? ? 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,分別是默認狀態(tài)的,選中狀態(tài)的,錯誤狀態(tài)的
? ? ? ? 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;
? ? }
?
? ? //設置回調(diào)接口
? ? 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);
? ? ? ? //根據(jù)存儲在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)){
? ? ? ? ? ? ? ? ? ? //如果匹配了密碼,則返回數(shù)字1代表解鎖成功
? ? ? ? ? ? ? ? ? ? return 1;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? //如果不匹配密碼返回2
? ? ? ? ? ? ? ? return 2;
? ? ? ? ? ? }
?
? ? ? ? ? ? @Override
? ? ? ? ? ? public void onResult(String result) {
? ? ? ? ? ? ? ? //根據(jù)密碼匹配情況再從接口獲得要toast的數(shù)據(jù)
? ? ? ? ? ? ? ? Toast.makeText(LockedActivity.this,result,Toast.LENGTH_SHORT).show();
? ? ? ? ? ? ? ? //數(shù)據(jù)是解鎖成功,則跳轉(zhuǎn)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>
以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關文章
Android實現(xiàn)類似360,QQ管家那樣的懸浮窗
用到的就是WindowManager以及WindowManager.LayoutParams,對這個LayoutParams做文章,當設置為屬性后,然后,創(chuàng)建一個View,將這個View添加到WindowManager中就行2013-06-06
Flutter系統(tǒng)網(wǎng)絡圖片加載流程解析
這篇文章主要介紹了Flutter系統(tǒng)網(wǎng)絡圖片加載流程,從構造函數(shù)開始說起,我們以最簡單的調(diào)用方式舉例,當我們使用Image.network(imageUrl)這種方式來顯示圖片時,Image組件內(nèi)部image屬性就會被賦值NetworkImage,具體操作步驟跟隨小編一起看看吧2022-05-05

