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
ListView
的Adapter
,並且呼叫了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()
,只要你的佈局有一個viewId
是android: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
.