Android實(shí)現(xiàn)九宮格解鎖的實(shí)例代碼
當(dāng)年感覺九宮格解鎖很是高大上,一臉懵逼,今天正好要做解鎖這一塊業(yè)務(wù),回頭來(lái)看九宮格,這特么簡(jiǎn)單啊
首先理清一下邏輯,我們要做NxN的九宮格 下圖是3x3的簡(jiǎn)單圖例
// -(--)-(--)-(--)-
// -(--)-(--)-(--)-
// -(--)-(--)-(--)-

我們就把九宮格分解成
外圓 、內(nèi)圓、連線三部分
外圓半徑Radius,內(nèi)圓半徑dp(5)
建立一個(gè)集合來(lái)放置 外圓的圓心( 內(nèi)圓的圓心也一樣)
private ArrayList<Point> mListCircle;//外圓的圓心
for (int i = 0; i < mCount; i++) {
for (int j = 0; j < mCount; j++) {
Point point = new Point((3 * i + 2) * (int) mRadius, (3 * j + 2) * (int) mRadius);
mListCircle.add(point);
}
}
這樣我們就初始化好了內(nèi)外圓的位置point集合
我們draw一下看一下效果
void drawAll_Cicle(Canvas canvas) {
for (int i = 0; i < mListCircle.size(); i++) {
Point point = mListCircle.get(i);
canvas.drawCircle(point.x, point.y, mRadius, mPaint);
canvas.drawCircle(point.x, point.y, mMinRadius, miniPaint);
}
效果圖就是上圖了 (哈哈一樣的)
主結(jié)構(gòu)已經(jīng)畫完了,接下來(lái)就是如何繪制點(diǎn)與點(diǎn)之間的連線了,有人會(huì)覺得沒思路,其實(shí)很簡(jiǎn)單了,以3X3 為例子哈
我們可以給這九個(gè)棋子編號(hào)1--9號(hào),把他存入LinkedHashSet中,著重介紹這個(gè)LinkedHashSet有順序不重復(fù)這個(gè)真的在合適不過了。
這樣就不會(huì)有重復(fù)的事情了。這個(gè)和解鎖時(shí)候的密碼也很契合。所以選對(duì)了存儲(chǔ)方式會(huì)事半功倍。
還有一個(gè)問題,就是點(diǎn)擊邊界問題,這個(gè)好解決,我們把每一個(gè)棋子都花矩形,通過圓來(lái)控制邊界,(其實(shí)也可以通過矩形來(lái)控制邊界,這個(gè)也很簡(jiǎn)單,原理是差不多的,有興趣的同學(xué)可以下去試試)
同樣也是用集合。和上邊的圓是一樣一樣的。
ArrayList<RectF> mListRectFs;
for (int i = 0; i < mCount; i++) {
for (int j = 0; j < mCount; j++) {
RectF rectF = new RectF((3 * i + 1) * mRadius, (3 * j + 1) * mRadius, (3 * i + 3) * mRadius, (3 * j + 3) * mRadius);
mListRectFs.add(rectF);
}
}
好邊界也有了,我們來(lái)計(jì)算邊界返回編號(hào)
/**
* 點(diǎn)和圓形碰撞檢測(cè)
*
* @param x1 手指接觸點(diǎn)
* @param y1 手指接觸點(diǎn)
* @param x2 外圓
* @param y2 外圓
* @param radius 半徑
* @return
*/
private boolean isCollision(float x1, float y1, float x2, float y2, float radius) {
if (Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2)) <= radius) {
// 如果點(diǎn)和圓心距離小于或等于半徑則認(rèn)為發(fā)生碰撞
return true;
}
return false;
}
/**
* 判斷觸摸點(diǎn)在哪個(gè)item上
*
* @param x
* @param y
* @return
*/
private int touchIndex(float x, float y) {
for (int i = 0; i < mListCircle.size(); i++) {
Point p = mListCircle.get(i);
if (isCollision(x, y, p.x, p.y, mRadius)) {
return i;
}
}
return -1;
}
幾個(gè)關(guān)鍵點(diǎn)都寫到了接下來(lái)就是具體的細(xì)節(jié)了。我把代碼都貼上來(lái),注釋的很詳細(xì)。當(dāng)然加入了一個(gè)手指觸控點(diǎn),更加好看一些。
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.Log;
import android.view.HapticFeedbackConstants;
import android.view.MotionEvent;
import android.view.View;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
/**
* Created by ld on 2017/7/25.
*/
public class NineGridLockView extends View {
private Context context;
int index_point = 0;
private float mDensity;
private int mCount = 3;
private ArrayList<RectF> mListRectFs;//圓的外形矩形
private ArrayList<Point> mListCircle;//外圓的圓心
private LinkedHashSet<Integer> mSetPoints;//記錄需要連線的外圓圓心點(diǎn)在mListCircle中的索引值,LinkedHashSet線性不可重復(fù) 集合,F(xiàn)IFO
private Paint mPaint, miniPaint;//畫筆
private float mRadius;//外圓半徑
private float mMinRadius;//內(nèi)圓半徑
private float mStrokeWidth = 10; //繪制時(shí)的畫筆寬度
private Point mMovePoint; //記錄手指移動(dòng)時(shí)的點(diǎn)
public NineGridLockView(Context context) {
this(context, null);
}
public NineGridLockView(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
mDensity = getContext().getResources().getDisplayMetrics().density;
mListRectFs = new ArrayList<>();
mListCircle = new ArrayList<>();
mPaint = new Paint();
mPaint.setStrokeWidth(5);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setAntiAlias(true);
mPaint.setColor(Color.BLUE);
miniPaint = new Paint();
miniPaint.setColor(Color.BLACK);
miniPaint.setAntiAlias(true);
miniPaint.setStyle(Paint.Style.STROKE);
miniPaint.setStrokeWidth(5);
mMinRadius = dp(5);
mSetPoints = new LinkedHashSet<>();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
drawAll_Cicle(canvas);
draw_line(canvas);
}
private int dp(int dp) {
return (int) (dp * mDensity + 0.5f);
}
//繪制 連接線
void draw_line(Canvas canvas) {
Point p1 = null;
Point p2 = null;
//從結(jié)合中獲取 move過的點(diǎn)
for (int index : mSetPoints) {
//當(dāng)?shù)谝粋€(gè)點(diǎn)為null的時(shí)候 給第一個(gè)點(diǎn)賦值
if (p1 == null) {
p1 = mListCircle.get(index);
//然后 給第二個(gè)點(diǎn)賦值 兩點(diǎn)一條線
} else if (p2 == null) {
p2 = mListCircle.get(index);
canvas.drawLine(p1.x, p1.y, p2.x, p2.y, mPaint);
//p1 挪到p2
p1 = p2;
//接下來(lái)就是第三個(gè)點(diǎn)了 乃至更多點(diǎn)
} else {
//p2重新賦值 兩點(diǎn)一條線
p2 = mListCircle.get(index);
canvas.drawLine(p1.x, p1.y, p2.x, p2.y, mPaint);
//p1 挪到p2
p1 = p2;
}
}
//繪制實(shí)時(shí)連線
if (mMovePoint != null && p1 != null) {
canvas.drawLine(p1.x, p1.y, mMovePoint.x, mMovePoint.y, mPaint);
}
}
//繪制 外圓 內(nèi)圓
void drawAll_Cicle(Canvas canvas) {
for (int i = 0; i < mListRectFs.size(); i++) {
Point point = mListCircle.get(i);
//如果move過的 就換顏色
if (mSetPoints.contains(i)) {
canvas.drawCircle(point.x, point.y, mRadius, miniPaint);
canvas.drawCircle(point.x, point.y, mMinRadius, mPaint);
} else {
canvas.drawCircle(point.x, point.y, mRadius, mPaint);
canvas.drawCircle(point.x, point.y, mMinRadius, miniPaint);
}
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
float w = Math.min(getMeasuredWidth(), getMeasuredHeight());
// -(--)-(--)-(--)-
// -(--)-(--)-(--)-
// -(--)-(--)-(--)-
mRadius = getMeasuredWidth() * 1.00f / (mCount * 3 + 1);
float rectWH = mRadius * 2;
mListRectFs.clear();
mListCircle.clear();
for (int i = 0; i < mCount; i++) {
for (int j = 0; j < mCount; j++) {
RectF rectF = new RectF((3 * i + 1) * mRadius, (3 * j + 1) * mRadius, (3 * i + 3) * mRadius, (3 * j + 3) * mRadius);
mListRectFs.add(rectF);
Point point = new Point((3 * i + 2) * (int) mRadius, (3 * j + 2) * (int) mRadius);
mListCircle.add(point);
}
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
float point_x = event.getX();
float point_y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_UP:
mMovePoint = null;
mSetPoints.clear();
invalidate();
break;
case MotionEvent.ACTION_MOVE:
int index = touchIndex(point_x, point_y);
if (mMovePoint == null) {
mMovePoint = new Point((int) point_x, (int) point_y);
} else {
mMovePoint.set((int) point_x, (int) point_y);
}
if (index != -1) {
mSetPoints.add(index);
if (index_point != mSetPoints.size()) {
index_point = mSetPoints.size();
performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
}
}
invalidate();
break;
}
return true;
}
/**
* 判斷觸摸點(diǎn)在哪個(gè)item上
*
* @param x
* @param y
* @return
*/
private int touchIndex(float x, float y) {
for (int i = 0; i < mListCircle.size(); i++) {
Point p = mListCircle.get(i);
if (isCollision(x, y, p.x, p.y, mRadius)) {
return i;
}
}
return -1;
}
/**
* 點(diǎn)和圓形碰撞檢測(cè)
*
* @param x1 手指接觸點(diǎn)
* @param y1 手指接觸點(diǎn)
* @param x2 外圓
* @param y2 外圓
* @param radius 半徑
* @return
*/
private boolean isCollision(float x1, float y1, float x2, float y2, float radius) {
if (Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2)) <= radius) {
// 如果點(diǎn)和圓心距離小于或等于半徑則認(rèn)為發(fā)生碰撞
return true;
}
return false;
}
}
下一篇會(huì)把把應(yīng)用鎖和這個(gè)結(jié)合起來(lái),就是appLock功能了。敬請(qǐng)期待···
以上就是本文的全部?jī)?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)滑動(dòng)解鎖九宮格
- Android 仿小米鎖屏實(shí)現(xiàn)九宮格解鎖功能(無(wú)需圖片資源)
- Android自定義控件實(shí)現(xiàn)九宮格解鎖功能
- Android自定義View九宮格手勢(shì)密碼解鎖
- Android實(shí)現(xiàn)九宮格手勢(shì)解鎖
- Android自定義控件實(shí)現(xiàn)九宮格解鎖
相關(guān)文章
android 實(shí)現(xiàn)控件左右或上下抖動(dòng)教程
這篇文章主要介紹了android 實(shí)現(xiàn)控件左右或上下抖動(dòng)教程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來(lái)看看吧2020-03-03
微信小程序 canvas開發(fā)實(shí)例及注意事項(xiàng)
這篇文章主要介紹了微信小程序 wxcanvas開發(fā)實(shí)例及注意事項(xiàng)的相關(guān)資料,這里對(duì)微信canvas與H5中的canvas做對(duì)比,并說(shuō)明注意事項(xiàng),需要的朋友可以參考下2016-12-12
Android判斷屏幕是橫屏或是豎屏的簡(jiǎn)單實(shí)現(xiàn)方法
這篇文章主要介紹了Android判斷屏幕是橫屏或是豎屏的簡(jiǎn)單實(shí)現(xiàn)方法,是Android應(yīng)用開發(fā)中常用的功能,需要的朋友可以參考下2014-07-07
Android添加圖片到ListView或者RecyclerView顯示
這篇文章主要介紹了Android添加圖片到ListView或者RecyclerView顯示的相關(guān)資料,需要的朋友可以參考下2016-08-08
Android?Studio實(shí)現(xiàn)簡(jiǎn)單補(bǔ)間動(dòng)畫
這篇文章主要為大家詳細(xì)介紹了Android?Studio實(shí)現(xiàn)簡(jiǎn)單補(bǔ)間動(dòng)畫,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-07-07
基于Android CALL && SendMes Test的相關(guān)介紹
本篇文章小編為大家介紹,Android CALL && SendMes Test 需要的朋友參考下2013-04-04

