【Android】GesturePassword

辛建业
2023-12-01

本文仅是个人学习笔记,不喜勿喷!更多更好的开源框架可以移步Github!

跳转至:https://github.com/

实现思路

不论什么功能,不要总想着一步实现,要分解功能,一步一步实现,哪里遇到问题再解决问题,当解决完所有的问题时,功能也就实现完了。

  1. 自定义View继承自ViewGroup
  2. 实现宫格布局
  3. 实现不同状态显示不同图片
  4. 处理点击切换状态
  5. 处理触摸滑动时,根据point与rect的包含关系改变状态
  6. 重写onDraw()处理触摸滑动时,再相邻的密码点中心点之间划线
  7. 画最后一个密码点与当前手指的线,滑动时显示,抬起时隐藏
  8. 抬起手指时,重置状态
  9. 修修补补,处理小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;
            }
        });

    }
 类似资料: