Android自定义View——手把手教你九宫格手势解锁

最近写了个九宫格手势解锁,先附上效果图吧?

Android自定义View——手把手教你九宫格手势解锁_第1张图片 Android自定义View——手把手教你九宫格手势解锁_第2张图片 Android自定义View——手把手教你九宫格手势解锁_第3张图片

上面是效果图,再附上代码?行。
Github 戳我
这篇博客讲述一下实现这个效果的思路。我觉得知道了这个思路,并且顺着这个思路走,每个人都可以自己写一个出来。但是写出来的代码健壮否,就看个人功底了。

创建GestureView继承自View

在这个代码里你只需要做两件事

  • 实现onDraw方法
  • 实现onTouchEvent方法

记下来我们肢解一下知识点。
在onDraw方法中,需要做四件事:

  • 计算获取9个点的坐标和小箭头的三个坐标点
  • 初始化9个点圆
  • 绘制选中的圆,以及选中的圆的内圆,还有就是圆和圆之间的连接线,以及小箭头
  • 绘制最后一条线段,也就是最后一个选中点到你正在触摸移动的线段。
	viewImpl.initData(viewWidth, viewHeight); //初始化9个点数据
    viewImpl.onDrawInitView(paint, canvas); //初始化View
    viewImpl.onDrawSelectView(paint, canvas); //绘制选中的View
    viewImpl.onDrawLineView(paint, canvas); //绘制连接线

我们需要重写onTouchEvent,在onTouchEvent方法中我们可以处理三个事件:

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN : {
                viewImpl.touchDown(event);
                break;
            }
            case MotionEvent.ACTION_MOVE : {
                viewImpl.touchMove(event);
                break;
            }
            case MotionEvent.ACTION_UP : {
                viewImpl.touchUp(event);
                break;
            }
        }
        return true;
    }

这三个事件中,一个一个说

  1. ACTION_DOWN事件,这个事件处理比较简单,我们需要判断当前按下的点是否是有效点,也就是:当前触摸的点是否在9个圆内。 如果不在里面,就不重新绘制View;如果在里面,需要存储下当前的圆的中心点坐标,表示:选中点。我们代码中是:selectPointMap;
    用到的数学知识:一个点(触摸点)是否在圆内。
  2. ACTION_MOVE事件,这个事件内,我们需要获取当前触摸移动过程中选中的点,存储到selectPointMap集合中。用到的数学知识:一个点(圆的坐标)的投影是否在线段上;线段是否穿过圆。
  3. ACTION_UP事件内处理很简单,就是你是想:校验密码,还是设置密码。都可以在这里面出来。

总结用到的数学知识点:

  1. 一个点(触摸点)是否在圆内?点到中心点的距离小于半径。如下图:
    Android自定义View——手把手教你九宫格手势解锁_第4张图片
  2. 一个点C(圆的坐标)的投影是否在线段上?AB * AB >= BC * BC + AC * AC, 即可认为:该点的投影在线段上。如下图:
    Android自定义View——手把手教你九宫格手势解锁_第5张图片
  3. 线段是否穿过圆? 圆的中心圆到线段的距离如果小于半径,即可认为线段和圆相切。
  4. 已知一个条高,还有这条高的两个坐标点,以及角度,求另外一点的坐标? 这是求小箭头的三个坐标。如下图:
    Android自定义View——手把手教你九宫格手势解锁_第6张图片

至此我们的整体流程讲完了。接下来说一下我们的这个Demo的功能点:

功能点

属性设置



    
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        	
            
            
            
        
    

上述列出了该Demo支持的属性。

支持自定义大圆,小圆,以及连接的线段

方法 参数
setHandleBigGraphical IGraphicalView接口
setHandleSmallGraphical IGraphicalView接口
setHandleLineGraphical IGraphicalView接口

/**
 * FileName:IGraphicalView
 * Create By:liumengqiang
 * Description:自定义需要实现的接口
 */
