>>分享Android开发相关的技术 书籍支持  卫琴直播  品书摘要  在线测试  资源下载  联系我们
发表一个新主题 开启一个新投票 回复文章 您是本文章第 20101 个阅读者 刷新本主题
 * 贴子主题:  Scroll的原理和简单使用 回复文章 点赞(0)  收藏  
作者:flybird    发表时间:2024-05-01 11:09:14     消息  查看  搜索  好友  邮件  复制  引用

Scroller是Android中处理滑动效果的核心类。它通过配合View所提供的scrollTo()和scrollBy()这两个方法,就能够使用多种多样的滑动效果。网上也有很多文章介绍关于Scroller的使用,那么在这篇文章中,我尽可能缩减篇幅将Scroller的原理和实现介绍一下。

1. scrollTo()和scrollBy()
首先介绍一下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) {    
  if (mScrollX != x || mScrollY != y) {        
    int oldX = mScrollX;        
    int oldY = mScrollY;        
    mScrollX = x;        
    mScrollY = y;        
    invalidateParentCaches();        
    onScrollChanged(mScrollX, mScrollY, oldX, oldY);        
    if (!awakenScrollBars()) {            
        postInvalidateOnAnimation();        
    }    
  }  
}

scrollTo()是让当前的View能够平移到所给定的位置,然后通过起始位置和结束位置的坐标,根据onScrolledChanged()方法的调用最终完成绘制。


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

而scrollBy()方法的实现是根据scrollTo()而完成的,唯一的区别在于scrollBy()是根据当前已经位移的坐标为基础再继续进行位移。也就是说scrollTo()实现的是输入参数的绝对滑动,而scrollBy()实现的是输入参数的相对滑动。

另外,scrollTo()和scrollBy()方法中都提到了mScrollX和mScrollY两个属性,这两个属性可以分别通过getScrollX()和getScrollY()获得。

点击在新窗口中浏览原图
CTRL+鼠标滚轮放大或缩小

如上图,我们得到以下结论:
(1)View的左边界位于View内容左边界的左侧时,mScrollX < 0;反之,mScrollX > 0。
(2)View的上边界超过View内容上边界时,mScrollY < 0;反之,mScrollY > 0。

2. Scroller的滑动偏移计算
通过阅读Scroller的源码我们发现,Scroller虽然作为View滑动的重要依据,但是却不能真正操纵View滑动,他是通过旧的坐标值和新的坐标值算出偏移距离,配合View的computeScroll()方法完成滑动操作。


/**
* Start scrolling by providing a starting point, the distance to travel,  
* and the duration of the scroll.
*  
* @param startX Starting horizontal scroll offset in pixels. Positive
*        numbers will scroll the content to the left.
* @param startY Starting vertical scroll offset in pixels. Positive numbers
*        will scroll the content up.
* @param dx Horizontal distance to travel. Positive numbers will scroll the
*        content to the left.
* @param dy Vertical distance to travel. Positive numbers will scroll the
*        content up.
* @param duration Duration of the scroll in milliseconds.
*/

public void startScroll(int startX, int startY, int dx, int dy, int duration) {
    mMode = SCROLL_MODE;    
    mFinished = false;    
    mDuration = duration;    
    mStartTime = AnimationUtils.currentAnimationTimeMillis();
    mStartX = startX;    
    mStartY = startY;    
    mFinalX = startX + dx;    
    mFinalY = startY + dy;    
    mDeltaX = dx;    
    mDeltaY = dy;    
    mDurationReciprocal = 1.0f / (float) mDuration;
}

startX - 起始滑动的X坐标
startY - 起始滑动的Y坐标
dx - X方向上的滑动偏移距离
dy - Y方向上的滑动偏移距离
duration - 完成滑动的时间

通过代码我们可以看出,Scroller的startScroll()方法作为View的滑动依据只是单纯的对于变量进行和一系列的赋值操作。

3. 实现滑动效果
通过上面的startScroll()方法的分析,我们了解到该方法只是针对View滑动偏移的计算和赋值工作,并没有真正的执行滑动效果。而真正实现滑动的是View类的computeScroll()方法完成的。

在源码中,我们可以看到View类的computeScroll()方法只是一个空实现,因此我们要完成滑动效果需要复写此方法。如下图:
点击在新窗口中浏览原图
CTRL+鼠标滚轮放大或缩小

