連線9宮格可實現手勢密碼等

 

最近一直想寫一片視圖的繪製過程,一張圖,從測量佈局到繪製,真的會的人也不用看,不會的人吧看完也不懂..尷尬。

這幾天項目用到手勢密碼,網上下載了改了改。發現晚上很多都是自己從頭畫到底,很多帶的功能不少,但是實際開發中就很尷尬。自己有想到了個實現方法,閒來沒事搞了下,感覺可行分享出來。

這個使用的時候必須注意要給LinGesturePassword設置上背景,否則不會調用的的onDraw,主要是父類組件的一個特性,父類容器是用來發子類的所以沒背景是不會調用的的onDraw。

這裏要使用的時候添加的子類必須實現LinGesturePassword.ZhuangTai接口的三個方法,這樣滑動纔會改變,這裏爲了方便英語學渣的我直接沒用使用有道查詢,有強迫的人可以修改。這裏我還主要標記了一下onLayout方法,這是父類的一個佈局方法,有的時候是很方便的,自定義視圖不自己寫也要理解,不然怎麼魔改別人的代碼。

public class LinGesturePassword extends LinearLayout {
    float xy[] = new float[2];
    List<float[]> list = new ArrayList<>();
    List<Integer> lintp = new ArrayList<>();//保存密碼id
    WanChengHuiDiao wanChengHuiDiao;
    private int lineColor = 0xFF378FC9;
    private int lineColorCuo = 0xFF378FC9;
    private int linStrokeWidth=10;
    private boolean isTouch = true;
    private boolean cuowu=false;

    public LinGesturePassword(Context context) {
        super(context);
    }

    public LinGesturePassword(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public LinGesturePassword(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public LinGesturePassword(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }


    @Override
    public void onDraw(Canvas canvas) {
        Log.e("aaa", "運行拉啦啦啦");
        super.onDraw(canvas);
        drawAllLine(canvas);

    }


    /**
     * 關閉手勢密碼
     * @param touch
     */
    public void setTouch(boolean touch) {
        isTouch = touch;
    }

    public void setWanChengHuiDiao(WanChengHuiDiao wanChengHuiDiao) {
        this.wanChengHuiDiao = wanChengHuiDiao;
    }

    /**
     * 線條顏色
     * @param lineColor
     */

    public void setLineColor(int lineColor) {
        this.lineColor = lineColor;
    }

    /**
     * 選擇錯誤後的線條顏色
     * @param lineColorCuo
     */
    public void setLineColorCuo(int lineColorCuo) {
        this.lineColorCuo = lineColorCuo;
    }

    /**
     * 設置線條粗細
     * @param linStrokeWidth
     */
    public void setLinStrokeWidth(int linStrokeWidth) {
        this.linStrokeWidth = linStrokeWidth;
    }

    private void drawAllLine(Canvas canvas) {
        Paint mPaint = new Paint();
        canvas.setDrawFilter(new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG));
        mPaint.setAntiAlias(true);
        mPaint.setFilterBitmap(true);
        if(cuowu) {
            mPaint.setColor(lineColorCuo);
        }else {
            mPaint.setColor(lineColor);
        }
        mPaint.setStrokeWidth(linStrokeWidth);
        for (int i = 0; i < list.size(); i++) {
            if (i == list.size() - 1) {
                canvas.drawLine(list.get(i)[0], list.get(i)[1], xy[0], xy[1], mPaint);
            } else {
                canvas.drawLine(list.get(i)[0], list.get(i)[1], list.get(i + 1)[0], list.get(i + 1)[1], mPaint);
            }

        }

    }


    /**
     * 控制子控件的換行
     */
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        int cellWidth = 0;//寬度偏差
        int columns = 0;//保存每排放的第幾個view
        int y = 0;      //高度偏移量
        int maxy = 0;   //最大高度
        int count = getChildCount();//子view數量
        int gw = getMeasuredWidth() / 3;//獲取寬度1/3這裏主要是我一排放三個,看需求可以改成變量
        for (int j = 0; j < count; j++) {
            final View childView = getChildAt(j);
            // 獲取子控件Child的寬高
            int w = childView.getMeasuredWidth();
            int h = childView.getMeasuredHeight();
            // 計算子控件的頂點座標
            int left = (gw - w) / 2 + cellWidth;
            cellWidth += gw;//下一一個位置
            int top = y + (gw - w) / 2;
            // 佈局子控件
            maxy = Math.max(gw, maxy);
            columns++;
            childView.layout(left, top, left + w, top + h);
            //判斷一排是否到三個了,到三個換行
            if (columns >= 3) {
                cellWidth = 0;
                y += maxy;
                maxy = 0;
                columns = 0;
            }

        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        float[] fxy;
        if (isTouch) {
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:

                    chongZhi();
                    cuowu=false;
                    fxy = jx(event.getX(), event.getY());
                    if (fxy != null) {
                        list.add(fxy);
                        xy[0] = event.getX();
                        xy[1] = event.getY();
                    }
                    break;
                case MotionEvent.ACTION_MOVE:
                    fxy = jx(event.getX(), event.getY());
                    if (fxy != null) {
                        list.add(fxy);
                    }
                    xy[0] = event.getX();
                    xy[1] = event.getY();
                    break;
                case MotionEvent.ACTION_UP:
                    if (wanChengHuiDiao == null) {
                        list.clear();
                        lintp.clear();
                    } else {
                        wanChengHuiDiao.wanCheng(lintp);
                    }
                    break;
            }
            postInvalidate();
            return true;
        } else {
            return false;
        }
    }

