Android 自定义View之Scroller处理滚动工具类详解

1.简介

Scroller是一个专门用于处理滚动效果的工具类,可能在大多数情况下,我们直接使用Scroller的场景并不多,但是很多大家所熟知的控件在内部都是使用Scroller来实现的,如ViewPager、ListView等。




2.View的scrollBy和scrollTo方法


2.1.图解





2.2.scrollBy方法

2.2.1.方法

public void scrollBy (int x, int y)


2.2.2.作用

移动你视图的滚动位置。这将导致对onScrollChanged(int, int, int, int)的调用,视图将被无效。


2.2.3.参数

x:X轴(水平)方向移动的距离。正值向左移动,负值向右移动,单位是像素。

y:Y轴(垂直)方向移动的距离。正值向上移动,负值向下移动,单位是像素。 



2.3.scrollTo方法

2.3.1.方法

public void scrollTo (int x, int y)


2.3.2.作用

设置视图的滚动位置。这将导致对onScrollChanged(int, int, int, int)的调用,视图将被无效。


2.3.3.参数

x:X轴(水平)方向移动到目的坐标。正值向左移动,负值向右移动,单位是像素。

y:Y轴(垂直)方向移动到目的坐标。正值向上移动,负值向下移动,单位是像素。 




2.3.scrollTo实现Demo

2.3.1.布局

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/activity_marginlayout_layout"
    android:orientation="vertical">

    <TextView
        android:id="@+id/activity_marginlayout_textview1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:background="#FF0000"
        android:gravity="center"
        android:padding="10dp"
        android:text="scrollTo方法测试"
        android:textColor="#FFFFFF" />


</RelativeLayout>


2.3.2.Java代码

public class MarginLayoutParamsActivity extends AppCompatActivity {

    private RelativeLayout layout;
    private TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_marginlayoutparams);

        layout= (RelativeLayout) findViewById(R.id.activity_marginlayout_layout);
        textView= (TextView) findViewById(R.id.activity_marginlayout_textview1);
        textView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                layout.scrollTo(-100, -100);
            }
        });

    }

}

2.3.3.效果

原始:



点击(scrollTo到某某 所以点击一次到达指定目的地后再点击无效果)




2.4.scrollBy实现Demo

2.4.1.布局

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/activity_marginlayout_layout"
    android:orientation="vertical">

    <TextView
        android:id="@+id/activity_marginlayout_textview1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:background="#FF0000"
        android:gravity="center"
        android:padding="10dp"
        android:text="scrollBy方法测试"
        android:textColor="#FFFFFF" />


</RelativeLayout>



2.4.2.Java代码

public class MarginLayoutParamsActivity extends AppCompatActivity {

    private RelativeLayout layout;
    private TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_marginlayoutparams);

        layout= (RelativeLayout) findViewById(R.id.activity_marginlayout_layout);
        textView= (TextView) findViewById(R.id.activity_marginlayout_textview1);
        textView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                layout.scrollBy(-100, -100);
            }
        });

    }

}


2.4.3.效果

原始:



点击



多次点击





