Android實現(xiàn)九宮格解鎖的實例代碼
當(dāng)年感覺九宮格解鎖很是高大上,一臉懵逼,今天正好要做解鎖這一塊業(yè)務(wù),回頭來看九宮格,這特么簡單啊
首先理清一下邏輯,我們要做NxN的九宮格 下圖是3x3的簡單圖例
// -(--)-(--)-(--)-
// -(--)-(--)-(--)-
// -(--)-(--)-(--)-
我們就把九宮格分解成
外圓 、內(nèi)圓、連線三部分
外圓半徑Radius,內(nèi)圓半徑dp(5)
建立一個集合來放置 外圓的圓心( 內(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)畫完了,接下來就是如何繪制點與點之間的連線了,有人會覺得沒思路,其實很簡單了,以3X3 為例子哈
我們可以給這九個棋子編號1--9號,把他存入LinkedHashSet中,著重介紹這個LinkedHashSet有順序不重復(fù)這個真的在合適不過了。
這樣就不會有重復(fù)的事情了。這個和解鎖時候的密碼也很契合。所以選對了存儲方式會事半功倍。
還有一個問題,就是點擊邊界問題,這個好解決,我們把每一個棋子都花矩形,通過圓來控制邊界,(其實也可以通過矩形來控制邊界,這個也很簡單,原理是差不多的,有興趣的同學(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); } }
好邊界也有了,我們來計算邊界返回編號
/** * 點和圓形碰撞檢測 * * @param x1 手指接觸點 * @param y1 手指接觸點 * @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) { // 如果點和圓心距離小于或等于半徑則認為發(fā)生碰撞 return true; } return false; } /** * 判斷觸摸點在哪個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; }
幾個關(guān)鍵點都寫到了接下來就是具體的細節(jié)了。我把代碼都貼上來,注釋的很詳細。當(dāng)然加入了一個手指觸控點,更加好看一些。
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;//記錄需要連線的外圓圓心點在mListCircle中的索引值,LinkedHashSet線性不可重復(fù) 集合,F(xiàn)IFO private Paint mPaint, miniPaint;//畫筆 private float mRadius;//外圓半徑 private float mMinRadius;//內(nèi)圓半徑 private float mStrokeWidth = 10; //繪制時的畫筆寬度 private Point mMovePoint; //記錄手指移動時的點 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過的點 for (int index : mSetPoints) { //當(dāng)?shù)谝粋€點為null的時候 給第一個點賦值 if (p1 == null) { p1 = mListCircle.get(index); //然后 給第二個點賦值 兩點一條線 } else if (p2 == null) { p2 = mListCircle.get(index); canvas.drawLine(p1.x, p1.y, p2.x, p2.y, mPaint); //p1 挪到p2 p1 = p2; //接下來就是第三個點了 乃至更多點 } else { //p2重新賦值 兩點一條線 p2 = mListCircle.get(index); canvas.drawLine(p1.x, p1.y, p2.x, p2.y, mPaint); //p1 挪到p2 p1 = p2; } } //繪制實時連線 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; } /** * 判斷觸摸點在哪個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; } /** * 點和圓形碰撞檢測 * * @param x1 手指接觸點 * @param y1 手指接觸點 * @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) { // 如果點和圓心距離小于或等于半徑則認為發(fā)生碰撞 return true; } return false; } }
下一篇會把把應(yīng)用鎖和這個結(jié)合起來,就是appLock功能了。敬請期待···
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Android自定義ViewGroup實現(xiàn)側(cè)滑菜單
這篇文章主要為大家詳細介紹了Android如何通過自定義ViewGroup實現(xiàn)側(cè)滑菜單,文中的示例代碼講解詳細,感興趣的小伙伴可以了解一下2023-01-01Android中不同狀態(tài)頁面管理優(yōu)化技巧詳解
在Android中,不管是activity或者fragment,在加載視圖的時候都有可能會出現(xiàn)多種不同的狀態(tài)頁面View,所以本文就來聊聊Android中不同狀態(tài)頁面管理優(yōu)化吧2024-04-04Android使用ViewFlipper實現(xiàn)圖片上下自動輪播的示例代碼
這篇文章主要介紹了Android使用ViewFlipper實現(xiàn)圖片上下自動輪播的示例代碼,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-05-05Android Handler實現(xiàn)閃屏頁倒計時代碼
這篇文章主要介紹了Android Handler實現(xiàn)閃屏頁倒計時代碼,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-08-08Android自定義View實現(xiàn)拖動自動吸邊效果
這篇文章主要為大家詳細介紹了Android自定義View實現(xiàn)拖動自動吸邊效果,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-06-06