您的位置:首页 > 移动开发 > Android开发

自定义View之常用工具类汇总

2016-06-20 11:10 495 查看
在自定义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–常用工具介绍

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息