編輯:關於Android編程
周末閒著沒事,寫了個手勢解鎖的view,實現起來也蠻快的,半天多一點時間就完事。把源碼和資源貼出來,給大家分享,希望對大家有用。
效果,就跟手機上的九點手勢解鎖一樣,上個圖吧:
過程嘛感覺確實沒啥好講的了,涉及的知識以前的博客都說過了,無非就是canva,paint,touch事件這些,畫畫圓圈畫畫線條,剩下的就是細節處理邏輯了。都在代碼裡,所以這裡就主要是貼資源吧。
這個自定義view就一個類,源碼如下:
package com.cc.library.view; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Path; import android.location.Location; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.view.ViewTreeObserver; /** * 圖案解鎖view * Created by zhangyu on 2016-07-15 15:05. */ public class UnlockView extends View { private static final String TAG = "UnlockView"; //view寬高 private float width, height; //平均寬高(分三份) private float averageWidth, averageHeight; //九個點的位置數據,從左到右、從上到下 123...789 Location[] locations = new Location[9]; //圓圈半徑 private float radius; //繪制密碼 private int[] drawingPwd = new int[9]; //正確的密碼 private int[] rightPwd; //畫筆 private Paint whitePaint, cyanPaint; //已經繪制過了的點個數 private int drawedNumber; //當前正被觸摸的點 private Location nowTouchedPosition = new Location(); //監聽 private UnlockListener unlockListener; public void setUnlockListener(UnlockListener unlockListener) { this.unlockListener = unlockListener; } public UnlockView(Context context) { super(context); init(); } public UnlockView(Context context, AttributeSet attrs) { super(context, attrs); init(); } public UnlockView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init() { ViewTreeObserver vto = getViewTreeObserver(); vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { height = getHeight(); width = getWidth(); Log.d(TAG, "width = " + width + " ,height = " + height); averageWidth = width / 3f; averageHeight = height / 3f; radius = averageHeight > averageWidth ? averageWidth / 5f : averageHeight / 5f; initLocation(); invalidate(); } }); whitePaint = new Paint(); whitePaint.setAntiAlias(true); whitePaint.setColor(Color.parseColor("#ffffff")); whitePaint.setStyle(Paint.Style.STROKE); cyanPaint = new Paint(); cyanPaint.setAntiAlias(true); cyanPaint.setColor(Color.parseColor("#4169E1")); cyanPaint.setStyle(Paint.Style.STROKE); initDrawingPwd(); } private void drawStart() { drawedNumber = 0; } private void drawOver() { //debug StringBuffer sb = new StringBuffer(); for (int i = 0; i < drawingPwd.length; i++) { sb.append(drawingPwd[i] + ","); } Log.i(TAG, "drawingPwd:" + sb.toString()); initLocation(); initDrawingPwd(); drawedNumber = 0; invalidate(); } /** * 初始化繪制密碼 */ private void initDrawingPwd() { for (int i = 0; i < 9; i++) { drawingPwd[i] = -1; } } /** * 初始化九個點坐標 */ private void initLocation() { for (int i = 0; i < 9; i++) { locations[i] = new Location(); locations[i].deawed = false; //縱向1、2、3列x坐標 if (i % 3 == 0) { locations[i].x = averageWidth * 0.5f; } else if (i % 3 == 1) { locations[i].x = averageWidth * 1.5f; } else if (i % 3 == 2) { locations[i].x = averageWidth * 2.5f; } //橫向1、2、3排y坐標 if (i / 3 == 0) { locations[i].y = averageHeight * 0.5f; } else if (i / 3 == 1) { locations[i].y = averageHeight * 1.5f; } else if (i / 3 == 2) { locations[i].y = averageHeight * 2.5f; } } } @Override protected void onDraw(Canvas canvas) { for (int i = 0; i < 9; i++) { if (!locations[i].deawed) {//沒被畫 whitePaint.setStrokeWidth(4); canvas.drawPoint(locations[i].x, locations[i].y, whitePaint); whitePaint.setStrokeWidth(1.5f); canvas.drawCircle(locations[i].x, locations[i].y, radius, whitePaint); } else {//被畫過了 cyanPaint.setStrokeWidth(8); canvas.drawPoint(locations[i].x, locations[i].y, cyanPaint); cyanPaint.setStrokeWidth(3f); canvas.drawCircle(locations[i].x, locations[i].y, radius, cyanPaint); } int lastestDrawedPoint = -1; if (drawedNumber > 0) lastestDrawedPoint = drawingPwd[drawedNumber - 1]; if (lastestDrawedPoint != -1) { Location lastestDrawedLocation = locations[lastestDrawedPoint];//最新一個被選中的點 cyanPaint.setStrokeWidth(3f); canvas.drawLine(lastestDrawedLocation.x, lastestDrawedLocation.y, nowTouchedPosition.x, nowTouchedPosition.y, cyanPaint); } if (drawedNumber > 1) { for (int j = 0; j < drawedNumber - 1; j++) { cyanPaint.setStrokeWidth(3f); canvas.drawLine(locations[drawingPwd[j]].x, locations[drawingPwd[j]].y, locations[drawingPwd[j + 1]].x, locations[drawingPwd[j + 1]].y, cyanPaint); } } } super.onDraw(canvas); } float moveX, moveY; @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: drawStart(); break; case MotionEvent.ACTION_MOVE: moveX = event.getX(); moveY = event.getY(); dealPosition(moveX, moveY); break; case MotionEvent.ACTION_UP: if (unlockListener != null) { unlockListener.drawOver(drawingPwd); if (verificationPwd(rightPwd)) { unlockListener.isPwdRight(true); } else { unlockListener.isPwdRight(false); } } drawOver(); break; } return true; } private void dealPosition(float nowX, float nowY) { nowTouchedPosition.x = nowX; nowTouchedPosition.y = nowY; int nowTouched = getWhichOneBeTouched(nowX, nowY); if (nowTouched != -1) {//觸摸到了點上 if (!locations[nowTouched].deawed) {//如果這點沒被觸摸過 drawingPwd[drawedNumber] = nowTouched; //記錄密碼 drawedNumber++; //被觸摸點數+1 Log.v(TAG, "nowTouched " + nowTouched + " ,drawedNumber = " + drawedNumber); } locations[nowTouched].deawed = true; } invalidate(); } private class Location { public float x = -1, y = -1; public boolean deawed;//是否被畫過了 } /** * 獲取被觸摸到的點 * * @param x 坐標x點 * @param y 坐標y點 * @return 被觸摸的坐標點位置 或者-1 */ private int getWhichOneBeTouched(float x, float y) { for (int i = 0; i < locations.length; i++) { double lPowX = Math.pow(Math.abs(x - locations[i].x), 2); double lPowY = Math.pow(Math.abs(y - locations[i].y), 2); if (Math.sqrt(lPowX + lPowY) < radius) return i; } return -1; } /** * 校驗密碼是否正確 * * @param rightPwd 正確的密碼 * @return 正確返回true 否則返回false */ public boolean verificationPwd(int[] rightPwd) { if (rightPwd == null) return false; for (int i = 0; i < rightPwd.length; i++) { if (rightPwd[i] != drawingPwd[i]) return false; } return true; } /** * 獲取當前繪制的密碼 * * @return */ public int[] getDrawedPwd() { return drawingPwd; } /** * 設置正確的密碼 * * @param rightPwd */ public void setRightPwd(int[] rightPwd) { this.rightPwd = rightPwd; } //監聽接口 public interface UnlockListener { public void drawOver(int[] pwd); public void isPwdRight(boolean isRight); } }布局(view的寬高你可以隨意指定,bg1是效果裡那張背景圖片):
package com.sz.china.testmoudule; import android.app.Activity; import android.os.Bundle; import android.widget.Toast; import com.cc.library.view.UnlockView; /** * Created by zhangyu on 2016-07-15 14:55. */ public class TestUnlockViewActivity extends Activity { private UnlockView unlockView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.unlock_activity); unlockView = (UnlockView) findViewById(R.id.unlock_view); //設置回調監聽 unlockView.setUnlockListener(new UnlockView.UnlockListener() { @Override public void drawOver(int[] pwd) {//繪制完成,獲取繪制的密碼 unlockView.getDrawedPwd(); } @Override public void isPwdRight(boolean isRight) {//密碼是否校驗正確 if(isRight) Toast.makeText(TestUnlockViewActivity.this,"密碼正確",Toast.LENGTH_SHORT).show(); else Toast.makeText(TestUnlockViewActivity.this,"密碼錯誤",Toast.LENGTH_SHORT).show(); } }); int[] pwd = {0,5,7,6}; unlockView.setRightPwd(pwd); //設置密碼 } }
菜單的概念,現在已經很普及了。 Windows系統、Mac、桌面版Linux、Java Swing等,都有可視化菜單。 一、Android平台3種菜單 選項菜單(Op
首先來說一下具體的需求是什麼樣的:需求如圖所示,這裡面有ABCD四個選項的題目,當點擊A選項,如果A是正確的答案,則變成對勾的圖案,如果是錯誤答案,則變成錯誤的圖案,這裡
“點九”圖片概述 “點九”是andriod平台的應用軟件開發裡的一種特殊的圖片形式,文件擴展名為:.9.png。 我們都知道android平台有多種不同的分辨
一、為什麼Android要進行分辨率與屏幕適配最大的原因是碎片化,因為Android的開源措施和各個廠商的自己細微修改,結果就變成了這個樣需要適配的屏幕尺寸就有這麼多:這