Scroller
1.在android 当中实现滑动功能的时候往往是要用来Scroller这个类的,但这个类的运行方式很特别,它不是直接和要滚动view绑定在一起的,而是通过子view和父view这层关系来发挥作用的
来看看api是怎么说Scroller的:
This class encapsulates scrolling. The duration of the scroll can be passed in the constructor and specifies the maximum time that the scrolling animation should take. Past this time,
the scrolling is automatically moved to its final stage and computeScrollOffset() will always return false to indicate that scrolling is over
其中包含的一些方法:
主要用到的方法是:
computeScrollOffset():当view在滚动的时候返回的是true,滚动完成后返回的是false, 就是通过这个方法来判断view的滚动是否完成的
还有一些就是用于获取当前滚动view的x 和 y的坐标的
startScroll((int startX, int startY, int dx, int dy, int duration):
设定view的滚动参数,
startX : X 的起始位置。
startY : Y 的起始位置。
dx : 水平偏移距离
dy : 垂直偏移距离
duration : 到达指定距离所需要的时间
3.要想利用Scroller这个类就必须要了解的一个重要方法:
1).computeScroll()
1 /** 2 * Called by a parent to request that a child update its values for mScrollX 3 * and mScrollY if necessary. This will typically be done if the child is 4 * animating a scroll using a {@link android.widget.Scroller Scroller} 5 * object. 6 */ 7 public void computeScroll() 8 { 9 }
这是一个空函数,就需要我们去实现它,对它的实现就设定了对view的怎样滚动进行实现。
还有个android源代码中的方法:
2).dispatchDraw()
1 @Override 2 protected void dispatchDraw(Canvas canvas) { 3 4 5 for (int i = 0; i < count; i++) { 6 final View child = children[i]; 7 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) 8 9 { 10 more |= drawChild(canvas, child, drawingTime); 11 }
从这段代码可以知道,父view会调用drawChild对每个子view进行绘制。
在下面的例子中, ContentLinearLayout的孩子有2个,是2个MyLinearLayout类型的实例
3).drawChild对子view进行绘制
1 protected boolean drawChild(Canvas canvas, View child, long drawingTime) { 2 3 ................ 4 5 ................ 6 7 child.computeScroll(); 8 9 ................ 10 11 ................ 12 13 }
在父容器重画自己的孩子时,它会调用孩子的 computScroll方法,也就是说例程中的ContentLinearLayout在调用dispatchDraw()函数时会调用 MyLinearLayout的computeScroll方法。
这个computeScroll()函数正是我们大展身手的地方,在这个函数里我们可以去取得事先设置好的成员变量mScroller中的位置信息、速度信息等等,用这些参数来做我们想做的事情。
4.下面就通过一个例子来说讲解Scroller:
1 public class MainActivity extends Activity { 2 3 LinearLayout lay1, lay2, lay0, lay3, lay4; 4 private Scroller mScroller; 5 6 @Override 7 public void onCreate(Bundle savedInstanceState) { 8 super.onCreate(savedInstanceState); 9 mScroller = new Scroller(this); 10 lay1 = new MyLinearLayout(this); 11 lay2 = new MyLinearLayout(this); 12 13 lay1.setBackgroundColor(this.getResources().getColor( 14 android.R.color.darker_gray)); 15 lay2.setBackgroundColor(this.getResources().getColor( 16 android.R.color.white)); 17 lay0 = new ContentLinearLayout(this); 18 lay0.setOrientation(LinearLayout.VERTICAL); 19 LinearLayout.LayoutParams p0 = new LinearLayout.LayoutParams( 20 LinearLayout.LayoutParams.MATCH_PARENT, 21 LinearLayout.LayoutParams.MATCH_PARENT); 22 this.setContentView(lay0, p0); 23 24 LinearLayout.LayoutParams p1 = new LinearLayout.LayoutParams( 25 LinearLayout.LayoutParams.MATCH_PARENT, 26 LinearLayout.LayoutParams.MATCH_PARENT); 27 p1.weight = 1; 28 lay0.addView(lay1, p1); 29 LinearLayout.LayoutParams p2 = new LinearLayout.LayoutParams( 30 LinearLayout.LayoutParams.MATCH_PARENT, 31 LinearLayout.LayoutParams.MATCH_PARENT); 32 p2.weight = 1; 33 lay0.addView(lay2, p2); 34 MyButton btn1 = new MyButton(this); 35 MyButton btn2 = new MyButton(this); 36 MyButton btn3 = new MyButton(this); 37 btn1.setText("btn in layout1"); 38 btn2.setText("btn in layout2"); 39 btn3.setText("btn in layout3"); 40 41 btn1.setOnClickListener(new OnClickListener() { 42 @Override 43 public void onClick(View v) { 44 mScroller.startScroll(0, 0, -30, -30, 50); 45 } 46 }); 47 btn2.setOnClickListener(new OnClickListener() { 48 @Override 49 public void onClick(View v) { 50 mScroller.startScroll(20, 20, -500, -500, 500); 51 } 52 }); 53 lay1.addView(btn1); 54 lay1.addView(btn3); 55 lay2.addView(btn2); 56 } 57 58 class MyButton extends Button { 59 public MyButton(Context ctx) { 60 super(ctx); 61 } 62 63 @Override 64 protected void onDraw(Canvas canvas) { 65 super.onDraw(canvas); 66 System.out.println("MyButton ====" + this.toString() 67 + " onDraw------"); 68 69 } 70 } 71 72 class MyLinearLayout extends LinearLayout { 73 public MyLinearLayout(Context ctx) { 74 super(ctx); 75 } 76 77 @Override 78 /** 79 * Called by a parent to request that a child update its values for mScrollX 80 * and mScrollY if necessary. This will typically be done if the child is 81 * animating a scroll using a {@link android.widget.Scroller Scroller} 82 * object. 83 */ 84 public void computeScroll() { 85 86 System.out.println(this.toString() + " computeScroll-----------"); 87 88 if (mScroller.computeScrollOffset())// 如果mScroller没有调用startScroll,这里将会返回false。 89 { 90 // 因为调用computeScroll函数的是MyLinearLayout实例, 91 // 所以调用scrollTo移动的将是该实例的孩子,也就是MyButton实例 92 scrollTo(mScroller.getCurrX(), 0); // 意思是儿子的view的怎么变化去找老子computeScroll函数里面的设置 93 System.out.println("getCurrX = " + mScroller.getCurrX()); 94 95 // 当点击lay1中的btn in layout1就会通知系统重绘制,点击btn in layout3的时候就只绘制一次的 96 getChildAt(0).invalidate(); 97 } 98 } 99 } 100 101 class ContentLinearLayout extends LinearLayout { 102 public ContentLinearLayout(Context ctx) { 103 super(ctx); 104 } 105 106 public void computeScroll() { 107 System.out.println("ContentLinearLayout中的" 108 + " computeScroll-----------"); 109 } 110 111 @Override 112 protected void dispatchDraw(Canvas canvas) { 113 System.out.println("contentview dispatchDraw"); 114 super.dispatchDraw(canvas); 115 } 116 117 } 118 119 }
运行后UI 的效果是:
5.现在就分别对Button进行点击,来进行说明:
1).当点击btn in layout1的时候
2)当点击btn in layout2的时候(太多了就截取了一部分)
3)当点击btn in layout3的时候:
现在对上面的结果进行一下分析:当btn in layout1的时候首先是调用ContentLinearLayout里面computScroll,然后再调用ConentLinearLayout的dispatchDraw进行子View的
分发绘制。btn in layout1在lay1中则调用lay1中的computScroll方法,调用完这个方法后继而执行在lay1中子view的onDraw进行绘制。 因为lay1的computScroll里面的getChildAt(0).invalidate(); 让点击btn in layout1时候通知系统重绘制,而点击btn in layout3的时候没哟通知系统重绘制,所以从上面点击显示结果中btn in layout3的结果比btn in layout1的结果少很多。点击btn in layout2的理解和上面是一样的
引用别人的一段话:
既然重绘请求已发出了,那么整个View系统就会来一次自上而下的绘制了,首先输出的Log 就是“contentview dispatchDraw”了,它将绘制需要重绘的孩子(lay1和lay2中的一个),接着会调用drawChild,使得computeScroll 函数被触发(drawChild里面会调用child.computeScroll()),于是,lay1或者lay2就会以mScroller的位置信 息为依据来调用scrollTo了,它的孩子btn1或者btn2就会被移动了。之后又调用了getChildAt(0).invalidate(),这 将导致系统不断重绘,直到startScroll中设置的时间耗尽mScroller.computeScrollOffset()返回false才停下
如果你想控制lay1和lay2的移动可以在ContentLinearout的computScroll中利用scrollTo()进行移动
思想总结:1.绘制是至上而下的 2.要移动子View就要在父view的computScroll中写方法。
代码下载:代码