[原] 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也有一定的理解了吧,剩下的就是與其他知識結合再深入研究了。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM