zoukankan      html  css  js  c++  java
  • Android Scroller及实际使用

    什么是Scroller

    我们知道View中有两个方法可以实现滚动/位置变化,scrollTo/scrollBy

    /**
         * Set the scrolled position of your view. This will cause a call to
         * {@link #onScrollChanged(int, int, int, int)} and the view will be
         * invalidated.
         * @param x the x position to scroll to
         * @param y the y position to scroll to
         */
        public void scrollTo(int x, int y) `
    
    /**
         * Move the scrolled position of your view. This will cause a call to
         * {@link #onScrollChanged(int, int, int, int)} and the view will be
         * invalidated.
         * @param x the amount of pixels to scroll by horizontally
         * @param y the amount of pixels to scroll by vertically
         */
        public void scrollBy(int x, int y) {
            scrollTo(mScrollX + x, mScrollY + y);
        }
    
    

    那为什么view已经有了滚动的能力还要一个Scroller去单独实现一套:

    1. 自带的效果比较生硬(瞬间完成),没有中间的过度效果,所以需要scroller

    Sroller 使用

    郭霖老师文章里的使用:

    public class ScrollerLayout extends ViewGroup {
    
        private static final String TAG = "ScrollerLayout";
    
        /**
         * 用于完成滚动操作的实例
         */
        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);
            Log.d(TAG, "ScrollerLayout: default=" + ViewConfigurationCompat.getScaledPagingTouchSlop(configuration)
                    + " mTouchSlop=" + mTouchSlop);
        }
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            Log.d(TAG, "onMeasure: widthMeasureSpec=" + widthMeasureSpec + " heightMeasureSpec=" + 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();
    
                Log.d(TAG, "onLayout: changed=" + changed + "   l=" + l + " t=" + t + " r=" + r + " b=" + b
                        + " leftBorder=" + leftBorder + "  rightBorder=" + rightBorder);
            }
        }
    
        @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) {
            Log.d(TAG, "onTouchEvent: event=" + event.getAction());
            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() {
            Log.d(TAG, "computeScroll: ");
            // 第三步,重写computeScroll()方法,并在其内部完成平滑滚动的逻辑
            if (mScroller.computeScrollOffset()) {
                scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
                invalidate();
            }
        }
    }
    

    布局文件

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        tools:context="com.example.guolin.scrollertest.MainActivity">
        <Button
            android:id="@+id/scroll_to_btn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="scrollTo"/>
        <Button
            android:id="@+id/scroll_by_btn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="scrollBy"/>
    </LinearLayout>
    
    1. TouchSlop是做什么用的
      view跟随手指移动的最小距离,小于这个距离,view不会跟随手指移动

    Scroller 原理

    Scroller 原理

    Scroller 使用场景

    参考

    http://blog.csdn.net/guolin_blog/article/details/48719871

    梦想不是浮躁,而是沉淀和积累
  • 相关阅读:
    linux 下使用scp命令传输文件
    yii2使用vendor文件夹下的的css文件
    yii2中使用定义在 params.php文件中的配置
    PHP Catchable fatal error: Argument 2 passed to IlluminateRoutingUrlGenerator::__construct()
    git 去除本地所有没有保存的修改
    学习修复Laravel The only supported ciphers are AES-128-CBC and AES-256-CBC
    [190308]Ubuntu 安装完之后,安装的软件小记
    swagger.yaml转换为swagger.json文件
    Linux sed -i 字符串替换
    [笔记]Laravel TDD 胡乱记录
  • 原文地址:https://www.cnblogs.com/NeilZhang/p/15055324.html
Copyright © 2011-2022 走看看