一直对View的滚动了解的不深,说明确了吧也能说出个所以然来,所以我就花了点时间做了一个小小的总结,言归正传,view的滑动分为下面三种:

1)View本身不滚动,指滚动View的内容,这也是View类提供的原始方法。通过scrollTo和ScrollBy方法来实现。

2)使用动画,让View来产生滚动效果

3)通过动态的改动LayoutParams的margin等属性让View来产生滚动

本篇博客就简单的分析一下第一种情况,同一时候本文最后还会简单的提供了一个样例:

View本身就提供了scrollBy和scrollTo方法,当中scrollBy方法又是调用了scrollTo方法:

    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();
}
}
} public void scrollBy(int x, int y) {
scrollTo(mScrollX + x, mScrollY + y);
}

从方法的实现上就能够看出来两者的差别。scrollTo用mScrollX和mScrollY记录了滚动的偏移量。没有滚动的时候mScrollX和mScrollY皆为0,它表明了View的内容在x或者y的方向上滚动动的绝对滚动;而scrollBy在mScrollX或者mScrollY的基础上实现了相对滚动

在深入研究之前先说说为什么scrollTo和scrollBy滚动的是View的内容而非View本身。由于View在ViewGroup中的位置是由LayoutParams的margin等參数决定的。要想滚动View或者说要想改变View的位置仅仅须要改变LayoutParams的相关參数就能够。

可是scrollTo和scrollBy改变的仅仅是mScrollX和mScrollY的值,这两个值对于改变View在ViewGroup里面的位置是毫无关系的;这就排除了scrollTo或者scrollBy滚动的是View本身了,可是又有什么证据证明这两个方法滚动的是View的内容呢?且看这两个变量用在了什么地方就知道了。我们在View的draw方法里面发现了些许痕迹:

   if (background != null) {
final int scrollX = mScrollX;
final int scrollY = mScrollY; 省略了部分代码
if ((scrollX | scrollY) == 0) {
background.draw(canvas);
} else {
canvas.translate(scrollX, scrollY);
background.draw(canvas);
canvas.translate(-scrollX, -scrollY);
}
}

我们知道一个View的显示须要经过測量Measure-->布局Layout-->绘制内容onDraw三个流程的。最后一个流程就是绘制View内容。在哪儿绘制?当然是在画布Canvas上面绘制!

上面的代码能够看出mScrollX和mScrollY这两个变量正式交给显示View内容的Canvase来操作的。所以我们说scrollTo或者scrollBy滚动的是View的内容。而不是改变View在parentView显示的位置关系。当然还须要注意的是假设是ViewGroup或者说parentView自己调动了scrollTo/scrollBy方法,那么viewGroup里面的childView的位置是能够改变的,由于childView本身就是ViewGroup里面的内容,(事实上ScrollView的滚动原理也是如此),这也是左右滑动屏幕实现的基本思路(可參考网上的这篇博客)。

事实上关于mScrollX和mSrcorllY的凝视就说明了这个问题:The offset, in pixels, by which the content of this view is scrolled。!

也就是说mScrollX/xScrollY是相对于“起始位置”在水平/竖直方向的偏移量;结合网上的一些博客资料能够得到例如以下图的关系

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">

也就是说假设向左滚动,mScrollX为正值,反之为负值。假设向上滚动。mScrollY为正值,反之为负值!

尽管我们能够从源代码上看出scrollTo和scrollBy的差别,以下简单的通过样例来直观说一下二者的差别。以下是滚动之前的页面:

上下两个button点击事件的代码为:

/**记录上面button的点击次数**/
private int toCount = 0;
public void scrollToTest(View v) {
v.scrollTo(300, 0);
toCount++;
toTV.setText("" + toCount);
} /**记录羡慕button的点击次数**/
private int byCount = 0;
public void scrollByTest(View v) {
v.scrollBy(100, 0);
byCount ++;
byTV.setText(""+byCount);
}

也即是说上面的button每次点击时向左滚动300的距离,而以下的button每次点击向左滚动100的距离.执行效果例如以下:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="" height="225" width="322">

执行效果发现调用scrollTo的时候不管点击了多少次都不会滚动了,仅仅是在点击第一次的时候才滚动了300.而scrollBy每次滚动在原来的基础上滚动了100,点击三次后达到了与scrollTo一样的效果。所以说scrollTo是一次性滚动到位的绝对滚动,而scrollBy是在前次滚动的基数上继续滚动,逐次滚动到位,是相对滚动!

