Android自定義控件實(shí)現(xiàn)九宮格解鎖功能
更新時(shí)間:2021年09月10日 10:10:40 作者:一生中所愛
這篇文章主要為大家詳細(xì)介紹了Android自定義控件實(shí)現(xiàn)九宮格解鎖功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
最終Android九宮格解鎖效果如下

1.進(jìn)行定義實(shí)體point點(diǎn)
public class Point {
private float x;
private float y;
//正常模式
public static final int NORMAL_MODE = 1;
//按下模式
public static final int PRESSED_MODE = 2;
//錯(cuò)誤模式
public static final int ERROR_MODE = 3;
private int state = NORMAL_MODE;
private String mark;
public Point(float x, float y, String mark) {
this.x = x;
this.y = y;
this.mark = mark;
}
public float getX() {
return x;
}
public void setX(float x) {
this.x = x;
}
public float getY() {
return y;
}
public void setY(float y) {
this.y = y;
}
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
}
public String getMark() {
return mark;
}
public void setMark(String mark) {
this.mark = mark;
}
}
2.自定義ScreenLockView
public class ScreenLockView extends View {
private static final String TAG = "ScreenLockView";
// 錯(cuò)誤格子的圖片
private Bitmap errorBitmap;
// 正常格子的圖片
private Bitmap normalBitmap;
// 手指按下時(shí)格子的圖片
private Bitmap pressedBitmap;
// 錯(cuò)誤時(shí)連線的圖片
private Bitmap lineErrorBitmap;
// 手指按住時(shí)連線的圖片
private Bitmap linePressedBitmap;
// 偏移量,使九宮格在屏幕中央
private int offset;
// 九宮格的九個(gè)格子是否已經(jīng)初始化
private boolean init;
// 格子的半徑
private int radius;
// 密碼
private String password = "123456";
// 九個(gè)格子
private Point[][] points = new Point[3][3];
private int width;
private int height;
private Matrix matrix = new Matrix();
private float moveX = -1;
private float moveY = -1;
// 是否手指在移動(dòng)
private boolean isMove;
// 是否可以觸摸,當(dāng)用戶抬起手指,劃出九宮格的密碼不正確時(shí)為不可觸摸
private boolean isTouch = true;
// 用來存儲(chǔ)記錄被按下的點(diǎn)
private List<Point> pressedPoint = new ArrayList<>();
// 屏幕解鎖監(jiān)聽器
private OnScreenLockListener listener;
public ScreenLockView(Context context) {
super(context);
init();
}
public ScreenLockView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public ScreenLockView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
errorBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.bitmap_error);
normalBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.bitmap_normal);
pressedBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.bitmap_pressed);
lineErrorBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.line_error);
linePressedBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.line_pressed);
radius = normalBitmap.getWidth() / 2;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
if (widthSize > heightSize) {
offset = (widthSize - heightSize) / 2;
} else {
offset = (heightSize - widthSize) / 2;
}
setMeasuredDimension(widthSize, heightSize);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (!init) {
width = getWidth();
height = getHeight();
initPoint();
init = true;
}
drawPoint(canvas);
if (moveX != -1 && moveY != -1) {
drawLine(canvas);
}
}
// 畫直線
private void drawLine(Canvas canvas) {
// 將pressedPoint中的所有格子依次遍歷,互相連線
for (int i = 0; i < pressedPoint.size() - 1; i++) {
// 得到當(dāng)前格子
Point point = pressedPoint.get(i);
// 得到下一個(gè)格子
Point nextPoint = pressedPoint.get(i + 1);
// 旋轉(zhuǎn)畫布
canvas.rotate(RotateDegrees.getDegrees(point, nextPoint), point.getX(), point.getY());
matrix.reset();
// 根據(jù)距離設(shè)置拉伸的長度
matrix.setScale(getDistance(point, nextPoint) / linePressedBitmap.getWidth(), 1f);
// 進(jìn)行平移
matrix.postTranslate(point.getX(), point.getY() - linePressedBitmap.getWidth() / 2);
if (point.getState() == Point.PRESSED_MODE) {
canvas.drawBitmap(linePressedBitmap, matrix, null);
} else {
canvas.drawBitmap(lineErrorBitmap, matrix, null);
}
// 把畫布旋轉(zhuǎn)回來
canvas.rotate(-RotateDegrees.getDegrees(point, nextPoint), point.getX(), point.getY());
}
// 如果是手指在移動(dòng)的情況
if (isMove) {
Point lastPoint = pressedPoint.get(pressedPoint.size() - 1);
canvas.rotate(RotateDegrees.getDegrees(lastPoint, moveX, moveY), lastPoint.getX(), lastPoint.getY());
matrix.reset();
Log.i(TAG, "the distance : " + getDistance(lastPoint, moveX, moveY) / linePressedBitmap.getWidth());
matrix.setScale(getDistance(lastPoint, moveX, moveY) / linePressedBitmap.getWidth(), 1f);
matrix.postTranslate(lastPoint.getX(), lastPoint.getY() - linePressedBitmap.getWidth() / 2);
canvas.drawBitmap(linePressedBitmap, matrix, null);
canvas.rotate(-RotateDegrees.getDegrees(lastPoint, moveX, moveY), lastPoint.getX(), lastPoint.getY());
}
}
// 根據(jù)point和坐標(biāo)點(diǎn)計(jì)算出之間的距離
private float getDistance(Point point, float moveX, float moveY) {
Point b = new Point(moveX,moveY,null);
return getDistance(point,b);
}
// 根據(jù)兩個(gè)point計(jì)算出之間的距離
private float getDistance(Point point, Point nextPoint) {
return (float) Math.sqrt(Math.pow(nextPoint.getX() - point.getX(), 2f) + Math.pow(nextPoint.getY() - point.getY(), 2f));
}
private void drawPoint(Canvas canvas) {
for (int i = 0; i < points.length; i++) {
for (int j = 0; j < points[i].length; j++) {
int state = points[i][j].getState();
if (state == Point.NORMAL_MODE) {
canvas.drawBitmap(normalBitmap, points[i][j].getX() - radius, points[i][j].getY() - radius, null);
} else if (state == Point.PRESSED_MODE) {
canvas.drawBitmap(pressedBitmap, points[i][j].getX() - radius, points[i][j].getY() - radius, null);
} else {
canvas.drawBitmap(errorBitmap, points[i][j].getX() - radius, points[i][j].getY() - radius, null);
}
}
}
}
//初始化九宮格的點(diǎn)
private void initPoint() {
points[0][0] = new Point(width / 4, offset + width / 4, "0");
points[0][1] = new Point(width / 2, offset + width / 4, "1");
points[0][2] = new Point(width * 3 / 4, offset + width / 4, "2");
points[1][0] = new Point(width / 4, offset + width / 2, "3");
points[1][1] = new Point(width / 2, offset + width / 2, "4");
points[1][2] = new Point(width * 3 / 4, offset + width / 2, "5");
points[2][0] = new Point(width / 4, offset + width * 3 / 4, "6");
points[2][1] = new Point(width / 2, offset + width * 3 / 4, "7");
points[2][2] = new Point(width * 3 / 4, offset + width * 3 / 4, "8");
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (isTouch) {
float x = event.getX();
float y = event.getY();
Point point;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// 判斷用戶觸摸的點(diǎn)是否在九宮格的任意一個(gè)格子之內(nèi)
point = isPoint(x, y);
if (point != null) {
point.setState(Point.PRESSED_MODE); // 切換為按下模式
pressedPoint.add(point);
}
break;
case MotionEvent.ACTION_MOVE:
if (pressedPoint.size() > 0) {
point = isPoint(x, y);
if (point != null) {
if (!crossPoint(point)) {
point.setState(Point.PRESSED_MODE);
pressedPoint.add(point);
}
}
moveX = x;
moveY = y;
isMove = true;
}
break;
case MotionEvent.ACTION_UP:
isMove = false;
String tempPwd = "";
for (Point p : pressedPoint) {
tempPwd += p.getMark();
}
if (listener != null) {
listener.getStringPassword(tempPwd);
}
if (tempPwd.equals(password)) {
if (listener != null) {
listener.isPassword(true);
this.postDelayed(runnable, 1000);
}
} else {
for (Point p : pressedPoint) {
p.setState(Point.ERROR_MODE);
}
isTouch = false;
this.postDelayed(runnable, 1000);
if (listener != null) {
listener.isPassword(false);
}
}
break;
}
invalidate();
}
return true;
}
private boolean crossPoint(Point point) {
if (pressedPoint.contains(point)) {
return true;
}
return false;
}
public interface OnScreenLockListener {
public void getStringPassword(String password);
public void isPassword(boolean flag);
}
public void setOnScreenLockListener(OnScreenLockListener listener) {
this.listener = listener;
}
private Point isPoint(float x, float y) {
Point point;
for(int i = 0; i<points.length;i++){
for (int j = 0; j < points[i].length; j++) {
point = points[i][j];
if (isContain(point, x, y)) {
return point;
}
}
}
return null;
}
private boolean isContain(Point point, float x, float y) {
return Math.sqrt(Math.pow(x - point.getX(), 2f) + Math.pow(y - point.getY(), 2f)) <= radius;
}
private Runnable runnable = new Runnable() {
@Override
public void run() {
isTouch = true;
reset();
invalidate();
}
};
// 重置格子
private void reset(){
for (int i = 0; i < points.length; i++) {
for (int j = 0; j < points[i].length; j++) {
points[i][j].setState(Point.NORMAL_MODE);
}
}
pressedPoint.clear();
}
}
3.RotateDegress類
public class RotateDegrees {
public static float getDegrees(Point a, Point b){
float degrees = 0 ;
float aX = a.getX();
float aY = a.getY();
float bX = b.getX();
float bY = b.getY();
if(aX == bX){
if(aY<bY){
degrees = 90;
}else{
degrees = 270;
}
}else if(bY == aY){
if(aX<bX){
degrees = 0 ;
}else{
degrees = 180;
}
}else{
if(aX>bX){
if(aY>bY){
degrees = 180 + (float)(Math.atan2(aY-bY,aX-bX)*180/Math.PI);
}else{
degrees = 180 - (float)(Math.atan2(bY -aY,aX - bX)*180/Math.PI);
}
}else{
if(aY>bY){
degrees = 360 -(float)(Math.atan2(aY - bY,bX-aX)*180/Math.PI);
}else{
degrees = (float)(Math.atan2(bY - aY,bX - aX)*180/Math.PI);
}
}
}
return degrees;
}
public static float getDegrees(Point a, float bX,float bY){
Point b = new Point(bX,bY,null);
return getDegrees(a,b);
}
}
用到的圖片資源



