在android开发中,ListView是一个很常用的控件,用于基本的信息展示。这里总结一下ListView的基本用法。
Views/Lists
ListView显示的核心控制在于给它绑定数据与视图的Adapter上,BaseAdapter是所有adapter的基类。ListView可以显示任意的布局形式,一般如果只显示静态的信息,那么使用SDK里的adapter就可以实现,ArrayAdapter用于显示一行字符,SimpleAdapter可用于显示自定义的复杂布局文件。
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; } }
(1)使部分行不能点击
在BaseAdapter中通过覆写isEnabled方法可以控制具体某一行的是否可以点击,例如Separator中
@Override public boolean isEnabled(int position) { return !mStrings[position].startsWith("-"); }
(2)滑动中背景变黑问题
设置属性android:cacheColorHint="#00000000"即可