标签:
最近写代码总是用到ListActivity,因此看了下ListView和ListActivity的源代码.
一般来说我们使用自定义的ListActivity时,在创建时首先需要调用setContentView函数来设置这个Activity对应的view布局.
而从Activity.onContentChanged()的api文档说明来看,setContentView方法会导致onContentChanged方法被调用.因此我们接下来来看下ListActivity的onContentChanged方法.
public void onContentChanged() {
super.onContentChanged();
View emptyView = findViewById(com.android.internal.R.id.empty);
mList = (ListView)findViewById(com.android.internal.R.id.list);
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);
}
mList.setOnItemClickListener(mOnClickListener);
if (mFinishedStart) {
setListAdapter(mAdapter);
}
mHandler.post(mRequestFocus);
mFinishedStart = true;
}
我们可以看到这里取了两个固定ID的view,一个是R.id.empty,一个是R.id.list.
R.id.list对应Activity中的listview,这也是为什么官方文档(参看前一篇文档)中写道”确保你自定义的Layout包含一个id为”@android:id/list”的ListView”.我们也可以看到如果系统取不到R.id.list对应的view,会直接抛出异常.得到的这个view将保存在mList中.
而R.id.empty在官方文档中也有提到,这view将会在list view为空时显示.
因为mList刚刚获取得到,因此此时肯定为空,一旦我们得到这个empty view,立刻调用ListView的setEmptyView函数:
public void setEmptyView(View emptyView) {
mEmptyView = emptyView;
// If not explicitly specified this view is important for accessibility.
if (emptyView != null
&& emptyView.getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
emptyView.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
}
final T adapter = getAdapter();
final boolean empty = ((adapter == null) || adapter.isEmpty());
updateEmptyStatus(empty);
}
这个时候adapter尚未设置所以肯定为空,所以empty为true.
updateEmptyStatus主要是设置一些可见性之类的参数.
接下来onContentChanged函数调用了ListView的setOnItemClickListener方法注册每个item的点击回调监听类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);
}
};
我们看到这里调用了方法onListItemClick,这也就是为什么我们希望在自定义的ListActivity中监听每个item的点击方法时,必须重载父类中的onListItemClick方法.
回到ListActivity的onContentChanged函数中来,mFinishedStart参数起到了这样一个作用.
当这个Activity被最初创建时,mFinishedStart值为false.因此这个时候自然也不需要设置adapter.而在onContentChanged函数被调用过一次之后, mFinishedStart变为true.这保证了在后面已经设置了adapter之后,一旦内容变化,adapter不需要再次被用户手动设置.
最后mHandler.post(mRequestFocus);通知父类mList可以获得焦点.至此,setContentView方法结束.
后面我们使用ListActivity一般就会调用方法setListAdapter,设置对应的adapter用于给ListView提供数据.这个函数涉及到了ListView和Adapter交互的过程,我们将在下一章节详细讲述.
public void setAdapter(ListAdapter adapter) {
if (mAdapter != null && mDataSetObserver != null) {
mAdapter.unregisterDataSetObserver(mDataSetObserver);
}
resetList();
mRecycler.clear();
if (mHeaderViewInfos.size() > 0|| mFooterViewInfos.size() > 0) {
mAdapter = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, adapter);
} else {
mAdapter = adapter;
}
mOldSelectedPosition = INVALID_POSITION;
mOldSelectedRowId = INVALID_ROW_ID;
// AbsListView#setAdapter will update choice mode states.
super.setAdapter(adapter);
if (mAdapter != null) {
mAreAllItemsSelectable = mAdapter.areAllItemsEnabled();
mOldItemCount = mItemCount;
mItemCount = mAdapter.getCount();
checkFocus();
mDataSetObserver = new AdapterDataSetObserver();
mAdapter.registerDataSetObserver(mDataSetObserver);
mRecycler.setViewTypeCount(mAdapter.getViewTypeCount());
int position;
if (mStackFromBottom) {
position = lookForSelectablePosition(mItemCount - 1, false);
} else {
position = lookForSelectablePosition(0, true);
}
setSelectedPositionInt(position);
setNextSelectedPositionInt(position);
if (mItemCount == 0) {
// Nothing selected
checkSelectionChanged();
}
} else {
mAreAllItemsSelectable = true;
checkFocus();
// Nothing selected
checkSelectionChanged();
}
requestLayout();
}
ListActivity和ListView源码分析(1) 加载布局
标签:
原文地址:http://blog.csdn.net/lee_3do/article/details/43668757