自定义View之常用工具类汇总
2016-06-20 11:10
495 查看
在自定义View的过程中,我们经常用到各种工具类来简化我们的工作。这篇文章就对sdk提供的一些常用的工具类进行一下总结。
获取ViewConfiguration实例:
这个类提供了一些常用的对象方法用于获取基本常量值:
还有一些常用的静态方法:
Detects various gestures and events using the supplied MotionEvents. The GestureDetector.OnGestureListener callback will notify users when a particular motion event has occurred.
一般我们按照如下步骤使用GestureDetector
创建GestureDetector实例
监听各种手势回调函数
将MotionEvent对象传递给GestureDetector
OK,下面贴一下Log.d的打印,方便理解各个回调函数何时调用
单击
双击
Scroll,从左上到右下滑动
Fling,从左上到右下
这里Fling手势速度的含义。velocityX:1秒钟在X方向上滑动的像素值;velocityY:1秒钟在Y方向上滑动的像素值。
ViewDragHelper is a utility class for writing custom ViewGroups. It offers a number of useful operations and state tracking for allowing a user to drag and reposition views within their parent ViewGroup.
看一下ViewDragHelper最简单的使用
解释一下:
创建ViewDragHelper实例
第一个参数传入ViewGroup;第二参数是拖拽的精确度,通常传入1.0f;第三个参数是ViewDragHelper.Callback()实例,这个回调接口就用来控制拖拽的细节。
回调函数
想要实现拖拽效果,至少重写这三个回调函数。每个函数的功能都有注释,就不赘述了。
最后需要把MotionEvent交给ViewDragHelper
下面贴个效果图
OK,可以发现,代码在水平方向上做了边界控制,垂直方向上可以任意拖动。
回调接口中还有很多方法可以让我们实现更加复杂的功能,比如:释放某个子view之后就回到原位;仿QQ侧滑菜单功能等等。
第一步:开始速度追踪
第二步:获取追踪到的速度
最后:停止速度追踪
我在前面GestureDetector的demo中,加入了VelocityTracker的打印,看一下效果。
首先,开始速度追踪
打印追踪到的速度
下面是程序打印内容:
从打印内容可以看出,GestureDetector中的onFling()回调函数传入的速度参数和使用VelocityTracker得到的速度值是一致的。
首先View是支持scroll操作,api中提供了两个方法:scrollTo()和scrollBy()。
关于scrollTo()和scrollBy()。需要注意3点:第一、scrollBy()最终调用的是scrollTo()方法;第二、移动坐标的方向问题,比如:scrollBy(-30, -50),代表向右移动30像素,向下移动50像素;第三、移动的是View的内容,而不是View本身。如果你调用一个TextView的scrollBy()或者scrollTo()方法,将会移动TextView的文本内容,TextView本身并不会移动。
由于scrollTo()和scrollBy()移动效果是瞬间的,借助Scroller工具类可以完成平滑移动的效果。需要掌握的就是Scroller的固定用法以及使用步骤,可以参考之前的文章。
欧克,本篇文章先介绍这么多。自定义View过程中用到的工具类,sdk还提供了很多,后续会进行补充。
ViewConfiguration
这个类是在自定义View的过程中使用频率非常高的。比如自定义View需要和用户进行Touch交互的时候,常常需要获得TouchSlop,就是通过这个类来获得。获取ViewConfiguration实例:
ViewConfiguration vc = ViewConfiguration.get(context);
这个类提供了一些常用的对象方法用于获取基本常量值:
//认定是Scroll操作的最短距离,以像素为单位 int touchSlop = vc.getScaledTouchSlop(); //是否含有物理实体按键 boolean isHavePermanentMenuKey = vc.hasPermanentMenuKey(); //获取fling速度的最大值和最小值 int maxFlingVelocity = vc.getScaledMaximumFlingVelocity(); int minFlingVelocity = vc.getScaledMinimumFlingVelocity();
还有一些常用的静态方法:
//双击的时间间隔,否则认定是单击 int doubleTapTimeout = ViewConfiguration.getDoubleTapTimeout(); //长按状态的时间长度 int longPressTimeout = ViewConfiguration.getLongPressTimeout();
GestureDetector
在编写自定义View的时候,可能需要检测一些特定的手势。如果我们在onTouchEvent回调函数中自己处理必定非常麻烦。使用工具类GestureDetector可以简化我们的工作量。Detects various gestures and events using the supplied MotionEvents. The GestureDetector.OnGestureListener callback will notify users when a particular motion event has occurred.
一般我们按照如下步骤使用GestureDetector
创建GestureDetector实例
mGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener(){ ... }
监听各种手势回调函数
mGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener(){ @Override public boolean onSingleTapUp(MotionEvent e) { Log.d("CustomView", "onSingleTapUp"); return true; } @Override public void onLongPress(MotionEvent e) { Log.d("CustomView", "onLongPress"); } @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { Log.d("CustomView", "onScroll, distanceX=" + distanceX + ", distanceY=" + distanceY); return true; } @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { Log.d("CustomView", "onFling, velocityX=" + velocityX + ", velocityY=" + velocityY); return true; } @Override public boolean onDoubleTap(MotionEvent e) { Log.d("CustomView", "onDoubleTap"); return true; } });
将MotionEvent对象传递给GestureDetector
@Override public boolean onTouchEvent(MotionEvent event) { mGestureDetector.onTouchEvent(event); return true; }
OK,下面贴一下Log.d的打印,方便理解各个回调函数何时调用
单击
06-19 22:01:25.240 14850-14850/com.czh.customviewtools D/CustomView: onSingleTapUp
双击
06-19 22:01:04.054 14850-14850/com.czh.customviewtools D/CustomView: onSingleTapUp 06-19 22:01:04.139 14850-14850/com.czh.customviewtools D/CustomView: onDoubleTap
Scroll,从左上到右下滑动
06-19 22:03:29.463 14850-14850/com.czh.customviewtools D/CustomView: onScroll, distanceX=-17.110687, distanceY=-15.121155 06-19 22:03:29.478 14850-14850/com.czh.customviewtools D/CustomView: onScroll, distanceX=-9.851639, distanceY=-11.855209 06-19 22:03:29.497 14850-14850/com.czh.customviewtools D/CustomView: onScroll, distanceX=-11.986954, distanceY=-11.993195 06-19 22:03:29.512 14850-14850/com.czh.customviewtools D/CustomView: onScroll, distanceX=-11.984398, distanceY=-12.395477 06-19 22:03:29.529 14850-14850/com.czh.customviewtools D/CustomView: onScroll, distanceX=-9.235718, distanceY=-12.83255 06-19 22:03:29.547 14850-14850/com.czh.customviewtools D/CustomView: onScroll, distanceX=-2.74868, distanceY=-7.7461853 06-19 22:03:29.563 14850-14850/com.czh.customviewtools D/CustomView: onScroll, distanceX=-3.9947968, distanceY=-3.163269 06-19 22:03:29.579 14850-14850/com.czh.customviewtools D/CustomView: onScroll, distanceX=-1.9973984, distanceY=-5.362854 06-19 22:03:29.596 14850-14850/com.czh.customviewtools D/CustomView: onScroll, distanceX=-2.996109, distanceY=-2.4653015
Fling,从左上到右下
06-19 22:06:57.446 14850-14850/com.czh.customviewtools D/CustomView: onScroll, distanceX=-24.924263, distanceY=-25.196625 06-19 22:06:57.462 14850-14850/com.czh.customviewtools D/CustomView: onScroll, distanceX=-29.701729, distanceY=-34.453888 06-19 22:06:57.479 14850-14850/com.czh.customviewtools D/CustomView: onScroll, distanceX=-50.0672, distanceY=-40.056976 06-19 22:06:57.497 14850-14850/com.czh.customviewtools D/CustomView: onScroll, distanceX=-59.65364, distanceY=-41.45517 06-19 22:06:57.513 14850-14850/com.czh.customviewtools D/CustomView: onScroll, distanceX=-46.238846, distanceY=-26.566162 06-19 22:06:57.521 14850-14850/com.czh.customviewtools D/CustomView: onScroll, distanceX=-21.808838, distanceY=-11.828033 06-19 22:06:57.521 14850-14850/com.czh.customviewtools D/CustomView: onFling, velocityX=985.20465, velocityY=402.38626
这里Fling手势速度的含义。velocityX:1秒钟在X方向上滑动的像素值;velocityY:1秒钟在Y方向上滑动的像素值。
ViewDragHelper
自定义ViewGroup的时候,子view和用户进行交互是常有的事,即用户拖动某个子view(eg:侧滑菜单)。针对具体需求重写onInterceptTouchEvent和onTouchEvent不是件容易的事。ViewDragHelper可以帮助我们完成很多工作。ViewDragHelper is a utility class for writing custom ViewGroups. It offers a number of useful operations and state tracking for allowing a user to drag and reposition views within their parent ViewGroup.
看一下ViewDragHelper最简单的使用
public class ViewDragHelperDemo extends LinearLayout { private ViewDragHelper mDragger; public ViewDragHelperDemo(Context context, AttributeSet attrs) { super(context, attrs); //创建ViewDragHelper实例 mDragger = ViewDragHelper.create(this, 1.0f, new ViewDragHelper.Callback() { @Override public boolean tryCaptureView(View child, int pointerId) { return true; } /** * 在水平方向上指定被拖拽子view的位置 * 这里做了边界控制,限制了子view在ViewGroup的水平范围内移动 */ @Override public int clampViewPositionHorizontal(View child, int left, int dx) { int leftBound = getPaddingLeft(); int rightBound = getWidth() - getPaddingRight() - child.getWidth(); left = left < leftBound ? leftBound : left; left = left > rightBound ? rightBound : left; return left; } /** * 在垂直方向上指定被拖拽子view的位置 * 这里没有做边界控制 */ @Override public int clampViewPositionVertical(View child, int top, int dy) { return top; } }); } @Override public boolean onInterceptHoverEvent(MotionEvent event) { return mDragger.shouldInterceptTouchEvent(event); } @Override public boolean onTouchEvent(MotionEvent event) { mDragger.processTouchEvent(event); return true; } }
解释一下:
创建ViewDragHelper实例
mDragger = ViewDragHelper.create(this, 1.0f, new ViewDragHelper.Callback(){ ... });
第一个参数传入ViewGroup;第二参数是拖拽的精确度,通常传入1.0f;第三个参数是ViewDragHelper.Callback()实例,这个回调接口就用来控制拖拽的细节。
回调函数
mDragger = ViewDragHelper.create(this, 1.0f, new ViewDragHelper.Callback() { /** * 返回true表示,可以对子view进行拖拽 */ @Override public boolean tryCaptureView(View child, int pointerId) { return true; } /** * 在水平方向上指定被拖拽子view的位置 * 这里做了边界控制,限制了子view在ViewGroup的水平范围内移动 */ @Override public int clampViewPositionHorizontal(View child, int left, int dx) { int leftBound = getPaddingLeft(); int rightBound = getWidth() - getPaddingRight() - child.getWidth(); left = left < leftBound ? leftBound : left; left = left > rightBound ? rightBound : left; return left; } /** * 在垂直方向上指定被拖拽子view的位置 * 这里没有做边界控制 */ @Override public int clampViewPositionVertical(View child, int top, int dy) { return top; } });
想要实现拖拽效果,至少重写这三个回调函数。每个函数的功能都有注释,就不赘述了。
最后需要把MotionEvent交给ViewDragHelper
@Override public boolean onInterceptHoverEvent(MotionEvent event) { return mDragger.shouldInterceptTouchEvent(event); } @Override public boolean onTouchEvent(MotionEvent event) { mDragger.processTouchEvent(event); return true; }
下面贴个效果图
OK,可以发现,代码在水平方向上做了边界控制,垂直方向上可以任意拖动。
回调接口中还有很多方法可以让我们实现更加复杂的功能,比如:释放某个子view之后就回到原位;仿QQ侧滑菜单功能等等。
/** * 释放某个子view的回调 * 释放子view之后,让子view回到原位,就可以在这里实现 */ @Override public void onViewReleased(View releasedChild, float xvel, float yvel) { } /** * 触摸屏幕Edge是触发;可以用来实现侧滑菜单功能 */ @Override public void onEdgeDragStarted(int edgeFlags, int pointerId) { }
VelocityTracker
触摸手势的速度追踪帮助类。简单说一下使用方法:第一步:开始速度追踪
private void startVelocityTracker(MotionEvent event) { if(mVelocityTracker == null) { mVelocityTracker = VelocityTracker.obtain(); } mVelocityTracker.addMovement(event); }
第二步:获取追踪到的速度
/** * 获取scroll时X方向上的速度 * @return */ private int getScrollVelocityX() { //设置单位,1000表示返回的是一秒内移动的像素值 mVelocityTracker.computeCurrentVelocity(1000); int velocityX = (int) mVelocityTracker.getXVelocity(); return Math.abs(velocityX); } /** * 获取scroll时Y方向上的速度 * @return */ private int getScrollVelocityY() { //设置单位,1000表示返回的是一秒内移动的像素值 mVelocityTracker.computeCurrentVelocity(1000); int velocityY = (int) mVelocityTracker.getYVelocity(); return Math.abs(velocityY); }
最后:停止速度追踪
private void stopVelocityTracker() { if(mVelocityTracker != null) { mVelocityTracker.recycle(); mVelocityTracker = null; } }
我在前面GestureDetector的demo中,加入了VelocityTracker的打印,看一下效果。
首先,开始速度追踪
@Override public boolean onTouchEvent(MotionEvent event) { mGestureDetector.onTouchEvent(event); startVelocityTracker(event); return true; }
打印追踪到的速度
mGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener(){ //这里省略了一些回调函数 @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { Log.d("CustomView", "onFling, velocityX=" + velocityX + ", velocityY=" + velocityY); Log.d("CustomView", "velocityTracker: velocityX=" + getScrollVelocityX()+ ", velocityY=" + getScrollVelocityY()); return true; } });
下面是程序打印内容:
06-20 21:06:55.342 4247-4247/com.czh.customviewtools D/CustomView: onScroll, distanceX=-37.950577, distanceY=-33.97345 06-20 21:06:55.359 4247-4247/com.czh.customviewtools D/CustomView: onScroll, distanceX=-23.83014, distanceY=-19.845673 06-20 21:06:55.376 4247-4247/com.czh.customviewtools D/CustomView: onScroll, distanceX=-9.126953, distanceY=-7.1332397 06-20 21:06:55.387 4247-4247/com.czh.customviewtools D/CustomView: onScroll, distanceX=-23.96878, distanceY=-15.987518 06-20 21:06:55.387 4247-4247/com.czh.customviewtools D/CustomView: onFling, velocityX=678.5648, velocityY=314.01727 06-20 21:06:55.387 4247-4247/com.czh.customviewtools D/CustomView: velocityTracker: velocityX=678, velocityY=314
从打印内容可以看出,GestureDetector中的onFling()回调函数传入的速度参数和使用VelocityTracker得到的速度值是一致的。
Scroller
之前写过一篇关于Scroller的文章Android Scroller使用详解。这里就不在赘述。下面就进行简单的说明:首先View是支持scroll操作,api中提供了两个方法:scrollTo()和scrollBy()。
关于scrollTo()和scrollBy()。需要注意3点:第一、scrollBy()最终调用的是scrollTo()方法;第二、移动坐标的方向问题,比如:scrollBy(-30, -50),代表向右移动30像素,向下移动50像素;第三、移动的是View的内容,而不是View本身。如果你调用一个TextView的scrollBy()或者scrollTo()方法,将会移动TextView的文本内容,TextView本身并不会移动。
由于scrollTo()和scrollBy()移动效果是瞬间的,借助Scroller工具类可以完成平滑移动的效果。需要掌握的就是Scroller的固定用法以及使用步骤,可以参考之前的文章。
欧克,本篇文章先介绍这么多。自定义View过程中用到的工具类,sdk还提供了很多,后续会进行补充。
参考文章:自定义View系列教程01–常用工具介绍
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Manifest 用法
- [转载]Activity中ConfigChanges属性的用法
- Android之获取手机上的图片和视频缩略图thumbnails
- Android之使用Http协议实现文件上传功能
- Android学习笔记(二九):嵌入浏览器
- android string.xml文件中的整型和string型代替
- i-jetty环境搭配与编译
- android之定时器AlarmManager
- android wifi 无线调试
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题
- android:installLocation简析
- android searchView的关闭事件
- SourceProvider.getJniDirectories