    /**
     * 判斷滑動的點是否和子類交叉
     * @param x
     * @param y
     * @return
     */

    public float[] jx(float x, float y) {
        int count = getChildCount();
        for (int j = 0; j < count; j++) {
            View childView = getChildAt(j);
            boolean t = true;
            for (int i = 0; i < lintp.size(); i++) {
                if (lintp.get(i) == j) {
                    t = false;
                    break;
                }
            }
            if (t) {
                if (childView.getX() < x && childView.getX() + childView.getWidth() > x) {
                    if (childView.getY() < y && childView.getY() + childView.getHeight() > y) {
                        lintp.add(j);
                        if (childView instanceof ZhuangTai) {
                            ((ZhuangTai) childView).xuanZhong();
                        }
                        return new float[]{childView.getX() + childView.getWidth() / 2, childView.getY() + childView.getHeight() / 2};
                    }
                }
            }
        }
        return null;
    }

    /**
     * 重置界面
     */
    public void chongZhi() {
        int count = getChildCount();
        for (int j = 0; j < count; j++) {
            View childView = getChildAt(j);
            if (childView instanceof ZhuangTai) {
                ((ZhuangTai) childView).weiXuanZhong();
            }
        }
        list.clear();
        lintp.clear();
        postInvalidate();

    }

    /**
     * 密碼錯誤
     */
    public void  cuoWuJieMina(){
        cuowu=true;
        int count = getChildCount();
        for (int j = 0; j < count; j++) {
            View childView = getChildAt(j);
            if (childView instanceof ZhuangTai) {
                ((ZhuangTai) childView).cuoWu();
            }
        }
        postInvalidate();
    }

    public interface ZhuangTai {
        void xuanZhong();

        void cuoWu();

        void weiXuanZhong();
    }

    public interface WanChengHuiDiao {
        void wanCheng(List<Integer> list);
    }
}

 

隨便實現的一個子類,正常用的ImageView的也不錯看需求了,現在這個實現很難看,你可以找設計設置幾張圖完事,項目在接口放裏面自己畫也可,感覺還是跟要UI圖方便......

public class Textvvvv extends TextView implements LinGesturePassword.ZhuangTai {
    public Textvvvv(Context context) {
        super(context);
    }

