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

先上效果图


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

九宫格解锁还是比较经典的,作为学习自定义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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值