自定义view学习-创建自己的九宫格解锁view

来源:互联网 发布:淘宝权现在在哪里直播 编辑:程序博客网 时间:2024/04/27 12:30

先上效果图


效果如上,接下来是如何实现,并没有那么快上代码,不看代码不舒服的请迅速下翻。

九宫格解锁还是比较经典的,作为学习自定义view的入门。


对于九宫格解锁,我的实现思路是这样的:

1.先在屏幕上绘制一个正方形,就是上面草图中间的正方形。

2.将这个正方形纵向横向三等分,分为九个小正方形,分别序号1-9…

3.每个小正方形以1/3边长为半径画出圆,这时候我们已经有了基本的样子。

4.接下来是触屏事件的处理,把所有的触屏坐标减去大正方形的坐上点,就是以大正方形起始点为坐标原点。

 private int whichPath(int x,int y){        if(x>startX+width | y>startY+width |x<startX |y<startY){           // Log.i("xy",x+"-"+y);            return 0;        }else {            //以起始点开始计算坐标            int nX=x-startX;            int nY=y-startY;            int hang=(int)nY/tWidth;//在哪行,从零开始            int lie=(int)nX/tWidth;//在哪列,从零开始           // Log.i("nXY",nX+"-"+nY);            int reInt= hang*3+1+lie;            //缩小判断区域            int smallY=(tWidth/6)+hang*tWidth;            int smallX=(tWidth/6)+lie*tWidth;            if (nX>smallX && nY>smallY && nX<(smallX+2*tWidth/3) && nY<(smallY+2*tWidth/3)){                return reInt;            }else{                return 0;            }        }    }//which
我用以上的方法判断坐标所在区域,并且把判断区域缩小范围,留出足够的空间进行斜向连线。

就是原来的范围为每个小正方形,我把他判定范围缩减到草图中一号位圆圈外的黑色方框以内。


5.当触屏移动,坐标改变。调用上面方法判断在哪个区域,如果进入了合法区域就记录在结果集list中。

不管坐标在不在合法区域,需要把list中的最后一个区域的圆心跟当前移动的点连接,画一条直线。

然后循环取出结果集的区域圆心,两两连线,把结果集中的所有圆圈变为红色。

6.最后,绘制解锁图案完成后提供接口回调。


思路看完,下面是主要代码:(为了方便给代码,我省略了attr的操作,可自行扩展)

package com.toxicant.hua.nicepathsunlock;import android.content.Context;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.Point;import android.util.AttributeSet;import android.util.Log;import android.view.MotionEvent;import android.view.View;import java.util.ArrayList;import java.util.List;/** * Created by hua on 2016/1/31. */public class NinePathsView extends View{    private Paint mPaint=new Paint();//画笔    private Paint mRedPaint=new Paint();    private int startX=0;//    private int startY=0;//    private Point touchPoint=new Point(0,0);//当前触控点    private int width=0;//大正方形的边长    private int tWidth=0;//九个小正方形的边长    private int nowP=0;//当前所在区域    private  List<Point> pointList=new ArrayList<>();//存放圆心的list    private volatile List<Integer> resultList=new ArrayList<>();//存放结果的list    private boolean isUnLock=false;    private DrawFinishListener mListener;    //绘制完成的接口    interface DrawFinishListener{        void finish(List<Integer> resultList);    }    public NinePathsView(Context context) {        super(context);    }    public NinePathsView(Context context, AttributeSet attrs) {        super(context, attrs);    }    public NinePathsView(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        //此处获取自定义属性    }    @Override    protected void onDraw(Canvas canvas) {        width=Math.min(getMeasuredHeight(),getMeasuredWidth());//获取正方形区域边长//        Log.i("width",""+width);        //获取起始绘制点        startY=(getMeasuredHeight()-width)/2;        startX=(getMeasuredWidth()-width)/2;        //绘制大正方形        mPaint.setColor(Color.WHITE);        canvas.drawRect(startX, startY, startX + width, startY + width, mPaint);        //设置画笔        mPaint.setColor(Color.BLUE);        mPaint.setStyle(Paint.Style.STROKE);        mPaint.setStrokeWidth(3);        mRedPaint.setColor(Color.RED);        mRedPaint.setStyle(Paint.Style.STROKE);        mRedPaint.setStrokeWidth(3);        //三等分正方形        tWidth=width/3;        pointList.clear();        for(int j=0;j<3;j++){            int tY=startY+j*tWidth+tWidth/2;            for(int k=0;k<3;k++){                int tX=startX+k*tWidth+tWidth/2;                canvas.drawCircle(tX, tY, 2, mPaint);                //此处已连接的点要变色                canvas.drawCircle(tX, tY, tWidth / 3, mPaint);                pointList.add(new Point(tX,tY));//圆心存入list            }        }        //连线已连接的点        for (int i=1;i<resultList.size();i++){            int back =resultList.get(i-1);            Point backP=pointList.get(back-1);//上一个圆心            int now=resultList.get(i);            Point nowP=pointList.get(now-1);            canvas.drawLine(backP.x,backP.y,nowP.x,nowP.y,mRedPaint);        }        for (int i:resultList){            Point nowP=pointList.get(i-1);            canvas.drawCircle(nowP.x,nowP.y,tWidth / 3,mRedPaint);        }        //如果正在连线,把最后一个圆心点和现在触控的点连接        if(isUnLock){            int last=resultList.get(resultList.size()-1);            Point lastP=pointList.get(last-1);            canvas.drawLine(lastP.x,lastP.y,touchPoint.x,touchPoint.y,mRedPaint);        }    }//draw    @Override    public boolean onTouchEvent(MotionEvent event) {        int x= (int) event.getRawX();        int y= (int) event.getRawY();        //坐标转换        int[] location = new  int[2] ;        getLocationInWindow(location); //获取在当前窗口内的绝对坐标        //getLocationOnScreen(location);//获取在整个屏幕内的绝对坐标        //location [0]--->x坐标,location [1]--->y坐标        x=x-location [0];        y=y-location [1];       // Log.e("绝对坐标",x+"--"+y);        int which=whichPath(x,y);        switch (event.getAction()){            case MotionEvent.ACTION_DOWN://                Log.i("which","->"+which);                resultList.clear();//开始下一次绘制前清空结果集                isUnLock=true;                break;            case MotionEvent.ACTION_MOVE:                if (which!=nowP && which!=0){//移动到了另一个合法区域                    nowP=which;//更改当前区域                    if(!resultList.contains(which)){//确保没进入过此区域                        resultList.add(which);                    }                }                //记录当前触控点并请求重绘                touchPoint.set(x,y);                invalidate();                break;            case MotionEvent.ACTION_UP:                //完成绘制解锁图后                nowP=0;                isUnLock=false;                Log.i("result",resultList.toString());                if(mListener!=null){                    mListener.finish(resultList);                }                rePlay();                break;        }        return true;    }//touch    private int whichPath(int x,int y){        if(x>startX+width | y>startY+width |x<startX |y<startY){           // Log.i("xy",x+"-"+y);            return 0;        }else {            //以起始点开始计算坐标            int nX=x-startX;            int nY=y-startY;            int hang=(int)nY/tWidth;//在哪行,从零开始            int lie=(int)nX/tWidth;//在哪列,从零开始           // Log.i("nXY",nX+"-"+nY);            int reInt= hang*3+1+lie;            //缩小判断区域            int smallY=(tWidth/6)+hang*tWidth;            int smallX=(tWidth/6)+lie*tWidth;            if (nX>smallX && nY>smallY && nX<(smallX+2*tWidth/3) && nY<(smallY+2*tWidth/3)){                return reInt;            }else{                return 0;            }        }    }//which    public void rePlay(){        resultList.clear();        isUnLock=false;        invalidate();    }//rePlay    void setDrawFinishListener(DrawFinishListener l){        this.mListener=l;    }}//class
调用自定义view的方法依然是这样的:

<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:paddingBottom="@dimen/activity_vertical_margin"    android:paddingLeft="@dimen/activity_horizontal_margin"    android:paddingRight="@dimen/activity_horizontal_margin"    android:paddingTop="@dimen/activity_vertical_margin"    tools:context="com.toxicant.hua.nicepathsunlock.MainActivity">    <com.toxicant.hua.nicepathsunlock.NinePathsView        android:id="@+id/view"        android:layout_height="match_parent"        android:layout_width="match_parent"        android:layout_centerVertical="true"        android:layout_centerHorizontal="true">    </com.toxicant.hua.nicepathsunlock.NinePathsView></RelativeLayout>
直接作为普通组件调用。

在代码中进行回调:

package com.toxicant.hua.nicepathsunlock;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.widget.Toast;import java.util.List;public class MainActivity extends AppCompatActivity {    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        NinePathsView mView= (NinePathsView) findViewById(R.id.view);        mView.setDrawFinishListener(new NinePathsView.DrawFinishListener() {            @Override            public void finish(List<Integer> resultList) {                Toast.makeText(MainActivity.this,resultList.toString(),Toast.LENGTH_SHORT).show();            }        });    }}
回调的是一个List<Integer>类型,可以直接调用tostring来保存,比较密码,也可直接通过size来密码限制最小长度。







1 0
原创粉丝点击