在方法中,我们利用Scroller的computeScrollOffset()方法去判断是否View有滑动事件的发生,如果有,即可通过scrollTo()方法完成滑动偏移。




   /**
     * Call this when you want to know the new location.  If it returns true,
     * the animation is not yet finished.
     */

    public boolean computeScrollOffset() {
        if (mFinished) {
            return false;
        }

        int timePassed = (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime);
    
        if (timePassed < mDuration) {
            switch (mMode) {
            case SCROLL_MODE:
                final float x = mInterpolator.getInterpolation(timePassed * mDurationReciprocal);
                mCurrX = mStartX + Math.round(x * mDeltaX);
                mCurrY = mStartY + Math.round(x * mDeltaY);
                break;
            case FLING_MODE:
                final float t = (float) timePassed / mDuration;
                final int index = (int) (NB_SAMPLES * t);
                float distanceCoef = 1.f;
                float velocityCoef = 0.f;
                if (index < NB_SAMPLES) {
                    final float t_inf = (float) index / NB_SAMPLES;
                    final float t_sup = (float) (index + 1) / NB_SAMPLES;
                    final float d_inf = SPLINE_POSITION[index];
                    final float d_sup = SPLINE_POSITION[index + 1];
                    velocityCoef = (d_sup - d_inf) / (t_sup - t_inf);
                    distanceCoef = d_inf + (t - t_inf) * velocityCoef;
                }

                mCurrVelocity = velocityCoef * mDistance / mDuration * 1000.0f;
                
                mCurrX = mStartX + Math.round(distanceCoef * (mFinalX - mStartX));
                // Pin to mMinX <= mCurrX <= mMaxX
                mCurrX = Math.min(mCurrX, mMaxX);
                mCurrX = Math.max(mCurrX, mMinX);
                
                mCurrY = mStartY + Math.round(distanceCoef * (mFinalY - mStartY));
                // Pin to mMinY <= mCurrY <= mMaxY
                mCurrY = Math.min(mCurrY, mMaxY);
                mCurrY = Math.max(mCurrY, mMinY);

                if (mCurrX == mFinalX && mCurrY == mFinalY) {
                    mFinished = true;
                }

                break;
            }
        }
        else {
            mCurrX = mFinalX;
            mCurrY = mFinalY;
            mFinished = true;
        }
        return true;
    }

4. invalidate()
computeScroll()方法并非凭空实现的,它需要调用invalidate()去进行View的重新绘制,invalidate()的源码中有对于computeScroll()方法的调用。

网上关于Scroller的源码讲解也有很多,我也是借鉴别人的列子通过自己的理解完成的本文,因此不能完全说的上是原创。也希望大家能够理解。


小结,通过Scroller我们可以实现很多弹性滑动的效果。
-------------------------------------------------------
作者:NeroIsNovice
原文链接:https://www.jianshu.com/p/3cc780dc26b5






程序猿的技术大观园:www.javathinker.net


程序猿的技术大观园:www.javathinker.net
  Java面向对象编程-->输入与输出(上)
  JavaWeb开发-->JSP技术详解(Ⅱ)
  JSP与Hibernate开发-->映射组成关系
  Java网络编程-->安全网络通信
  精通Spring-->Vue组件开发基础
  Vue3开发-->绑定CSS样式
  Android面试题汇总
  Android基础之用Eclipse搭建Android开发环境和创建第一个And...
  Android中Activity之间的通信
  Android开发实践:用脚本编译Android工程
  Android静默安装的实现
  Android多线程及异步处理问题
  Android开发实践:编译VLC-for-Android
  Android平台概述
  Android多屏幕适配
  Android开发实践:Android.mk模板
  android使用工具性能优化
  Android Service学习之AIDL, Parcelable和远程服务-学习Andr...
  Android 代码混淆技术
  Android中使用Activity管理类
  Appbarlayout+Recycleview滑动效果颜色渐变
  更多...
 IPIP: 已设置保密
树形列表:   
1页 0条记录 当前第1
发表一个新主题 开启一个新投票 回复文章


中文版权所有: JavaThinker技术网站 Copyright 2016-2026 沪ICP备16029593号-2
荟萃Java程序员智慧的结晶,分享交流Java前沿技术。  联系我们
如有技术文章涉及侵权,请与本站管理员联系。