在android开发中,ListView是一个很常用的控件,用于基本的信息展示。这里总结一下ListView的基本用法。
Views/Lists
1、基本信息显示
ListView显示的核心控制在于给它绑定数据与视图的Adapter上,BaseAdapter是所有adapter的基类。ListView可以显示任意的布局形式,一般如果只显示静态的信息,那么使用SDK里的adapter就可以实现,ArrayAdapter用于显示一行字符,SimpleAdapter可用于显示自定义的复杂布局文件。
2、扩展信息显示
SimpleAdapter虽说已经可以满足大多数情况的需要,但还不够灵活,用SimpleAdapter显示的每一行布局都是相同的,不能有特殊情况。因此如果希望可以扩展ListView的显示特性,比如使一些行布局发生变化,或者添加Button控件的响应等,这时候就要自己去实现一个BaseAdapter,通过其getView方法为每一行返回特定的布局形式或者视图。例如
public View getView(int position, View convertView, ViewGroup parent) {
SpeechView sv;
if (convertView == null) {
sv = new SpeechView(mContext, mTitles[position],
mDialogue[position]);
} else {
sv = (SpeechView) convertView;
sv.setTitle(mTitles[position]);
sv.setDialogue(mDialogue[position]);
}
return sv;
}
其中,SpeechView就是自定义的一个视图。总之,getView控制了Listview的每一行的view显示,如果想改变,就要在这里发生。
3、ListView的显示优化
对于复杂的显示形式,我们可能需要做一些优化来保证ListView滑动过程的流畅性。
在getView方法中有一个convertView参数,它传入的是刚滑出屏幕的那个View对象,利用convertView就可以避免对象重复创建,而只改变了显示的内容。
例如在Efficient Adapter中演示的
/**
* Make a view to hold each row.
*
* @see android.widget.ListAdapter#getView(int, android.view.View,
* android.view.ViewGroup)
*/
public View getView(int position, View convertView, ViewGroup parent) {
// A ViewHolder keeps references to children views to avoid unneccessary calls
// to findViewById() on each row.
ViewHolder holder;
// When convertView is not null, we can reuse it directly, there is no need
// to reinflate it. We only inflate a new View when the convertView supplied
// by ListView is null.
if (convertView == null) {
convertView = mInflater.inflate(R.layout.list_item_icon_text, null);
// Creates a ViewHolder and store references to the two children views
// we want to bind data to.
holder = new ViewHolder();
holder.text = (TextView) convertView.findViewById(R.id.text);
holder.icon = (ImageView) convertView.findViewById(R.id.icon);
convertView.setTag(holder);
} else {
// Get the ViewHolder back to get fast access to the TextView
// and the ImageView.
holder = (ViewHolder) convertView.getTag();
}
// Bind the data efficiently with the holder.
holder.text.setText(DATA[position]);
holder.icon.setImageBitmap((position & 1) == 1 ? mIcon1 : mIcon2);
return convertView;
}
static class ViewHolder {
TextView text;
ImageView icon;
}
通过判断convertView是否为null来决定是否需要实例化一个视图对象,convertView只有在有视图滑出List的时候才不为空,更多关于ListView性能优化的建议可以参考http://www.cnblogs.com/over140/archive/2011/03/23/1991100.html。
还有一种情况是当List滑动过程中不去显示实际的View视图,而是加载一个提示信息如“Loading…”,等到滑动结束时再更新相的数据。这样也可以提高显示的效率,特别是对一些需要下载的网络数据而言。具体的演示在Slow Adapter中
数据加载:
public View getView(int position, View convertView, ViewGroup parent) {
TextView text;
if (convertView == null) {
text = (TextView)mInflater.inflate(android.R.layout.simple_list_item_1, parent, false);
} else {
text = (TextView)convertView;
}
if (!mBusy) {
text.setText(mStrings[position]);
// Null tag means the view has the correct data
text.setTag(null);
} else {
text.setText("Loading...");
// Non-null tag means the view still needs to load it's data
text.setTag(this);
}
return text;
}
监听滑动过程
public void onScrollStateChanged(AbsListView view, int scrollState) {
switch (scrollState) {
case OnScrollListener.SCROLL_STATE_IDLE:
mBusy = false;
int first = view.getFirstVisiblePosition();
int count = view.getChildCount();
for (int i=0; i<count; i++) {
TextView t = (TextView)view.getChildAt(i);
if (t.getTag() != null) {
t.setText(mStrings[first + i]);
t.setTag(null);
}
}
mStatus.setText("Idle");
break;
case OnScrollListener.SCROLL_STATE_TOUCH_SCROLL:
mBusy = true;
mStatus.setText("Touch scroll");
break;
case OnScrollListener.SCROLL_STATE_FLING:
mBusy = true;
mStatus.setText("Fling");
break;
}
}
3、ListView的一些特殊功能
(1)使部分行不能点击
在BaseAdapter中通过覆写isEnabled方法可以控制具体某一行的是否可以点击,例如Separator中
@Override
public boolean isEnabled(int position) {
return !mStrings[position].startsWith("-");
}
通过判断该行字符是否以‘‘-’’开头来决定使能。
(2)滑动中背景变黑问题
设置属性android:cacheColorHint="#00000000"即可