当然调用scrollBy能够在前面滚动的基础上持续滚动下去,比方上面的那个样例继续点击以下的button。仍然会继续滚动。

写到这儿,突然想起了去年刚接触android的时候有一个关于TextView滚动次数的需求,让TextView跑马灯两次就停止滚动,当时水平有限,没有实现出来,如今倒是能够用srcollTo/scrollBy简单粗糙实现出来了。以下就说说这个控制滚动次数的自己定义TextView的滚动实现效果如图:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">

跑马灯效果图

上图为实现滚动的效果,切且滚动的位置用图A,图B...图G来表明方便下文的描写叙述。。详细执行效果能够文章最后的demo.以下详细先说说实现的核心原理:

1)获取TextView的字符串的宽度:

/**TextView字符串的宽度**/
private int textWidth;
private void measureTextWidth() {
Paint paint = this.getPaint();
String str = this.getText().toString();
textWidth = (int) paint.measureText(str);
} @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        // TODO Auto-generated method stub
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        measureTextWidth();
    }

2)让自己定义的TextView实现Runnable接口,在run方法里完毕滚动的控制,同一时候提供startScroll方法来开启滚动操作。

private int marqueeTemp =0;
    @Override
    public void run() {
        //已经完毕了指定的跑马灯次数
        if(marqueeTemp==marqueeTime) {
            return;
        }
        scrollBy(1,0); //实现跑马灯
        //注意是text字符串的宽度,而不是textView的宽度
        if (getScrollX() >= textWidth) {//假设一轮滚动结束
            marqueeTemp++;//累加滚动次数             //scrollBy实现方式
//            if(marqueeTemp==marqueeTime) {
//                scrollBy(-textWidth,0);
//            }else {
//                scrollBy(-this.getWidth()-textWidth,0);
//            }
            //scrollTo实现方式
            if(marqueeTemp==marqueeTime) {
                scrollTo(0,0);
            }else {
                scrollTo(-this.getWidth(),0);
            }
        
        }
//开启下次滚动
        postDelayed(this, 10);

上面的代码完毕了滚动的主要工作,请对照这上面的效果图来看。在滚动的时候也就是图B或者图E两个过程时候上面的代码调用了scrollBy(1,0)来逐步滚动1,直到滚动到图C或者图F的时候说明完毕了一次滚动。也即是代码中if(getScrollX() >= textWidth)条件成立。

此时滚动次数+1.。

在完毕一次滚动的时候。第二次滚动我们须要让TextView的字符串在图D所在的位置滚动,所以我们须要先让TextView的内容向右滚动到D的位置,然后在向左继续跑马灯。通过对scrollTo和ScrollBy的了解,这两种方式都能够让处于图C状态的内容滚动到图D状态位置:

前提正如前面所说向左移动的时候mScrollX>0.向右mScrollX<0;

1)scrollBy的实现方式:由于scrollBy是逐步滚动。对照着图C和图D,我们仅仅须要让scollBy的向右移动字符串的宽度+TextView的宽度就可以,即上面代码中的scrollBy(-this.getWidth()-textWidth,0);

2)scollTo的实现方式:一步到位的实现方式。由于从源代码上看scrollBy调用了scrollTo(mScroll+x,0),并且完毕一次滚动后mScroll=textWidth(字符串的宽度);所以我们直接调用scollTo(-this.getWidth(),0)就能够让TextView的内容滚动到图D位置。

通过1和2的解说,我们更是能够进一步的理解scrollBy和scrollTo的不同之处!!

!!

另外当滚动指定次数过后须要让TextView的内容回到初始位置,即上图中的图G位置,相同能够用srcollBy和scrollTo两种方式:

1)scrollBy方式:if(marqueeTemp==marqueeTime) {//完毕了指定次数的滚动

              scrollBy(-textWidth,0);

           }

2)scrollTo方式:if(marqueeTemp==marqueeTime) {

                scrollTo(0,0);

            }

原因应该不用多说了,到此为止。本篇博文正式结束,假设有错误的地方。欢迎批评指正,共同学习,最后附上博主的demo下载链接

关于滚动的补充说明见《此博客