4.MainActivity中使用
public class MainActivity extends AppCompatActivity {
private ScreenLockView screenLockView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
screenLockView = (ScreenLockView) findViewById(R.id.slv);
screenLockView.setOnScreenLockListener(new ScreenLockView.OnScreenLockListener() {
@Override
public void getStringPassword(String password) {
}
@Override
public void isPassword(boolean flag) {
String content;
if (flag) {
content = "密碼正確";
} else {
content = "密碼錯(cuò)誤";
}
Toast.makeText(MainActivity.this, content, Toast.LENGTH_SHORT).show();
}
});
}
}
5.布局文件
<?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:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="com.example.admin.ninegridunlock.MainActivity"> <com.example.admin.ninegridunlock.ScreenLockView android:id="@+id/slv" android:layout_width="match_parent" android:layout_height="match_parent" /> </RelativeLayout>
以上就是本文的全部內(nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
您可能感興趣的文章:
- android 九宮格滑動(dòng)解鎖開機(jī)實(shí)例源碼學(xué)習(xí)
- 輕松實(shí)現(xiàn)Android自定義九宮格圖案解鎖
- Android實(shí)現(xiàn)九宮格解鎖
- 輕松實(shí)現(xiàn)安卓(Android)九宮格解鎖
- Android實(shí)現(xiàn)九宮格解鎖的實(shí)例代碼
- 使用Android自定義控件實(shí)現(xiàn)滑動(dòng)解鎖九宮格
- Android 仿小米鎖屏實(shí)現(xiàn)九宮格解鎖功能(無需圖片資源)
- Android自定義View九宮格手勢密碼解鎖
- Android實(shí)現(xiàn)九宮格手勢解鎖
- Android自定義控件實(shí)現(xiàn)九宮格解鎖
相關(guān)文章
關(guān)于Android中Gradle和jar包下載慢的問題及解決方法
這篇文章主要介紹了解決Android中Gradle和jar包下載慢的問題,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-10-10
Android網(wǎng)絡(luò)編程之獲取網(wǎng)絡(luò)上的Json數(shù)據(jù)實(shí)例
這篇文章主要介紹了Android網(wǎng)絡(luò)編程之獲取網(wǎng)絡(luò)上的Json數(shù)據(jù)實(shí)例,本文用完整的代碼實(shí)例講解了在Android中讀取網(wǎng)絡(luò)中Json數(shù)據(jù)的方法,需要的朋友可以參考下2014-10-10
Android調(diào)用手機(jī)攝像頭拍照和錄音功能
這篇文章主要為大家詳細(xì)介紹了Android調(diào)用手機(jī)攝像頭拍照和錄音功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-03-03

