`
king_tt
  • 浏览: 2110393 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

Android Scroller类的详细分析

 
阅读更多

尊重原创作者,转载请注明出处:

http://blog.csdn.net/gemmem/article/details/7321910

Scroller这个类理解起来有一定的困难,刚开始接触Scroller类的程序员可能无法理解Scroller和View系统是怎么样联系起来的。我经过自己的学习和实践,对Scroller的用法和工作原理有了一定的理解,在这里和大家分享一下,希望大家多多指教。

首先从源码开始分析:

View.java

      /**
     * Called by a parent to request that a child update its values for mScrollX
     * and mScrollY if necessary. This will typically be done if the child is
     * animating a scroll using a {@link android.widget.Scroller Scroller}
     * object.
     */
    public void computeScroll()
    {
    }



computeScroll是一个空函数,很明显我们需要去实现它,至于做什么,就由我们自己来决定了。

因为View的子类很多,在下面的例子中,我会在一个自定义的类MyLinearLayout中去实现它。

ViewGroup.java

    @Override
    protected void dispatchDraw(Canvas canvas) {

                .......

                .......

                .......

                .......

                for (int i = 0; i < count; i++) {
                final View child = children[i];
                if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null)

                {
                    more |= drawChild(canvas, child, drawingTime);
                }

                .......

                .......

                .......

  }


从dispatchDraw函数可以看出,ViewGroup会对它的每个孩子调用drawChild(),在下面的例子中, ContentLinearLayout的孩子有2个,是2个MyLinearLayout类型的实例。

再看看drawChild函数:

 protected boolean drawChild(Canvas canvas, View child, long drawingTime) {

            ................

            ................

           child.computeScroll();

            ................

            ................

}

看到这里,我想大家应该就明白了,在父容器重画自己的孩子时,它会调用孩子的computScroll方法,也就是说例程中的ContentLinearLayout在调用dispatchDraw()函数时会调用MyLinearLayout的computeScroll方法。

这个computeScroll()函数正是我们大展身手的地方,在这个函数里我们可以去取得事先设置好的成员变量mScroller中的位置信息、速度信息等等,用这些参数来做我们想做的事情。

看到这里大家一定迫不及待的想看代码了,代码如下:

package com.yulongfei.scroller;

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 TestScrollerActivity 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.setBackgroundColor(this.getResources().getColor(android.R.color.darker_gray));
        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, 50);
                }
        });
        btn2.setOnClickListener(new OnClickListener(){
            @Override
            public void onClick(View v) {
                    mScroller.startScroll(20, 20, -50, -50, 50);
                }
        });
        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("MyButton", this.toString() + " onDraw------");
     }
    }
    
    class MyLinearLayout extends LinearLayout
    {
     public MyLinearLayout(Context ctx)
     {
      super(ctx);
     }
     
     @Override
        /**
         * Called by a parent to request that a child update its values for mScrollX
         * and mScrollY if necessary. This will typically be done if the child is
         * animating a scroll using a {@link android.widget.Scroller Scroller}
         * object.
         */
        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("ContentLinearLayout", "contentview dispatchDraw");
      super.dispatchDraw(canvas);
     }
    }
}


对代码做一个简单介绍:

例子中定义了2个MyButton实例btn1和btn2,它们将被其父容器MyLinearLayout实例lay1和lay2通过调用scrollTo来移动。

ContentLinearLayout实例lay0为Activity的contentview,它有2个孩子,分别是lay1和lay2。

mScroller是一个封装位置和速度等信息的变量,startScroll()函数只是对它的一些成员变量做一些设置,这个设置的唯一效果就是导致mScroller.computeScrollOffset() 返回true。

这里大家可能有个疑问,既然startScroll()只是虚晃一枪,那scroll的动态效果到底是谁触发的呢?

后面我将给出答案。

运行程序,我们来看看Log

点击btn1:

点击btn2:


对照Log,我从button被点击开始,对整个绘制流程进行分析,首先button被点击(这里将回答上文的问题),button的背景将发生变化,这时button将调用invalidate()请求重绘,这就是View系统重绘的源头,即scroll动态效果的触发者。与此同时,mScroller.startScroll被调用了,mScroller在此时被设置了一些有效值。

好了,既然重绘请求已发出了,那么整个View系统就会来一次自上而下的绘制了,首先输出的Log就是“contentview dispatchDraw”了,它将绘制需要重绘的孩子(lay1和lay2中的一个),接着会调用drawChild,使得computeScroll函数被触发(drawChild里面会调用child.computeScroll()),于是,lay1或者lay2就会以mScroller的位置信息为依据来调用scrollTo了,它的孩子btn1或者btn2就会被移动了。之后又调用了getChildAt(0).invalidate(),这将导致系统不断重绘,直到startScroll中设置的时间耗尽mScroller.computeScrollOffset()返回false才停下来。

好了,现在整个流程都分析完了,相信大家应该清楚了Scroller类与View系统的关系了吧。理解了Scroller的工作原理,你会发现原来Scroller类并不神秘,甚至有点被动,它除了储存一些数值,什么其他的事情都没做,Scroller类中的一些变量mStartX, mFinalX, mDuration等等的意义也很好理解。

注意:

1、之前有朋友反馈button点击不能移动,这是因为android 4.0默认打开了硬件加速,如果想让button移动,请在AndroidManifest的Application标签或者activity标签中加入:android:hardwareAccelerated="false"

2、代码中的getChildAt(0).invalidate(); 是多余的,可以不写,多谢网友指出这一点。


分享到:
评论

相关推荐

    Android Scroller完全解析

    详见http://blog.csdn.net/huaxun66/article/details/52884998

    scroller类实战案例类似ViewPager滑动功能

    scroller类用法解析和scroller类实战案例类似ViewPager滑动功能

    android Scroller粗暴分析

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

    Android之Scroller(滑动)完全解析

    来自博客,http://blog.csdn.net/footballclub,欢迎留言交流

    Android Scroller及下拉刷新组件原理解析

    Android事件拦截机制 Android中事件的传递和拦截和View树结构是相关联的,在View树中,分为叶子节点和普通节点,普通节点有子节点只能是ViewGroup,叶子节点可以是View或者ViewGroup。Android和事件分发拦截相关的...

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

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

    scrollerDemo

    自己写的关于scroller的一个小demo,感兴趣的朋友可以下载下来看看,也可以查看我的文章产看更多的解析过程: http://blog.csdn.net/Anny_Lin/article/details/50346353

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

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

    Android 利用ViewPager实现类微信的左右滑动效果

    Android利用ViewPager实现类微信的左右滑动效果,详细代码注解解析setOnPageChangeListener各参数意义以及使用方法

    Android开发艺术探索

    第7章 Android动画深入分析 / 265 7.1 View动画 / 265 7.1.1 View动画的种类 / 265 7.1.2 自定义View动画 / 270 7.1.3 帧动画 / 272 7.2 View动画的特殊使用场景 / 273 7.2.1 LayoutAnimation / 273 ...

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

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

    用Scroller完成一个简单的ViewPager

    [ Android视图绘制流程完全解析,带你一步步深入了解View(二)](http://blog.csdn.net/guolin_blog/article/details/16330267) ##事件分发机制 此点可以参考笔者文章: [完全理解android事件分发机制]...

    《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群雄传

    第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 ...

    安卓高级UI培训课程

    自绘控件、继承控件、组合控件、Scroller详解及源码浅析、ViewDragHelper详解及源码浅析、自定义View触摸工具类解析(ViewConfiguration基础参数工具类、VelocityTracker手势速率工具类、GestureDetector手势工具类...

Global site tag (gtag.js) - Google Analytics