    public Textvvvv(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public Textvvvv(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public Textvvvv(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }


    @Override
    public void xuanZhong() {
        setText("選中了");
        setBackgroundColor(0xFF378FC9);
    }

    @Override
    public void cuoWu() {
        setText("選擇錯誤");
        setBackgroundColor(0xFFff0000);
    }

    @Override
    public void weiXuanZhong() {
        setText("未選中");
        setBackgroundColor(0xFF3ab826);
    }
}

佈局文件隨便加就行自動佈局,但是要設置高度DP的話基本不會出現什麼大的佈局問題。這個視圖的是現實根據父視圖大小來實現平均的,可以改變LinGesturePassword的寬度設置居中然後會還看點,fLinGesturePassword的寬度必須大於三個的TextView的總寬度,不然就沒間距了或者重疊

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <com.ag.text.myapplication.LinGesturePassword
        android:id="@+id/lin1"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#fff"
        android:layout_margin="10dp"
        android:orientation="vertical">

        <com.ag.text.myapplication.Textvvvv
            android:layout_width="80dp"
            android:layout_height="80dp"
            android:background="#FF3ab826"
            android:text="Hello World!"
            android:layout_marginTop="50dp"

            />
        <com.ag.text.myapplication.Textvvvv
            android:layout_width="80dp"
            android:layout_height="80dp"
            android:background="#FF3ab826"
            android:text="Hello World!"
            android:layout_marginTop="50dp"

            />
        <com.ag.text.myapplication.Textvvvv
            android:layout_width="80dp"
            android:layout_height="80dp"
            android:background="#FF3ab826"
            android:text="Hello World!"
            android:layout_marginTop="50dp"

            />
        <com.ag.text.myapplication.Textvvvv
            android:layout_width="80dp"
            android:layout_height="80dp"
            android:background="#FF3ab826"
            android:text="Hello World!"
            android:layout_marginTop="50dp"

            />
        <com.ag.text.myapplication.Textvvvv
            android:layout_width="80dp"
            android:layout_height="80dp"
            android:background="#FF3ab826"
            android:text="Hello World!"
            android:layout_marginTop="50dp"

            />
        <com.ag.text.myapplication.Textvvvv
            android:layout_width="80dp"
            android:layout_height="80dp"
            android:background="#FF3ab826"
            android:text="Hello World!"
            android:layout_marginTop="50dp"

            />
        <com.ag.text.myapplication.Textvvvv
            android:layout_width="80dp"
            android:layout_height="80dp"
            android:background="#FF3ab826"
            android:text="Hello World!"
            android:layout_marginTop="50dp"

            />
        <com.ag.text.myapplication.Textvvvv
            android:layout_width="80dp"
            android:layout_height="80dp"
            android:background="#FF3ab826"
            android:text="Hello World!"
            android:layout_marginTop="50dp"

            />
        <com.ag.text.myapplication.Textvvvv
            android:layout_width="80dp"
            android:layout_height="80dp"
            android:background="#FF3ab826"
            android:text="Hello World!"
            android:layout_marginTop="50dp"

            />
        <com.ag.text.myapplication.Textvvvv
            android:layout_width="80dp"
            android:layout_height="80dp"
            android:background="#FF3ab826"
            android:text="Hello World!"
            android:layout_marginTop="50dp"

            />

    </com.ag.text.myapplication.LinGesturePassword>

</LinearLayout>

使用

public class MainActivity extends AppCompatActivity {

    LinGesturePassword lin1;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        lin1=findViewById(R.id.lin1);
//        lin1.setLineColor();//線條顏色
//        lin1.setLineColorCuo();//錯誤線條顏色
        lin1.setLinStrokeWidth(20);//線條粗度
        lin1.setWanChengHuiDiao(new LinGesturePassword.WanChengHuiDiao() {
            @Override
            public void wanCheng(List<Integer> list) {
                String pw="";
                for (int i = 0; i < list.size(); i++) {
                    pw+=list.get(i);
                }
                Log.e("aaa","密碼"+pw);
//                lin1.cuoWuJieMina();//密碼錯誤是調用
//                lin1.chongZhi();//重置界面
            }
        });

    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.e("main","main"+event.getAction());
        return super.onTouchEvent(event);
    }

gitee可能會跟新呵呵~~

https://gitee.com/axshuai/hand_gesture_unlock代碼地址 

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