1. 程式人生 > >Android程式設計學習筆記 之 ListActivity原始碼詳解

Android程式設計學習筆記 之 ListActivity原始碼詳解

前言

最近在研究PreferenceActivity發現是繼承自ListActivity的,開啟看了下ListActivity的原始碼,發現也不長,就詳細閱讀認識一下。

正文

ListActivity簡單到只要在onCreate()中呼叫setListAdapter()方法就可以實現了。
支援空資料顯示。

點進去我們看到前兩個field很熟悉,就是一個ListView+Adapter
很容易就知道這兩個field就是ListActivity的核心,資料儲存在Adapter中,展示在ListView中。

    protected ListAdapter mAdapter;
    protected
ListView mList; /** 省略部分程式碼 */ public void setListAdapter(ListAdapter adapter) { //加上鎖,防止多個執行緒同時呼叫這個方法 synchronized (this) { ensureList();//內含setContentView的方法 mAdapter = adapter; mList.setAdapter(adapter); } }

在呼叫了setListAdapter()之後,對Adapter

重新賦值,並重新設定ListViewAdapter,並且呼叫了ensureList()方法,重新建立佈局。
點進ensureList()方法。

    private void ensureList() {
        if (mList != null) {
            return;
        }
        setContentView(com.android.internal.R.layout.list_content_simple);
    }

哈!原來每次setListAdapter()都會把Adapter替換掉,並且重新setContentView()

,怪不得ListActivity不用我們操心佈局。
我們再來看看這個佈局的內容

<ListView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@android:id/list"
    android:layout_width="match_parent" 
    android:layout_height="match_parent"
    android:drawSelectorOnTop="false"
    />

也就是說,你可以自己setContentView(),只要你的佈局有一個viewIdandroid:id="@android:id/list"的就行。
看到這你可能會無語,就一個簡單的ListView就完事了?這麼簡單我也會啊!
別急,這只是完成了基本的功能而已,還要開放一些介面給子Activity呼叫。
有了ListView當然要實現各種事件啊。

public void setSelection(int position) {
     mList.setSelection(position);
}
public int getSelectedItemPosition() {
    return mList.getSelectedItemPosition();
}
public long getSelectedItemId() {
    return mList.getSelectedItemId();
}

當然還有點選事件,我們發現有一個點選的監聽器mOnClickListener

private AdapterView.OnItemClickListener mOnClickListener = new AdapterView.OnItemClickListener() {
    public void onItemClick(AdapterView<?> parent, View v, int position, long id)    {
        onListItemClick((ListView)parent, v, position, id);
    }
};
protected void onListItemClick(ListView l, View v, int position, long id) {
    //空方法,交由子類實現
}

既然有監聽器,那又是在哪裡設定了這個監聽器呢?我們發現有一個onContentChanged()方法。

    public void onContentChanged() {
        super.onContentChanged();  //Activity中是空方法
        View emptyView = findViewById(com.android.internal.R.id.empty); //空資料時顯示
        mList = (ListView)findViewById(com.android.internal.R.id.list); //繫結ListView
        if (mList == null) {
            throw new RuntimeException( "Your content must have a ListView whose id attribute is "
                             + "'android.R.id.list'");
        }
        if (emptyView != null) {
            mList.setEmptyView(emptyView);    //多好啊,recyclerView就沒有這個方法
        }
        mList.setOnItemClickListener(mOnClickListener); //設定點選事件
        if (mFinishedStart) {
            // 這個if不知道有什麼用意
            setListAdapter(mAdapter);
        }
        mHandler.post(mRequestFocus); //待會解釋
        mFinishedStart = true;
    }

可是翻遍原始碼也沒發現哪裡呼叫了onContentChanged()
從字面上看,onContentChanged()Content改變時的回撥介面,等等,Content
我們不是在ensureList()中呼叫了setContentView()方法嗎?
答案是正確的,在呼叫了setContentView(),會呼叫onContentChanged()

// Activity的setContentView()呼叫Window(具體子類是PhoneWindow)的setContentView()方法
// 以下程式碼出現在PhoneWindow中
@Override  
public void setContentView(int layoutResID) {  
    /** 省略部分程式碼 */
    mLayoutInflater.inflate(layoutResID, mContentParent);  
    getCallback().onContentChanged();  //注意這裡!!!
}

再解釋下,上面的mHandler.post(mRequestFocus);

    private Runnable mRequestFocus = new Runnable() {
        public void run() {
            // 用來使ListView獲取焦點。
            mList.focusableViewAvailable(mList);
        }
    };
   protected void onDestroy() {
        // Activity銷燬時移除
        mHandler.removeCallbacks(mRequestFocus);
        super.onDestroy();
    }

至此,ListActivity已經認識的差不多了。有什麼講錯了希望能有大牛來提點一下。
有興趣的可以自己用recyclerView替換listView實現一個RecyclerActivity.