通过下拉来学习Scroller的内容滑动原理

首先介绍位置参数

View的位置top,left,right,bottom,四个参数是相对于父容器来说的。
translationX和translationY是View左上角相对于父容器的偏移量,也只能在父控件里移动,若超过范围,刚无法看到,translationX= 最后的相对于父容器的偏移量X - left,所以如果往左移,translationX为负值;translationY= 最后的相对于父容器的偏移量Y - top,所以如果往上移,translationY 为负值。
x = left + translationX;
y = top + translationY;
还有两个移动的重要参数:mScroolerX和mScrollerY

mScrollerX = 父容器的左边界-View的X,所以当View左边缘在View内容左边缘的右边时,mScrollerX为正值,否则为负值。同理,当View上边缘在View内容上边缘的下边时,mScrollerY为正值,否则为负值。

Scroller的滑动原理是将一次大的滑动分成若干次小的滑动,并在一个时间段内完成。

通过调用mScroller.startScroller()->invalidate()->导致界面重绘->View的computeScroll()方法执行,需要重写这个方法,并调用scrollTo完成一次小的滑动,并postInvalidate();

具体例子如下:

package com.luoxf.drag.view;

import android.content.Context;
import android.graphics.Point;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
import android.view.WindowManager;
import android.widget.RelativeLayout;
import android.widget.Scroller;

/**
 * Created by luoxf on 2015/10/27.
 */
public class MyRelativeLayout extends RelativeLayout{
    private final String TAG = MyRelativeLayout.class.getSimpleName();
    private int screenHeight;
    private int screenWidth;
    private Context mContext;
    private Scroller mScroller;
    private int mLastX;
    private int mLastY;
    private int touchSlop; //最小滑动距离
    private boolean isNeedToUp = false;
    public MyRelativeLayout(Context context) {
        super(context);
        initView(context);
    }

    public MyRelativeLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        initView(context);
    }

    public MyRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView(context);
    }

    private void initView(Context context) {
        this.mContext = context;
        this.mScroller = new Scroller(context);
        touchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
        WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
        Point point = new Point();
        wm.getDefaultDisplay().getSize(point);
        screenWidth = point.x;
        screenHeight = point.y;
    }

    //调用此方法滚动到目标位置
    public void smoothScrollTo(int fx, int fy) {
        int dx = fx - mScroller.getFinalX();
        int dy = fy - mScroller.getFinalY();
        smoothScrollBy(dx, dy);
    }

    //调用此方法设置滚动的相对偏移
    public void smoothScrollBy(int dx, int dy) {
        //设置mScroller的滚动偏移量
        mScroller.startScroll(mScroller.getFinalX(), mScroller.getFinalY(), dx, dy);
        invalidate();//这里必须调用invalidate()才能保证computeScroll()会被调用,否则不一定会刷新界面,看不到滚动效果
    }
    @Override
    public void computeScroll() {
        //先判断mScroller滚动是否完成
        if (mScroller.computeScrollOffset()) {
            //这里调用View的scrollTo()完成实际的滚动
            scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
            //必须调用该方法,否则不一定能看到滚动效果
            postInvalidate();
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int x = (int) event.getX();
        int y = (int) event.getY();
        switch (event.getAction()) {
            case MotionEvent.ACTION_MOVE:
                int deltaY = y - mLastY;
                //滑到顶部
                if(mScroller.getFinalY() - deltaY >= 0) {
                    smoothScrollTo(0, 0);
                    isNeedToUp = false;
                }
                //下滑大于屏幕三分之一
                else if(mScroller.getFinalY() - deltaY < -screenHeight / 3) {
                    smoothScrollTo(0, -screenHeight / 3);
                    isNeedToUp = true;
                } else {
                    smoothScrollBy(0, -deltaY);
                    isNeedToUp = true;
                }
                break;
            case MotionEvent.ACTION_UP:
                //自动滑到顶部
                if(isNeedToUp) {
                    smoothScrollTo(0, 0);
                }
                break;
            default:
                break;
        }
        mLastX = x;
        mLastY = y;
        return true;
    }
}


布局文件:

<com.luoxf.drag.view.MyRelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/myRelativiLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/black"
    tools:context=".MainActivity">

   <RelativeLayout
       android:layout_width="match_parent"
       android:layout_height="match_parent"
       android:background="@android:color/white">
       <TextView
           android:id="@+id/drag_demo"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:padding="10dp"
           android:text="ScrollDEMO"
           android:layout_centerInParent="true"/>
   </RelativeLayout>

</com.luoxf.drag.view.MyRelativeLayout>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值