3. Scroller工具类使用(参考:https://blog.csdn.net/guolin_blog/article/details/48719871)


3.1. Scroller使用步骤


3.1.1. 创建Scroller的实例。

3.1.2. 调用startScroll()方法来初始化滚动数据并刷新界面。

3.1.3. 重写computeScroll()方法,并在其内部完成平滑滚动的逻辑。



3.2.Demo实现仿ViewPager滑动

3.2.1.自定义View

public class ScrollerLayout extends ViewGroup {

    /**
     * 用于完成滚动操作的实例
     */
    private Scroller mScroller;

    /**
     * 判定为拖动的最小移动像素数
     */
    private int mTouchSlop;

    /**
     * 手机按下时的屏幕坐标
     */
    private float mXDown;

    /**
     * 手机当时所处的屏幕坐标
     */
    private float mXMove;

    /**
     * 上次触发ACTION_MOVE事件时的屏幕坐标
     */
    private float mXLastMove;

    /**
     * 界面可滚动的左边界
     */
    private int leftBorder;

    /**
     * 界面可滚动的右边界
     */
    private int rightBorder;

    public ScrollerLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        // 第一步,创建Scroller的实例
        mScroller = new Scroller(context);
        ViewConfiguration configuration = ViewConfiguration.get(context);
        // 获取TouchSlop值
        mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(configuration);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            View childView = getChildAt(i);
            // 为ScrollerLayout中的每一个子控件测量大小
            measureChild(childView, widthMeasureSpec, heightMeasureSpec);
        }
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        if (changed) {
            int childCount = getChildCount();
            for (int i = 0; i < childCount; i++) {
                View childView = getChildAt(i);
                // 为ScrollerLayout中的每一个子控件在水平方向上进行布局
                childView.layout(i * childView.getMeasuredWidth(), 0, (i + 1) * childView.getMeasuredWidth(), childView.getMeasuredHeight());
            }
            // 初始化左右边界值
            leftBorder = getChildAt(0).getLeft();
            rightBorder = getChildAt(getChildCount() - 1).getRight();
        }
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mXDown = ev.getRawX();
                mXLastMove = mXDown;
                break;
            case MotionEvent.ACTION_MOVE:
                mXMove = ev.getRawX();
                float diff = Math.abs(mXMove - mXDown);
                mXLastMove = mXMove;
                // 当手指拖动值大于TouchSlop值时,认为应该进行滚动,拦截子控件的事件
                if (diff > mTouchSlop) {
                    return true;
                }
                break;
        }
        return super.onInterceptTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_MOVE:
                mXMove = event.getRawX();
                int scrolledX = (int) (mXLastMove - mXMove);
                if (getScrollX() + scrolledX < leftBorder) {
                    scrollTo(leftBorder, 0);
                    return true;
                } else if (getScrollX() + getWidth() + scrolledX > rightBorder) {
                    scrollTo(rightBorder - getWidth(), 0);
                    return true;
                }
                scrollBy(scrolledX, 0);
                mXLastMove = mXMove;
                break;
            case MotionEvent.ACTION_UP:
                // 当手指抬起时,根据当前的滚动值来判定应该滚动到哪个子控件的界面
                int targetIndex = (getScrollX() + getWidth() / 2) / getWidth();
                int dx = targetIndex * getWidth() - getScrollX();
                // 第二步,调用startScroll()方法来初始化滚动数据并刷新界面
                mScroller.startScroll(getScrollX(), 0, dx, 0);
                invalidate();
                break;
        }
        return super.onTouchEvent(event);
    }

    @Override
    public void computeScroll() {
        // 第三步,重写computeScroll()方法,并在其内部完成平滑滚动的逻辑
        if (mScroller.computeScrollOffset()) {
            scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
            invalidate();
        }
    }
}

3.2.2.布局

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/activity_marginlayout_layout"
    android:orientation="vertical">

    <com.wjn.sqlitedemo.view.ScrollerLayout
        android:id="@+id/activity_marginlayout_scrollerlayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

    </com.wjn.sqlitedemo.view.ScrollerLayout>


</RelativeLayout>


3.2.3.Java代码使用

public class MarginLayoutParamsActivity extends AppCompatActivity {

    private ScrollerLayout scrollerLayout;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_marginlayoutparams);

        scrollerLayout= (ScrollerLayout) findViewById(R.id.activity_marginlayout_scrollerlayout);

        scrollerLayout.removeAllViewsInLayout();
        for(int i=0;i<3;i++){
            TextView textView=new TextView(this);
            textView.setBackgroundColor(Color.RED);
            textView.setTextColor(Color.WHITE);
            textView.setClickable(true);
            ViewGroup.LayoutParams layoutParams=new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,300);
            textView.setText("Scroller滑动Demo-"+"TextView-"+(i+1));
            textView.setGravity(Gravity.CENTER);
            textView.setLayoutParams(layoutParams);
            scrollerLayout.addView(textView);
        }

    }

}



猜你喜欢

转载自blog.csdn.net/weixin_37730482/article/details/80649317