本文僅是個人學習筆記,不喜勿噴!更多更好的開源框架可以移步Github!
跳轉至:https://github.com/
實現思路
不論什麼功能,不要總想着一步實現,要分解功能,一步一步實現,哪裏遇到問題再解決問題,當解決完所有的問題時,功能也就實現完了。
- 自定義View繼承自ViewGroup
- 實現宮格佈局
- 實現不同狀態顯示不同圖片
- 處理點擊切換狀態
- 處理觸摸滑動時,根據point與rect的包含關係改變狀態
- 重寫onDraw()處理觸摸滑動時,再相鄰的密碼點中心點之間劃線
- 畫最後一個密碼點與當前手指的線,滑動時顯示,擡起時隱藏
- 擡起手指時,重置狀態
- 修修補補,處理小BUG.
GesturePasswordView
import android.com.touchdemo.R;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.ViewGroup;
import android.widget.ImageView;
import java.util.ArrayList;
import java.util.List;
/**
* 手勢密碼
* Created by Yhyu on 2017/2/20.
*/
public class GesturePasswordView extends ViewGroup {
public static final String TAG = GesturePasswordView.class.getSimpleName();
//密碼數量
public static final int count = 9;
//密碼行數
public static final int column = 3;
// 間距
public static final int margin = 30;
// 密碼子view
private List<ImageView> views = new ArrayList<>();
// 已選中的子view
private List<ImageView> selectedViews = new ArrayList<>();
// 劃線
private Paint paint;
private int currentX;
private int currentY;
private Rect btnRect = new Rect();
// 是否可觸摸
private boolean canTouched=true;
private IPasswordListener listener;
public GesturePasswordView(Context context) {
super(context);
init(context);
}
public GesturePasswordView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public GesturePasswordView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
private void init(Context context) {
for (int i = 0; i < count; i++) {
ImageView imageView = new ImageView(context);
imageView.setTag(i + 1);
imageView.setImageResource(R.drawable.btn_selector);
addView(imageView);
views.add(imageView);
}
// 畫筆樣式
paint = new Paint();
paint.setColor(Color.GRAY);
paint.setStrokeWidth(30);
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setStrokeCap(Paint.Cap.ROUND);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (selectedViews.size() == 0)
return;
// 劃線
for (int i = 1; i < selectedViews.size() + 1; i++) {
selectedViews.get(i - 1).getHitRect(btnRect);
int startX = btnRect.centerX();
int startY = btnRect.centerY();
int endX = 0;
int endY = 0;
if (i == selectedViews.size()) {
//最後一跟線,是選中view中心點到手指位置
endX = currentX;
endY = currentY;
} else {
selectedViews.get(i).getHitRect(btnRect);
endX = btnRect.centerX();
endY = btnRect.centerY();
}
canvas.drawLine(startX, startY, endX, endY, paint);
}
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
Log.d(TAG, "onLayout: ");
int w = ((r - l) - margin * (column+1)) / column;
int h = w;
/*九宮格佈局*/
for (int i = 0; i < views.size(); i++) {
ImageView imageView = views.get(i);
int bl = margin + (margin + w) * (i % column);
int bt = margin + (margin + h) * (i / column);
imageView.layout(bl, bt, bl + w, bt + h);
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (!canTouched){
return true;
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
selectView(event);
invalidate();
break;
case MotionEvent.ACTION_UP:
onTouchUp();
break;
}
return true;
}
/**
* 手指擡起
*
*/
private void onTouchUp() {
//去掉最後一個view中心點和手指之間線
if (selectedViews.size()==0)
return;
selectedViews.get(selectedViews.size() - 1).getHitRect(btnRect);
currentX = btnRect.centerX();
currentY = btnRect.centerY();
invalidate();
StringBuilder pwd = new StringBuilder();
for (ImageView imageView : selectedViews) {
pwd.append(imageView.getTag());
}
//回調密碼
if (listener != null) {
boolean result = listener.onTouchEnd(pwd.toString());
if (!result) {
for (ImageView imageView : selectedViews) {
imageView.setEnabled(false);
}
}
}
//延時重置
postDelayed(new Runnable() {
@Override
public void run() {
reset();
}
}, 1500);
canTouched=false;
//
}
/**
* 根據手指位置,選中view
*
* @param event
*/
private void selectView(MotionEvent event) {
//當前手指點
currentX = (int) event.getX();
currentY = (int) event.getY();
for (ImageView imageView : views) {
//判斷點落在哪個子view的rect中
imageView.getHitRect(btnRect);
if (btnRect.contains(currentX, currentY)) {
imageView.setSelected(true);
if (!selectedViews.contains(imageView)) {
selectedViews.add(imageView);
}
break;
}
}
}
/*
重置當前view
*/
private void reset() {
for (ImageView imageView : views) {
imageView.setSelected(false);
imageView.setEnabled(true);
}
canTouched=true;
selectedViews.clear();
invalidate();
}
/**
* 設置監聽
* @param listener
*/
public void setIPasswordListener(IPasswordListener listener) {
this.listener = listener;
}
/**
* 結束監聽
*/
public interface IPasswordListener {
boolean onTouchEnd(String password);
}
}
xml使用
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@mipmap/home_refresh_bg"
android:orientation="vertical">
<ImageView
android:id="@+id/last_pwd_iv"
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_centerHorizontal="true"
android:layout_marginTop="@dimen/activity_vertical_margin" />
<android.com.touchdemo.touch.GesturePasswordView
android:background="@android:color/transparent"
android:id="@+id/genture_pwd_view"
android:layout_width="300dp"
android:layout_height="300dp"
android:layout_centerInParent="true" />
</RelativeLayout>
Activity中使用
public static final String TAG = MainActivity.class.getSimpleName();
private GesturePasswordView genturepwdview;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_gesture_password);
this.genturepwdview = (GesturePasswordView) findViewById(R.id.genture_pwd_view);
final String pwd = "123";
genturepwdview.setIPasswordListener(new GesturePasswordView.IPasswordListener() {
@Override
public boolean onTouchEnd(String password) {
//判斷密碼邏輯
boolean result = pwd.equals(password);
if (result){
Toast.makeText(GesturePasswordActivity.this,"密碼正確",Toast.LENGTH_SHORT).show();
}else {
Toast.makeText(GesturePasswordActivity.this,"密碼錯誤",Toast.LENGTH_SHORT).show();
}
return result;
}
});
}