android 最新消息滚动,Android 滚动操作Scroller类详解
Scroller這個(gè)類(lèi)理解起來(lái)有一定的困難,剛開(kāi)始接觸Scroller類(lèi)的程序員可能無(wú)法理解Scroller和View系統(tǒng)是怎么樣聯(lián)系起來(lái)的。我經(jīng)過(guò)自己的學(xué)習(xí)和實(shí)踐,對(duì)Scroller的用法和工作原理有了一定的理解,在這里和大家分享一下,希望大家多多指教。
首先從源碼開(kāi)始分析:
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 [email?protected] android.widget.Scroller Scroller}
* object.
*/
public void computeScroll()
{
}
computeScroll是一個(gè)空函數(shù),很明顯我們需要去實(shí)現(xiàn)它,至于做什么,就由我們自己來(lái)決定了。
因?yàn)閂iew的子類(lèi)很多,在下面的例子中,我會(huì)在一個(gè)自定義的類(lèi)MyLinearLayout中去實(shí)現(xiàn)它。
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函數(shù)可以看出,ViewGroup會(huì)對(duì)它的每個(gè)孩子調(diào)用drawChild(), 在下面的例子中, ContentLinearLayout的孩子有2個(gè),是2個(gè)MyLinearLayout類(lèi)型的實(shí)例。
再看看drawChild函數(shù):protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
................
................
child.computeScroll();
................
................
}
看到這里,我想大家應(yīng)該就明白了,在父容器重畫(huà)自己的孩子時(shí),它會(huì)調(diào)用孩子的computScroll方法,也就是說(shuō)例程中的ContentLinearLayout在調(diào)用dispatchDraw()函數(shù)時(shí)會(huì)調(diào)用MyLinearLayout的computeScroll方法。
這個(gè)computeScroll()函數(shù)正是我們大展身手的地方,在這個(gè)函數(shù)里我們可以去取得事先設(shè)置好的成員變量mScroller中的位置信息、速度信息等等,用這些參數(shù)來(lái)做我們想做的事情。
看到這里大家一定迫不及待的想看代碼了,代碼如下: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 [email?protected] android.widget.Scroller Scroller}
* object.
*/
public void computeScroll()
{
Log.d(TAG, this.toString() + " computeScroll-----------");
if (mScroller.computeScrollOffset())//如果mScroller沒(méi)有調(diào)用startScroll,這里將會(huì)返回false。
{
//因?yàn)檎{(diào)用computeScroll函數(shù)的是MyLinearLayout實(shí)例,
//所以調(diào)用scrollTo移動(dòng)的將是該實(shí)例的孩子,也就是MyButton實(shí)例
scrollTo(mScroller.getCurrX(), 0);
Log.d(TAG, "getCurrX = " + mScroller.getCurrX());
//繼續(xù)讓系統(tǒng)重繪
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);
}
}
}
對(duì)代碼做一個(gè)簡(jiǎn)單介紹:
例子中定義了2個(gè)MyButton實(shí)例btn1和btn2,它們將被其父容器MyLinearLayout實(shí)例lay1和lay2通過(guò)調(diào)用scrollTo來(lái)移動(dòng)。
ContentLinearLayout實(shí)例lay0為Activity的contentview,它有2個(gè)孩子,分別是lay1和lay2。
mScroller是一個(gè)封裝位置和速度等信息的變量,startScroll()函數(shù)只是對(duì)它的一些成員變量做一些設(shè)置,這個(gè)設(shè)置的唯一效果就是導(dǎo)致mScroller.computeScrollOffset() 返回true。
這里大家可能有個(gè)疑問(wèn),既然startScroll()只是虛晃一槍,那scroll的動(dòng)態(tài)效果到底是誰(shuí)觸發(fā)的呢?
后面我將給出答案。
運(yùn)行程序,我們來(lái)看看Log
點(diǎn)擊btn1:
點(diǎn)擊btn2:
對(duì)照Log,我從button被點(diǎn)擊開(kāi)始,對(duì)整個(gè)繪制流程進(jìn)行分析,首先button被點(diǎn)擊(這里將回答上文的問(wèn)題),button的背景將發(fā)生變化,這時(shí)button將調(diào)用invalidate()請(qǐng)求重繪,這就是View系統(tǒng)重繪的源頭,即scroll動(dòng)態(tài)效果的觸發(fā)者。與此同時(shí),mScroller.startScroll被調(diào)用了,mScroller在此時(shí)被設(shè)置了一些有效值。
好了,既然重繪請(qǐng)求已發(fā)出了,那么整個(gè)View系統(tǒng)就會(huì)來(lái)一次自上而下的繪制了,首先輸出的Log就是“contentview dispatchDraw”了,它將繪制需要重繪的孩子(lay1和lay2中的一個(gè)),接著會(huì)調(diào)用drawChild,使得computeScroll函數(shù)被觸發(fā)(drawChild里面會(huì)調(diào)用child.computeScroll()),于是,lay1或者lay2就會(huì)以mScroller的位置信息為依據(jù)來(lái)調(diào)用scrollTo了,它的孩子btn1或者btn2就會(huì)被移動(dòng)了。之后又調(diào)用了getChildAt(0).invalidate(),這將導(dǎo)致系統(tǒng)不斷重繪,直到startScroll中設(shè)置的時(shí)間耗盡mScroller.computeScrollOffset()返回false才停下來(lái)。
好了,現(xiàn)在整個(gè)流程都分析完了,相信大家應(yīng)該清楚了Scroller類(lèi)與View系統(tǒng)的關(guān)系了吧。理解了Scroller的工作原理,你會(huì)發(fā)現(xiàn)原來(lái)Scroller類(lèi)并不神秘,甚至有點(diǎn)被動(dòng),它除了儲(chǔ)存一些數(shù)值,什么其他的事情都沒(méi)做,Scroller類(lèi)中的一些變量mStartX, mFinalX, mDuration等等的意義也很好理解。
總結(jié):
一、mScroller.startScroll 并不會(huì)導(dǎo)致 View 立即進(jìn)行scroll,它只會(huì)導(dǎo)致當(dāng)前 View 無(wú)效,從而重新繪制,在 View 被它的parent View 調(diào)用繪制的時(shí)候,它的 computeScroll 函數(shù)會(huì)被調(diào)用,所以會(huì)在computeScroll這個(gè)函數(shù)中讓 View 調(diào)用 scrollTo 函數(shù)進(jìn)行實(shí)際的移動(dòng)。
mScroller 的純粹是一個(gè)Model,根據(jù)Animation提供數(shù)據(jù)而已,如果希望改變滾動(dòng)效果,例如快慢,回彈等,控制mScroller ,再讀取它的數(shù)據(jù)處理界面.
二、scoller只是封裝了將要滾動(dòng)的操作,并不是立即執(zhí)行的,執(zhí)行了startScroll方法后,調(diào)用了父控件的computeScroll方法來(lái)執(zhí)行的滾動(dòng)操作,并且滾動(dòng)并不是按鈕的滾動(dòng),而是布局滾動(dòng),那么里面的所有子元素也會(huì)跟著滾動(dòng)
總結(jié)
以上是生活随笔為你收集整理的android 最新消息滚动,Android 滚动操作Scroller类详解的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Linux+php+memcache+A
- 下一篇: 智齿客服Android集成流程,一种基于