在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性能优化的建议可以参考。

还有一种情况是当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"即可