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

来源:互联网 发布:js 调用按钮点击事件 编辑:程序博客网 时间:2024/05/22 12:23

在自定义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
@Overridepublic 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: onSingleTapUp06-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.12115506-19 22:03:29.478 14850-14850/com.czh.customviewtools D/CustomView: onScroll, distanceX=-9.851639, distanceY=-11.85520906-19 22:03:29.497 14850-14850/com.czh.customviewtools D/CustomView: onScroll, distanceX=-11.986954, distanceY=-11.99319506-19 22:03:29.512 14850-14850/com.czh.customviewtools D/CustomView: onScroll, distanceX=-11.984398, distanceY=-12.39547706-19 22:03:29.529 14850-14850/com.czh.customviewtools D/CustomView: onScroll, distanceX=-9.235718, distanceY=-12.8325506-19 22:03:29.547 14850-14850/com.czh.customviewtools D/CustomView: onScroll, distanceX=-2.74868, distanceY=-7.746185306-19 22:03:29.563 14850-14850/com.czh.customviewtools D/CustomView: onScroll, distanceX=-3.9947968, distanceY=-3.16326906-19 22:03:29.579 14850-14850/com.czh.customviewtools D/CustomView: onScroll, distanceX=-1.9973984, distanceY=-5.36285406-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.19662506-19 22:06:57.462 14850-14850/com.czh.customviewtools D/CustomView: onScroll, distanceX=-29.701729, distanceY=-34.45388806-19 22:06:57.479 14850-14850/com.czh.customviewtools D/CustomView: onScroll, distanceX=-50.0672, distanceY=-40.05697606-19 22:06:57.497 14850-14850/com.czh.customviewtools D/CustomView: onScroll, distanceX=-59.65364, distanceY=-41.4551706-19 22:06:57.513 14850-14850/com.czh.customviewtools D/CustomView: onScroll, distanceX=-46.238846, distanceY=-26.56616206-19 22:06:57.521 14850-14850/com.czh.customviewtools D/CustomView: onScroll, distanceX=-21.808838, distanceY=-11.82803306-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
@Overridepublic boolean onInterceptHoverEvent(MotionEvent event){    return mDragger.shouldInterceptTouchEvent(event);}@Overridepublic boolean onTouchEvent(MotionEvent event){    mDragger.processTouchEvent(event);    return true;}

下面贴个效果图


这里写图片描述

OK,可以发现,代码在水平方向上做了边界控制,垂直方向上可以任意拖动。

回调接口中还有很多方法可以让我们实现更加复杂的功能,比如:释放某个子view之后就回到原位;仿QQ侧滑菜单功能等等。

/** * 释放某个子view的回调 * 释放子view之后,让子view回到原位,就可以在这里实现 */@Overridepublic void onViewReleased(View releasedChild, float xvel, float yvel){}/** * 触摸屏幕Edge是触发;可以用来实现侧滑菜单功能 */@Overridepublic 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的打印,看一下效果。

首先,开始速度追踪

@Overridepublic 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.9734506-20 21:06:55.359 4247-4247/com.czh.customviewtools D/CustomView: onScroll, distanceX=-23.83014, distanceY=-19.84567306-20 21:06:55.376 4247-4247/com.czh.customviewtools D/CustomView: onScroll, distanceX=-9.126953, distanceY=-7.133239706-20 21:06:55.387 4247-4247/com.czh.customviewtools D/CustomView: onScroll, distanceX=-23.96878, distanceY=-15.98751806-20 21:06:55.387 4247-4247/com.czh.customviewtools D/CustomView: onFling, velocityX=678.5648, velocityY=314.0172706-20 21:06:55.387 4247-4247/com.czh.customviewtools D/CustomView: velocityTracker: velocityX=678, velocityY=314

从打印内容可以看出,GestureDetector中的onFling()回调函数传入的速度参数和使用VelocityTracker得到的速度值是一致的。

Scroller

之前写过一篇关于Scroller的文章Android Scroller使用详解。这里就不在赘述。下面就进行简单的说明:

  1. 首先View是支持scroll操作,api中提供了两个方法:scrollTo()和scrollBy()。
  2. 关于scrollTo()和scrollBy()。需要注意3点:第一、scrollBy()最终调用的是scrollTo()方法;第二、移动坐标的方向问题,比如:scrollBy(-30, -50),代表向右移动30像素,向下移动50像素;第三、移动的是View的内容,而不是View本身。如果你调用一个TextView的scrollBy()或者scrollTo()方法,将会移动TextView的文本内容,TextView本身并不会移动。
  3. 由于scrollTo()和scrollBy()移动效果是瞬间的,借助Scroller工具类可以完成平滑移动的效果。需要掌握的就是Scroller的固定用法以及使用步骤,可以参考之前的文章。

欧克,本篇文章先介绍这么多。自定义View过程中用到的工具类,sdk还提供了很多,后续会进行补充。

参考文章:自定义View系列教程01–常用工具介绍

1 0
原创粉丝点击