public interface IGraphicalView {

    /**
     * 初始化绘制
     * @param paint 画笔
     * @param canvas 画板
     * @param coordinateList 9个点的中心点坐标
     * @param attrsModel 属性对象
     */
    void onDrawInitView(Paint paint, Canvas canvas, List coordinateList, AttrsModel attrsModel);

    /**
     * 
     * @param paint
     * @param canvas
     * @param selectMap 当前选中点的信息
     * @param attrsModel
     * @param TYPE 当前的类型(成功?错误?绘制中?)
     */
    void onDrawSelectView(Paint paint, Canvas canvas, LinkedHashMap selectMap, AttrsModel attrsModel, int TYPE);
}

详细的自定义可见:HandleBigGraphical类。

支持自定义手势结果处理器
支持自定义手势结果处理器
支持自定义手势结果处理器

这个功能点肯定很重要吧,因为,你想自己处理手势的判断逻辑。我们可能是设置密码,也可能是验证密码,这样的处理逻辑是不一样的,这时候我们就可以自定义。

方法 参数 返回值
setProcessor IProcessor接口 TYPE_COMPLETE,TYPE_ERROR, TYPE_RESET

示例详见:CheckGestureProcessor类,设置手势示例。

public class CheckGestureProcessor implements IProcessor{


    private String gestureValue;

    public CheckGestureHandleData() {
    }

    /**
     *
     * @param selectIndexArray
     * @return
     */
    public int handleData(Set selectIndexArray) {
        if(gestureValue.equals(getGestureValue(selectIndexArray))) { //密码一致
            return GestureViewType.TYPE_COMPLETE;
        }  else { // 密码不一致
            return GestureViewType.TYPE_ERROR;
        }
    }

    private String getGestureValue(Set selectIndexArray) {
        String value = "";
        for(Integer integer : selectIndexArray) {
            value += integer;
        }
        return value;
    }

    @Override
    public void setGestureValue(String gestureValue) {
        if(gestureValue == null) {
            throw new IllegalArgumentException("输入参数GestureValue非法!");
        } else {
            this.gestureValue = gestureValue;
        }
    }
}

handleData方法内部处理自定需要自定义的逻辑。
然后设置自定义处理:

gestureView.setiHandleData(new CheckGestureProcessor());

结果回调GestureListener

方法 描述
onStart 手势开始时触发
onPointNumberChange 移动过程中,选中点时触发
onComplete 设置成功时触发
onFailed 小于最低设置的点个数触发
valueDisaccord 输入手势值不一样时触发
transitionStatus 过渡状态触发(需要多次输入校验,比如:设置密码)

如下示例:

        gestureView.setGestureListener(new GestureListener() {
            @Override
            public void valueDisaccord() {
                Toast.makeText(CheckGestureActivity.this, "输入密码错误", Toast.LENGTH_SHORT).show();
            }

            @Override
            public void transitionStatus() {

            }

            @Override
            public void onStart() {
                Log.e(TAG , "初始化完成");
            }

            @Override
            public void onPointNumberChange(int selectIndex) {
                Log.e(TAG, "点被选中");
            }

            @Override
            public void onComplete(List list) {
                Toast.makeText(CheckGestureActivity.this, "校验成功", Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onFailed() {
                Toast.makeText(CheckGestureActivity.this, "个数小于四个", Toast.LENGTH_SHORT).show();
            }
        });

基本功能点讲完了,如果听的有点迷糊,我觉得下载一下Demo,然后对照 Demo跑一遍,就OK了。

再附上一张整体效果Gif:

这个Demo还是缺点还是有的:

  • 绘制调用和Touch耦合度有点高,详见:GestureViewImpl
  • 初始化坐标可放入onMeasure方法,未支持自适应布局;

这些后续再补上。

好了欢迎大家Star。如果BUG欢迎指正。
Github 戳我
Github 戳我
Github 戳我
Github 戳我

你可能感兴趣的:(android开发,自定义view)