【Android】GesturePassword

本文僅是個人學習筆記,不喜勿噴!更多更好的開源框架可以移步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;
            }
        });

    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章