1. 程式人生 > >android監聽View載入完成

android監聽View載入完成

最近專案中需要實現一個GridView顯示6*5=30項,並鋪滿整個介面,介面中還有自定義ActionBar等其他控制元件,所以需要獲取剩下螢幕的高度。通過百度得知View有一個監聽函式,親測使用有效,特此記錄,方便日後查閱。

gv_test.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                //給GridView設定Adapter,在adapter的getView中獲取GridView的高度,在這個回撥之前獲取的高度都是0
//處理完後remove掉,至於為什麼,後面有解釋 gv_test.getViewTreeObserver() .removeOnGlobalLayoutListener(this); } });

通過原始碼追溯進去,找到ViewTreeObserver這個類,裡面有很多interface,都是用來追蹤View的各種狀態變化的。
找到OnGlobalLayoutListener

/**
     * Interface definition for a callback to be invoked when the global layout state
     * or the visibility of views within the view tree changes.
     */
public interface OnGlobalLayoutListener { /** * Callback method to be invoked when the global layout state or the visibility of views * within the view tree changes */ public void onGlobalLayout(); }

註釋的大概意思就是這個回撥在佈局狀態和可見狀態發生變化時回撥,所以準確的說,這個不是監聽View的載入完成,而是監聽佈局變化的。
我們來測試一下。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.example.myapplication.MainActivity">

    <Button
        android:onClick="test"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="test"/>

    <TextView
        android:id="@+id/tv_test"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="測試"/>
</LinearLayout>
public class MainActivity extends AppCompatActivity {

    TextView tv_test;
    private static final String TAG = "MainActivity";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

        tv_test = (TextView)findViewById(R.id.tv_test);
        //app切換到後臺,再點開會呼叫一次,螢幕關閉執行程式會呼叫兩次
        tv_test.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                Log.e(TAG, "onGlobalLayout: ");
            }
        });
    }



    public void test(View v){
        //改變可見性,呼叫一次
//        tv_test.setVisibility(View.GONE);
        //改變文字佈局,沒有效果
//        tv_test.setGravity(Gravity.CENTER);

        //修改控制元件大小,呼叫一次
//        LinearLayout.LayoutParams para = (LinearLayout.LayoutParams) tv_test.getLayoutParams();
//        para.height = 200;
//        para.weight = 100;
//        tv_test.setLayoutParams(para);

        //修改layoutgravity,這個是在LayoutParams中,呼叫一次
        LinearLayout.LayoutParams para = (LinearLayout.LayoutParams) tv_test.getLayoutParams();
        para.gravity = Gravity.CENTER_HORIZONTAL;
        tv_test.setLayoutParams(para);
    }
}

執行程式,得到從android monitor中可以看到,啟動後呼叫了三次onGlobalLayout,很奇怪,為什麼是三次?後來有一次螢幕鎖了,發現呼叫了兩次。經過測試,app退到後臺後重新進入會呼叫一次,螢幕鎖屏後重新開啟會呼叫兩次(小米兩次,努比亞1次),其中一次猜測是控制元件的可見性改變了。
通過按鍵的測試,分別修改控制元件的可見性和佈局,都會呼叫一次,修改控制元件內部佈局,不會呼叫,同時修改佈局和可見性,只調用一次。

到此三次之謎依舊沒有解決,不過,可以肯定的是,這個會重複
呼叫多次,使用的時候需要注意。解決的辦法就是第一次回撥後,就把回撥remove掉,如:gv_test.getViewTreeObserver()
.removeOnGlobalLayoutListener(this);

如有錯誤,敬請雅正。