本文仅是个人学习笔记,不喜勿喷!更多更好的开源框架可以移步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;
}
});
}