头图

First look at View#scrollTo(int x,int y) , take x as an example. When the incoming x>0 , View content scrolls, and scroll along the negative direction of x why???

ScrollTo scrolls the content of the View. If you want to scroll a view through scrollTo, you should call the scrollTo method of its parent View. Why is the content of the view scrolled? I will talk about this later.

Look at the source code.

First look View#scrollTo(int x,int y) source code of 0612f98ac50e15. x directly assigned to mScrollX .

    #View.java
   public void scrollTo(int x, int y) {
        if (mScrollX != x || mScrollY != y) {
            int oldX = mScrollX;
            int oldY = mScrollY;
            mScrollX = x;
            mScrollY = y;
            invalidateParentCaches();
            onScrollChanged(mScrollX, mScrollY, oldX, oldY);
            if (!awakenScrollBars()) {
                postInvalidateOnAnimation();
            }
        }
    }
For a View, when the scrollTo method of its parent view is called to scroll from point A to point B, and then to point C, the mScrollX of the view is the total ( A->C ) scroll distance instead of Phase ( A->B or B->C ) rolling distance

Look at View# invalidate(int l, int t, int r, int b) . x passed in by the previous method. l-scrollX is the left side of the drawing area, and r-scrollX is the right side of the drawing area. x>0 drawing area of 0612f98ac50efc will move to the left; the x<0 will move to the right.

    #View.java
   public void invalidate(int l, int t, int r, int b) {
        final int scrollX = mScrollX;
        final int scrollY = mScrollY;
        invalidateInternal(l - scrollX, t - scrollY, r - scrollX, b - scrollY, true, false);
    }

From the above, we can see that the View#scrollTo call will trigger View#invalidate , which causes the drawing area to change, that is, we see View displays the content has changed.

For a View position is changed, should be changed View the mLeft and mTop two attribute values. Obviously, View#scrollTo did not change the values of these two attributes. Therefore, View#scrollTo will not cause View to scroll, but will scroll the content View

How to customize a scrollable View?

Inherited from ViewGroup. Of course, if necessary, you need to rewrite onInterceptTouchEvent, intercept the MotionEvent.ACTION_MOVE event, rewrite onTouchEvent, and then call scrollTo or scrollBy to achieve scrolling.



private float lastDownX,lastDownY;

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
    switch(event.getAction()){
        case MotionEvent.ACTION_DOWN:
            lastDownX = event.getX();
            lastDownY = event.getY();
        break;
        case MotionEvent.ACTION_MOVE:
        if (满足一定条件){
            //请求父View不要拦截
            getParent().requestDisallowInterceptTouchEvent(true);
            //返回true。将ACTION_MOVE事件交给自身处理。
            return true;
        }
        break;
    }
    return super.onInterceptTouchEvent(ev);
}


@Override
public boolean onTouchEvent(MotionEvent event) {
   switch(event.getAction()){
        case MotionEvent.ACTION_DOWN:
            lastDownX = event.getX();
            lastDownY = event.getY();
        break;

        case MotionEvent.ACTION_MOVE:
            float moveX = event.getX();
            float moveY = event.getY();
            float diffX = moveX - lastDownX;
            float diffY = moveY - lastDownY;
            /**
            * 从左向右滑动,diffX<0;getScrollX()>0;
            * scrollTo的参数x>0
            * x = getScrollX() - diffX
            *
            * 从右向左滑动,diffX>0,getScrollX()<0;
            * scrollTo的参数x<0
            * x = getScrollX() - diffX
            *
            * 参数y同理
            */
            scrollTo((int)(getScrollX()-diffX),(int)(getScrollY()-diffY));
            //scrollBy(-(int)diffX,-(int)diffY);
            return true;
        case MotionEvent.ACTION_UP:
        lastDownX = 0;
        lastDownY = 0;
        break;
    } 
    return super.onTouchEvent(event);
}

When scrolling, the problem of boundary constraints should also be considered. Take the sliding deletion of the QQ message list as an example.
Swiping from left to right constrains the left boundary, and sliding from right to left constrains the right boundary.
Do boundary constraints in the ACTION_MOVE event. Use the code above.

    //最大滑动距离,根据自己的业务情况来确定这个值。以QQ侧滑删除为例,这个值是删除按钮的宽度。
    int maxScrollDistance;
    case MotionEvent.ACTION_MOVE:
        //某一时刻要滑动到的位置,这个位置到达边界时,要做约束。
        float targetX = getScrollX() - (event.x - lastDownX);
        //从左向右滑动,当滑动到左边界时,getScrollX() = 0;继续滑动,getScrollX()<0;对这个情况要做个约束,避免滑动越过左边界。
        if (targetX<0) targetX = 0;
        //从右向左滑动,当滑动到右边界时,getScrollX() = maxScrollDistance;继续滑动,getScrollX()>maxScrollDistance;对这个情况要做个约束,避免滑动越过右边界。
        if (targetX>maxScrollDistance) targetX = maxScrollDistance;
        scrollTo(targetX,0);

idealcn
27 声望4 粉丝