Summary of common tools for customizing View

In the process of customizing View, we often use various tool classes to simplify our work. This article summarizes some commonly used tool classes provided by SDK.


ViewConfiguration

This class is very frequently used in the process of customizing View. For example, when a custom View needs to interact with the user through Touch, it often needs to obtain TouchSlop, which is obtained through this class.

Get the ViewConfiguration instance:

ViewConfiguration vc = ViewConfiguration.get(context);

This class provides some common object methods for obtaining basic constant values:

//认定是Scroll操作的最短距离,以像素为单位
int touchSlop = vc.getScaledTouchSlop();

//是否含有物理实体按键
boolean isHavePermanentMenuKey = vc.hasPermanentMenuKey();

//获取fling速度的最大值和最小值
int maxFlingVelocity = vc.getScaledMaximumFlingVelocity();
int minFlingVelocity = vc.getScaledMinimumFlingVelocity();

There are also some commonly used static methods:

//双击的时间间隔,否则认定是单击
int doubleTapTimeout = ViewConfiguration.getDoubleTapTimeout();
//长按状态的时间长度
int longPressTimeout = ViewConfiguration.getLongPressTimeout();

GestureDetector

When writing a custom View, you may need to detect some specific gestures. It must be very troublesome if we handle it ourselves in the onTouchEvent callback function. Using the tool class GestureDetector can simplify our workload.

Detects various gestures and events using the supplied MotionEvents. The GestureDetector.OnGestureListener callback will notify users when a particular motion event has occurred.

Generally, we use GestureDetector according to the following steps

  • Create a GestureDetector instance
mGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener(){
    ...
}
  • Monitor various gesture callback functions
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;
    }
});
  • Pass MotionEvent object to GestureDetector
@Override
public boolean onTouchEvent(MotionEvent event)
{
    mGestureDetector.onTouchEvent(event);
    return true;
}

OK, let’s post the print of Log.d below, so that we can understand when each callback function is called

  • click
06-19 22:01:25.240 14850-14850/com.czh.customviewtools D/CustomView: onSingleTapUp
  • double click
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, slide from upper left to lower right
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, top left to bottom right
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

Here is the meaning of Fling gesture speed. velocityX: the pixel value that slides in the X direction in 1 second; velocityY: the pixel value that slides in the Y direction in 1 second.

ViewDragHelper

When customizing the ViewGroup, it is common for the sub-view to interact with the user, that is, the user drags a sub-view (eg: side-sliding menu). It is not easy to rewrite onInterceptTouchEvent and onTouchEvent according to specific needs. ViewDragHelper can help us do a lot of work.

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.

Take a look at the simplest use of 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;
    }
}

explain:

  • Create an instance of ViewDragHelper
mDragger = ViewDragHelper.create(this, 1.0f, new ViewDragHelper.Callback(){
    ...
});

The first parameter is passed to the ViewGroup; the second parameter is the drag accuracy, usually 1.0f; the third parameter is the ViewDragHelper.Callback() instance, and this callback interface is used to control the details of the drag.

  • 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;
    }

});

To achieve the drag effect, at least rewrite these three callback functions. The function of each function has comments, so I won't go into details.

  • Finally, you need to hand the MotionEvent to ViewDragHelper
@Override
public boolean onInterceptHoverEvent(MotionEvent event)
{
    return mDragger.shouldInterceptTouchEvent(event);
}

@Override
public boolean onTouchEvent(MotionEvent event)
{
    mDragger.processTouchEvent(event);
    return true;
}

Post an effect picture below


Write picture description here

OK, it can be found that the code has boundary control in the horizontal direction, and can be dragged arbitrarily in the vertical direction.

There are still many methods in the callback interface that allow us to implement more complex functions, such as: returning to the original position after releasing a sub-view; imitating the QQ side-sliding menu function, and so on.

/**
 * 释放某个子view的回调
 * 释放子view之后,让子view回到原位,就可以在这里实现
 */
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel)
{

}

/**
 * 触摸屏幕Edge是触发;可以用来实现侧滑菜单功能
 */
@Override
public void onEdgeDragStarted(int edgeFlags, int pointerId)
{

}

VelocityTracker

Velocity tracking helper class for touch gestures. Simply talk about how to use it:

Step 1: Start Speed ​​Tracking

private void startVelocityTracker(MotionEvent event)
{
    if(mVelocityTracker == null) {
        mVelocityTracker = VelocityTracker.obtain();
    }

    mVelocityTracker.addMovement(event);
}

Step 2: Obtain the tracked speed

/**
 * 获取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);
}

Finally: stop speed tracking

private void stopVelocityTracker()
{
    if(mVelocityTracker != null) {
        mVelocityTracker.recycle();
        mVelocityTracker = null;
    }
}

In the previous demo of GestureDetector, I added the printing of VelocityTracker to see the effect.

First, start speed tracking

@Override
public boolean onTouchEvent(MotionEvent event)
{
    mGestureDetector.onTouchEvent(event);
    startVelocityTracker(event);
    return true;
}

print tracked velocity

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;
    }

});

Here is what the program prints:

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

It can be seen from the printed content that the velocity parameter passed in by the onFling() callback function in GestureDetector is consistent with the velocity value obtained by using VelocityTracker.

Scroller

I wrote an article about Scroller before and explained the use of Android Scroller in detail . I won't go into details here. Here is a brief explanation:

  1. First of all, View supports scroll operation. Two methods are provided in api: scrollTo() and scrollBy().
  2. About scrollTo() and scrollBy(). There are three points to note: first, scrollBy() finally calls the scrollTo() method; second, the direction of the moving coordinates, for example: scrollBy(-30, -50), means moving 30 pixels to the right and moving down 50 pixels; Third, what moves is the content of the View, not the View itself. If you call the scrollBy() or scrollTo() method of a TextView, the text content of the TextView will be moved, but the TextView itself will not move.
  3. Since the movement effects of scrollTo() and scrollBy() are instantaneous, the effect of smooth movement can be achieved with the help of the Scroller tool class. What you need to master is the fixed usage and usage steps of Scroller, you can refer to the previous article.

Ok, this article introduces so much first. The sdk also provides a lot of tool classes used in the process of customizing View, which will be added later.

Reference article: Custom View Series Tutorial 01 – Introduction to Common Tools

Guess you like

Origin blog.csdn.net/H_Zhang/article/details/51719411