Android触摸事件(三)-触摸事件类使用实例
目录
概述
本文主要介绍之前提到的AbsTouchEventHandle
(自定义触摸事件处理类)及TouchUtils
(触摸事件辅助工具类)如何结合一起使用.
使用的目的或者说达到的结果是:
简单方便地完成界面元素的拖动与缩放
在整个过程中,界面元素的拖动与缩放工作完全交给以上两个类处理,而不需要做任何其它的操作.只需要完成的是相关的一些接口实现与回调而已.
使用流程
以下为两个类结合使用的简单流程,若不是特别理解可以略过,下面会一一详细说明.
创建一个专门用于绘制整个界面的类,Draw
使Draw
继承自AbsTouchEventHandle
,并实现重写其所有抽象方法(暂时不需要处理) 创建TouchUtils
的实例对象,TestRectangleDraw
实现TouchUtils
里的移动及缩放接口IMoveEvent
及IScaleEvent
将TouchUtils
实例对象与其接口实现类对象绑定以方便回调
使用AbsTouchEventHandle
我们知道,界面元素最终的显示是以View
的形式绘制到屏幕上.而View
的绘制工作全部都是在onDraw()
方法里处理的.
但这里我们需要创建一个新的类去完成绘制工作而不是直接通过View
去完成工作.这是因为
AbsTouchEventHandle
本身是个抽象类,意味着它必须被继承才能使用.而自定义View必定是继承自系统类View
,所以这是不可能再继承另外一个类的
除此之外,使用全新的类专门处理绘制工作还有一个好处是: 绘制工作是独立的,而不会与View
本身的某些方法混淆在一起.这是一种类似组合的方式而不是嵌套的方式.
由于绘制类不继承自View
,则需要与显示此绘制界面的View
关联起来,所以此绘制需要提供一些方法以将绘制界面成功连接到View
上.
最后一个重要的点,
AbsTouchEventHandle
本质是实现了触摸事件接口View.OnTouchListener
处理的,千万不要忘了将View
的onTouchListener
事件设置为当前的绘制类~~~
public class TestRectangleDraw extends AbsTouchEventHandle{
private View mDrawView;
public TestRectangleDraw(View drawView){
mDrawView=drawView;
//必须将绘制View的触摸监听事件替换成当前的类
mDrawView.setOnTouchListener(this);
}
//忽略抽象方法的重写,后面使用 TouchUtils 时再处理
}
以上为AbsTouchEventHandle
类的使用,当继承此类时可以直接处理View的单击事件,双击事件(拖动与缩放需要TouchUtils
辅助)
使用TouchUtils
使用TouchUtils
辅助工具类是为了方便处理拖动事件与缩放事件,一般的拖动事件及缩放事件都不需要再自定义,直接使用TouchUtils
及实现相关的接口即可.
首先,根据之前有关TouchUtils
的介绍文章,我们知道使用此工具类需要实现对应的接口,这是一个很重要的操作.
关于接口的实现并不需要一定是当前的绘制类,如果你喜欢完全可以使用一个新类去处理这些接口.但实际并没有这个必要,这里我们直接使用绘制类去实现这些接口
之后需要在绘制类中创建
TouchUtils
的实例对象,同时将接口实现对象与其绑定,以实现其拖动与缩放的有效性.
//为了方便查看,忽略之前继承的 AbsTouchEventHandle,只看接口的实现
public class TestRectangleDraw implements TouchUtils.IMoveEvent, TouchUtils.IScaleEvent{
private TouchUtils mTouch;
public TestRectangleDraw(View drawView){
mDrawView=drawView;
//必须将绘制View的触摸监听事件替换成当前的类
mDrawView.setOnTouchListener(this);
mTouch=new TouchUtils();
mTouch.setMoveEvent(this);
mTouch.setScaleEvent(this);
}
//接口实现内容下面会详细解释,暂时忽略
}
以上即为TouchUtils
的基本使用.下面是关于一些细节部分的处理.
细节易错点
特别提出的是,这个使用上应该不会很难,但是在TouchUtils.IScaleEvent
接口的实现上,很可能出错率会很高.这并不是此两个类的问题,而是在处理数据时可能会考虑不完善的问题(已经踩了几次坑了),下面会有两个实现类的对比.文章最后也会将两个类完整地给出.可以做比较查看.
关于TouchUtils.IMoveEvent
前面两部分的操作是必须,这个地方只讨论这个接口的具体实现.
这个接口是为了实现拖动操作的.接口本身不处理任何的拖动操作事件,只是为了
确认是否允许拖动操作;拖动操作的执行及拖动操作失误时的处理.
这个接口一共有四个方法:
//是否允许X轴方向的拖动
boolean isCanMovedOnX(float moveDistanceX, float newOffsetX);
//是否允许Y轴方向的拖动
boolean isCanMovedOnY(float moveDistacneY, float newOffsetY);
//拖动事件(基本上就是重绘操作)
void onMove(int suggestEventAction);
//拖动失败事件(不能拖动)
void onMoveFail(int suggetEventAction);
接口的功能应该是很明确的.下面给出一个简单的实现.
//允许X轴方向的拖动
boolean isCanMovedOnX(float moveDistanceX, float newOffsetX){
return true;
}
//允许Y轴方向的拖动
boolean isCanMovedOnY(float moveDistacneY, float newOffsetY){
return true;
}
//通知View重绘
void onMove(int suggestEventAction){
mDrawView.postInvalidate();
}
//拖动失败事件(不能拖动)
void onMoveFail(int suggetEventAction){
//一般都不需要特别处理,可以选择提醒用户不能拖动了
}
拖动事件接口的实现并不会有很大的麻烦,一般也不容易出错.唯一的问题是在isCanMovedOn
方法中根据实际的情况去确定什么时候可以进行拖动什么时候不可以(绘制元素边界?还是达到屏幕边界?)
关于TouchUtils.IScaleEvent
缩放回调事件会相对复杂一点.这是因为拖动的时候元素大小是不变的,也就是界面元素的本身的属性都是不变的.仅仅改变的是坐标.
但从关于TouchUtils
的文章中我们已经是将坐标的变化与元素本身的坐标是分开的.即不管在任何时候,元素都只处理初始化时的坐标,任何移动产生偏移的坐标都由TouchUtils
类去处理.
但是缩放是不一样的.缩放时意味着元素本身的属性会改变,大小,长宽等都会改变.包括自身的坐标.
由于绘制的元素是什么工具类无法确定,所以整个元素的变化操作只能交给绘制元素的类去处理.即工具类不负责缩放元素的属性变化,只会告诉绘制类变化的比例.
基于这个原则,TouchUtils.IScaleEvent
就有存在的需要了.同理,参考TouchUtils.IMoveEvent
的方法,IScaleEvent
也有四个方法
//是否允许缩放,缩放肯定是整个元素一起缩放的,不存在某个维度可以缩放而另外一个却不可以的情况
boolean isCanScale(float newScaleRate);
//设置元素缩放比
void setScaleRate(float newScaleRate, boolean isNeedStoreValue);
//缩放操作(基本上是重绘)
void onScale(int suggestEventAction);
//缩放失败操作(不能缩放的情况下)
void onScaleFail(int suggetEventAction);
以上四个方法应该也不难理解的.其中onScale
及onScaleFail
两个方法是最容易的.isCanScale
取决于用户需求而是否复杂,在任何情况下都可以缩放直接返回true
即可.
最重要的一个方法,也可能是最复杂和容易出错的:setScaleRate
,必须记住的是
setScaleRate
是用于处理元素缩放属性数据的.在一次缩放事件中,每一次回调都是相对开始缩放前的原始元素大小的比例.
以下重点解释此方法,本小节最后会给出此接口实现的完整代码.
一次缩放事件是指: 两点触摸按下时到两点触摸抬起时整个过程
开始缩放前原始元素大小是指: 两点触摸按下时元素的大小
整个缩放过程的比例都是以 两点按下时元素的大小为基础的
在下面会使用两个不同的情景来解释此方法的使用.其中一个是TestCircleDraw
,界面元素仅为一个圆形;
另外一个是TestRectangleDraw
,界面元素仅为一个矩形;
通过这两个实际的不同的界面元素的绘制来说明该方法使用时需要注意的一些问题
使用缩放前,我们