`

Android中Scroller类的分析

 
阅读更多

今天看了一下项目中用到的ViewFlow控件,想弄明白其工作原理。从头开始分析,卡在“滚动”这儿了。

做android也快两年了,连最基本的滚动都不熟悉,真是惭愧。。。遂网上找资料,很容易的在google前排找到此文章:

Android Scroller类的详细分析 http://blog.csdn.net/gemmem/article/details/7321910

很受启发,学习之后总结一下自己的心得。

 

文章中的内容这里就不再重复了。

如文章中所写,在弄明白滚动的原理之前,需要先明白computeScroll() 这个方法。

computeScroll()是View类的一个空函数,在父容器重画自己的孩子时,它会调用孩子的computScroll方法。所以这个computeScroll()函数正是我们大展身手的地方,在这个函数里我们可以去取得事先设置好的成员变量mScroller中的位置信息、速度信息等等,用这些参数来做我们想做的事情。

 

然后放下代码,先考虑几个问题:

1.如何触发滚动?

2.谁要滚动?或者说哪个View要滚动?

3.从哪滚到哪?滚多久?

4.怎么滚动?

 

脑海中有这几个问题之后,好吧,看一下代码,直接拷一下上述作者文章中的,自己格式化稍改了一下:

复制代码
package cn.supersugar.tablelayout;

import android.widget.LinearLayout;
import android.widget.Scroller;
import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.view.View.OnClickListener;

public class TestMyTableLayoutActivity extends Activity {
    private static final String TAG = "TestScrollerActivity";
    LinearLayout lay1, lay2, lay0;
    private Scroller mScroller;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mScroller = new Scroller(this);
        lay1 = new MyLinearLayout(this);
        lay2 = new MyLinearLayout(this);

        lay1.setBackgroundDrawable(getResources().getDrawable(R.drawable.ic_launcher));
        lay2.setBackgroundColor(this.getResources().getColor(
                android.R.color.white));
        lay0 = new ContentLinearLayout(this);
        lay0.setOrientation(LinearLayout.VERTICAL);
        LinearLayout.LayoutParams p0 = new LinearLayout.LayoutParams(
                LinearLayout.LayoutParams.FILL_PARENT,
                LinearLayout.LayoutParams.FILL_PARENT);
        this.setContentView(lay0, p0);

        LinearLayout.LayoutParams p1 = new LinearLayout.LayoutParams(
                LinearLayout.LayoutParams.FILL_PARENT,
                LinearLayout.LayoutParams.FILL_PARENT);
        p1.weight = 1;
        lay0.addView(lay1, p1);
        LinearLayout.LayoutParams p2 = new LinearLayout.LayoutParams(
                LinearLayout.LayoutParams.FILL_PARENT,
                LinearLayout.LayoutParams.FILL_PARENT);
        p2.weight = 1;
        lay0.addView(lay2, p2);
        MyButton btn1 = new MyButton(this);
        MyButton btn2 = new MyButton(this);
        btn1.setText("btn in layout1");
        btn2.setText("btn in layout2");
        btn1.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                mScroller.startScroll(0, 0, -30, -30, 500);
                //开始滚动,设置初始位置--》结束位置 & 持续时间
            }
        });
        btn2.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                mScroller.startScroll(20, 20, -50, -50, 500);
            }
        });
        lay1.addView(btn1);
        lay2.addView(btn2);
    }

    class MyButton extends Button {
        public MyButton(Context ctx) {
            super(ctx);
        }

        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            Log.d(TAG, this.toString() + " onDraw------");
        }
    }

    class MyLinearLayout extends LinearLayout {
        public MyLinearLayout(Context ctx) {
            super(ctx);
        }

        @Override
        public void computeScroll() {
            Log.d(TAG, this.toString() + " computeScroll-----------");
            if (mScroller.computeScrollOffset())//如果mScroller没有调用startScroll,这里将会返回false。
            {
                //因为调用computeScroll函数的是MyLinearLayout实例,
                //所以调用scrollTo移动的将是该实例的孩子,也就是MyButton实例
                scrollTo(mScroller.getCurrX(), 0);
                Log.d(TAG, "getCurrX = " + mScroller.getCurrX());

                //继续让系统重绘
                getChildAt(0).invalidate();
            }
        }
    }

    class ContentLinearLayout extends LinearLayout {
        public ContentLinearLayout(Context ctx) {
            super(ctx);
        }

        @Override
        protected void dispatchDraw(Canvas canvas) {
            Log.d(TAG, "contentview dispatchDraw");
            super.dispatchDraw(canvas);
        }

        @Override
        public void computeScroll() {
        }
    }
}
复制代码

上运行截图:

如上,一一分析这四个问题:

1.按钮的点击事件触发了滚动

3.mScroller.startScroll()方法定义了滚动的路径和时间

 

好,1没问题,3也没问题,问题是2和4。

先看问题2:谁要滚动?

我在看完代码的时候我不知道谁会滚动- -...我猜是layout1,但是注释里写的是bt1!

然后我把代码跑到手机上看,果然是bt1...

可以看到,点击bt1的时候,bt1向右按照原计划向右滚动了。可是computeScroll()方法不是在bt1的父view中重写的吗?

据作者所写:

    //因为调用computeScroll函数的是MyLinearLayout实例,

    //所以调用scrollTo移动的将是该实例的孩子,也就是MyButton

    scrollTo(mScroller.getCurrX(), 0);

然后查看scrollTo的api:

Set the scrolled position of your view. This will cause a call to onScrollChanged(int, int, int, int) and the view will be invalidated.

依然不很理解,所以只好这么认为吧:谁调用了scrollTo,谁的孩子就滚动。

 

换句话说:谁想滚找他爹!!!

 

然后,在layout1中又添加了一个bt3,点击bt1的时候,果然bt3跟bt1一起滚了...那么2就有了答案。

 

再看问题4:怎么滚动?

其实当mScroller.startScroll(0, 0, -30, -30, 500)这局代码执行之后,由于最后一个参数传入的是执行时间,在这个时间范围内,也就是滚动没有执行完的时候,mScroller.computeScrollOffset()返回的都是true!

在这个过程中,mScroller.getCurrX()的值却是一直在变化的,变化的范围你懂的。

然后呢?界面怎么动呢?当然是不断的重绘了!

怎么不断重绘?getChildAt(0).invalidate()就是这句,然后系统会一直重复的执行computeScroll(),直到设定的时间结束,view也会滚动指定的位置,mScroller.computeScrollOffset()返回值变为false,这样就完成了整个滚动过程。

到这儿的时候差不多已经吸收了原作者要给的知识了,再看一个额外的:

每个view都有computeScroll()方法,那么在mScroller.startScroll()发起滚动的时候,ContentLinearLayout 能不能“听到”这个命令?

应该是能的!把MyLinearLayout 中的computeScroll()方法copy至ContentLinearLayout 中,方法同样会执行,那么滚动的就是layout1+layout2了!

试了试确实是这样的,再次验证了谁要滚找他爹!

那么问题4也有了答案。

 

如果这4个问题都有答案的话,那么相信对scroller也有一定的理解了吧,剩下的就是与其他知识结合再深入研究了。

 

 

原文:http://www.cnblogs.com/supersugar/archive/2012/08/13/2636691.html

分享到:
评论

相关推荐

    android Scroller粗暴分析

    剖析侧滑原理,子view 在Scroller 上的滚动原理和实现

    android开发通过Scroller实现过渡滑动效果操作示例

    主要介绍了android开发通过Scroller实现过渡滑动效果,结合实例形式分析了Android Scroller类实现过渡滑动效果的基本原理与实现技巧,需要的朋友可以参考下

    Android开发艺术探索.任玉刚(带详细书签).pdf

    本书是一本Android进阶类书籍,采用理论、源码和实践相结合的方式来阐述高水准的Android应用开发要点。本书从三个方面来组织内容。第一,介绍Android开发者不容易掌握的一些知识点;第二,结合Android源代码和应用层...

    Android开发艺术探索

    2.2 Android中的多进程模式 / 36 2.2.1 开启多进程模式 / 36 2.2.2 多进程模式的运行机制 / 39 2.3 IPC基础概念介绍 / 42 2.3.1 Serializable接口 / 42 2.3.2 Parcelable接口 / 45 2.3.3 Binder / 47 ...

    android开发艺术探索高清完整版PDF

    《Android开发艺术探索》是一本Android进阶类书籍,采用理论、源码和实践相结合的方式来阐述高水准的Android应用开发要点。《Android开发艺术探索》从三个方面来组织内容。第一,介绍Android开发者不容易掌握的一些...

    android群雄传

    第5章 Android Scroll分析 87 5.1 滑动效果是如何产生的 88 5.1.1 Android坐标系 88 5.1.2 视图坐标系 88 5.1.3 触控事件——MotionEvent 89 5.2 实现滑动的七种方法 91 5.2.1 layout方法 92 5.2.2 offset ...

    《Android自定义组件开发详解》

    8.3 Scroller类 264 8.4 平滑滚动的工作原理 271 8.5 案例:触摸滑屏 272 8.5.1 触摸滑屏的技术分析 272 8.5.2 速度跟踪器VelocityTracker 273 8.5.3 触摸滑屏的分步实现 274 8.6 练习作业 285 第九章 侧边栏 287 ...

    札记:android手势识别功能实现(利用MotionEvent)

    虽然android本身是一个完整的系统,它主要运行在移动设备的特性决定了我们在它上面开的app绝大数属于客户端程序,主要目标就是显示界面处理交互,这点和web前端以及桌面上的应用类似。 作为“客

    Android自定义控件简单实现侧滑菜单效果

    侧滑菜单在很多应用中都会见到,最近QQ5.0侧滑还玩了点花样~~对于侧滑菜单,一般大家都会自定义ViewGroup,然后隐藏菜单栏,当手指滑动时,通过Scroller或者不断的改变leftMargin等实现;多少都有点复杂,完成以后还...

    Android 侧滑关闭Activity的实例

    唯一的方法还是自己随手鲁一个~,侧滑这个东西在Android中是比较少见的,iOS是最常见不过了,因为毕竟他们没有物理返回键。还有UIScrollView那些。然而我们用的最多的QQ也只是有个功能,并没有真正的滑动效果。至于...

    Android 自定义ViewPager

    当页面内存在ScrollView这类子控件,事件要正常分发,不允许自定义ViewPager拦截事件。 回弹与切换动画处理。 源码分析 初始化 public ViewPagerY(Context context, AttributeSet attrs, int defStyleAttr) { super...

    Android自定义控件实现可左右滑动的导航条

    因为项目中遇到了一些特殊的定制要求,所以就自己写了一个,这里放出来。  首先来分析下这个控件的功能:  •能够响应左右滑动,并且能响应快速滑动 •选择项和未选择项有不同的样式表现,比如前景色,背景色,...

    Android 仿微信QQ对话列表 水平滑动删除

    该功能我在网上下载的,测试下有个小小的bug。经过分析后修改了。然后分享给大家参考,里面自己添加的备注啥的,仅供参考 。

    Android使用自定义控件HorizontalScrollView打造史上最简单的侧滑菜单

    侧滑菜单在很多应用中都会见到,最近QQ5.0侧滑还玩了点花样~~对于侧滑菜单,一般大家都会自定义ViewGroup,然后隐藏菜单栏,当手指滑动时,通过Scroller或者不断的改变leftMargin等实现;多少都有点复杂,完成以后还...

Global site tag (gtag.js) - Google Analytics