編輯:關於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); //設置密碼
}
}
Android開發之Menu:OptionMenu(選項菜單)、ContextMenu(上下文菜單)、SubMenu(子菜單)
菜單的概念,現在已經很普及了。 Windows系統、Mac、桌面版Linux、Java Swing等,都有可視化菜單。 一、Android平台3種菜單 選項菜單(Op
探究Android中ListView復用導致布局錯亂的解決方案
首先來說一下具體的需求是什麼樣的:需求如圖所示,這裡面有ABCD四個選項的題目,當點擊A選項,如果A是正確的答案,則變成對勾的圖案,如果是錯誤答案,則變成錯誤的圖案,這裡
詳解Android .9.png “點九”圖片的使用
“點九”圖片概述 “點九”是andriod平台的應用軟件開發裡的一種特殊的圖片形式,文件擴展名為:.9.png。 我們都知道android平台有多種不同的分辨
Android的分辨率和屏幕適配詳解
一、為什麼Android要進行分辨率與屏幕適配最大的原因是碎片化,因為Android的開源措施和各個廠商的自己細微修改,結果就變成了這個樣需要適配的屏幕尺寸就有這麼多:這