View的滚动原理简单解析的更多相关文章

  1. 锐速与BBR的原理简单解析

    锐速与BBR的原理简单解析  4 前言 昨天,有一位朋友在我的文章下留言说,锐速和BBR不都是一样,是拥塞算法嘛.因为这方面需要讲的东西比较多,所以我还是专门水一篇文章吧. 锐速 参考资料: http ...

  2. foreach的使用原理简单解析

    数组可以foreach遍历,这个是在jdk1.5之前就可以的,我也不太清楚是怎么做到的. 后面的List,Set等的foreach都是实现Iterable接口,基于iterator()对象实现的.Fo ...

  3. View Animation 运行原理解析

    Android 平台目前提供了两大类动画,在 Android 3.0 之前,一大类是 View Animation,包括 Tween animation(补间动画),Frame animation(帧 ...

  4. 自己定义 View 基础和原理

    课程背景: 在 Android 提供的系统控件不能满足需求的情况下,往往须要自己开发自己定义 View 来满足需求,可是该怎样下手呢.本课程将带你进入自己定义 View 的开发过程,来了解它的一些原理 ...

  5. Android艺术开发探索第四章——View的工作原理(下)

    Android艺术开发探索第四章--View的工作原理(下) 我们上篇BB了这么多,这篇就多多少少要来点实战了,上篇主席叫我多点自己的理解,那我就多点真诚,少点套路了,老司机,开车吧! 我们这一篇就扯 ...

  6. android多线程-AsyncTask之工作原理深入解析(上)

    关联文章: Android 多线程之HandlerThread 完全详解 Android 多线程之IntentService 完全详解 android多线程-AsyncTask之工作原理深入解析(上) ...

  7. view向上滚动

    之前本来是打算做TextView垂直向上滚动的,后来发现一位大神做得很好,https://github.com/sfsheng0322/MarqueeView 孙福生大神,然后自己要用到多个View向 ...

  8. GBDT算法原理深入解析

    GBDT算法原理深入解析 标签: 机器学习 集成学习 GBM GBDT XGBoost 梯度提升(Gradient boosting)是一种用于回归.分类和排序任务的机器学习技术,属于Boosting ...

  9. 梳理源码中 View 的工作原理

    欢迎Follow我的GitHub, 关注我的掘金. 在View的工作过程中, 执行三大流程完成显示, 测量(measure)流程, 布局(layout)流程, 绘制(draw)流程. 从perform ...

随机推荐

  1. git常用命令符

    全局配置 $ git config --global user.name "姓名" 告诉git你是谁 $ git config --global user.email " ...

  2. 创建 router 连通 subnet

    上一节我们为 Neutron 虚拟路由器配置好了 L3 agent,今天将创建虚拟路由器“router_100_101”,打通 vlan100 和 vlan101. 打开操作菜单 Project -& ...

  3. depletion mosfet 的 depletion 解釋

    Origin mosfet 除了有 n channel 及 p channel 外, 還分為 enhanced 及 depletion 兩種, 引起我注意的是, depletion 代表什麼, Exp ...

  4. BZOJ2748(DP)

    非常简单的DP题.类似背包的操作,按照音量改变值进行状态转移即可. #include <bits/stdc++.h> using namespace std; #define REP(i, ...

  5. OS | 死锁

    死锁的四个条件 互斥 占用等待 非剥夺 循环等待 死锁的解决方案 死锁预防 间接预防:防止前三个条件中的任何一个的发生 直接预防:防止循环等待的发生 死锁避免 进程启动拒绝:不启动任何一个可能发生死锁 ...

  6. fetch API 简单解读

    http://f2e.souche.com/blog/fetch-api-jie-du/?utm_source=tuicool&utm_medium=referral 在我们日常的前端开发中, ...

  7. ETL之Kettle

    Kettle是一款国外开源的ETL工具,纯java编写,可以在Window.Linux.Unix上运行. 说白了就是,很有必要去理解一般ETL工具必备的特性和功能,这样才更好的掌握Kettle的使用. ...

  8. Java分布式服务框架Dubbo初探(待实践)

    Dubbo是什么? Dubbo[]是一个分布式服务框架,致力于提供高性能和透明化的RPC远程服务调用方案,以及SOA服务治理方案. 其核心部分包含: 远程通讯: 提供对多种基于长连接的NIO框架抽象封 ...

  9. centos7.5更换docker-ce镜像源

    更换成阿里云 cd /etc/yum.repos.d/ vim docker-ce.repo # 按ecs进行非编辑模式 :%s/https:\/\/download.docker.com/https ...

  10. CPU、内存、IO虚拟化关键技术及其优化探索

    https://yq.aliyun.com/articles/71295?spm=5176.8091938.0.0.3LQ7NM