網站首頁 編程語言 正文
本文實例為大家分享了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>
原文鏈接:https://blog.csdn.net/Daisuki_ch/article/details/52677394
相關推薦
- 2022-02-17 docker容器內的數據存放在哪里
- 2022-03-07 Go語言參數傳遞是傳值還是傳引用_Golang
- 2022-06-21 Android?studio實現兩個界面間的切換_Android
- 2023-11-19 樹莓派/arm設備上安裝火狐Firefox瀏覽器
- 2023-04-22 python的open函數使用案例代碼_python
- 2022-05-25 pytorch?hook?鉤子函數的用法_python
- 2022-02-24 Matlab中plot函數及legend函數詳解
- 2022-05-13 顯示類型轉換 const_cast, static_cast, dynamic_cast, tein
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支