android listView

ListView是一种以列表形式展示具体内容,并且能够根据数据的长度自适应显示的控件。一个listView通常有两个职责:一是将数据填充到布局,二是处理用户的选择点击操作等

构建ListView的元素:
- ListView中每一列的view。
- 填入View的数据,如字符串、图片或组件。
- 连接数据与ListView的适配器。

适配器(Adapter)

适配器是一个连接数据和AdapterView(ListView就是一个典型的AdapterView,其他的AdapterView还有Spinner, GridView, Gallery and StackView)的桥梁,通过它能有效地实现数据与AdapterView的分离设置,使AdapterView与数据的绑定更加简便,修改更加方便。下表为常用的adapter。

Adapter 含义
BaseAdapter 通用的基础适配器
SimpleAdapter 用来绑定在xml中定义的控件对应的数据
ArrayAdapter 用来绑定一个数组,支持泛型操作
SimpleCursorAdapter 用来绑定游标得到的数据
listView使用BaseAdapter

首先在布局文件中定义一个listView控件


    

然后定义一个listView中每一行的布局


        

使用BaseAdapter必须写一个类继承它,同时BaseAdapter是一个抽象类,继承它必须实现它的方法。当系统开始绘制ListView的时候,首先调用getCount()方法。得到它的返回值,即ListView的长度。然后系统调用getView()方法,根据这个长度逐一绘制ListView的每一行。也就是说,如果让getCount()返回1,那么只显示一行。而getItem()和getItemId()则在需要处理和取得Adapter中的数据时调用。

public class TestListActivity extends AppCompatActivity {
    private ListView mListView;
    private String[] mStrArray;
    private MyBaseAdapter myBaseAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test_list);

        mListView = (ListView)findViewById(R.id.baseList);

        mStrArray = new String[30];
        for (int i = 0; i < 30; i++) {
            mStrArray[i] = String.valueOf(i);
        }

        myBaseAdapter = new MyBaseAdapter(this);
        mListView.setAdapter(myBaseAdapter);

        mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView adapterView, 
                                    View view, int i, long l) {
                TextView textView = (TextView)view.findViewById(R.id.list_text);
                mStrArray[i] = String.valueOf(Integer.valueOf(mStrArray[i]) + 1);
                myBaseAdapter.notifyDataSetChanged();
            }
        });
    }

    class MyBaseAdapter extends BaseAdapter {
        private Context mContext;
        private LayoutInflater mInflater; //得到一个LayoutInfalter对象用来导入布局
        private ViewHolder viewHolder;

        public MyBaseAdapter(Context context) {
            mContext = context;
            mInflater = (LayoutInflater) mContext.
                    getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        }

        @Override
        public int getCount() {
            //返回数据总数
            return mStrArray.length;
        }

        @Override
        public Object getItem(int i) {
            //返回某个位置的数据
            return mStrArray[i];
        }

        @Override
        public long getItemId(int i) {
            return i;
        }

        @Override
        public View getView(int i, View convertView, ViewGroup viewGroup) {
            if(convertView == null) {
                convertView= mInflater.inflate(R.layout.test_list_item, null);
                viewHolder = new ViewHolder();
                viewHolder.textView = (TextView) convertView.findViewById(R.id.list_text);
                convertView.setTag(viewHolder);  //绑定ViewHolder对象
            }
            else {
                viewHolder = (ViewHolder)convertView.getTag(); //取出ViewHolder对象
            }
            Log.v("myListView", "getView " + i + " " + view);
            viewHolder.textView.setText(mStrArray[i]);
            return view;

        }
    }

    class ViewHolder {
        public TextView textView;
    }
}

为了响应列表项的点击事件,应该调用OnItemClickListener方法:

listView.setOnItemClickListener(new OnItemClickListener() {
  @Override
  public void onItemClick(AdapterView parent, View view,
    int position, long id) {
         ...............
  }
}); 

当数据改变时,adapter应该调用notifyDataSetChanged()方法来告知数据变化。

android listView_第1张图片

那么getView如何使用呢?如果有10000行数据,就绘制10000次?这肯定会极大的消耗资源,导致ListView滑动非常的慢,那应该怎么做呢?

android listView_第2张图片

代码中,在getView()方法中加入了一行log输出view的内容。滚动ListView,输出信息如如下图


android listView_第3张图片

  从图中可以看出,当启动Activity呈现第一屏ListView的时候,convertView为null并被分配了一系列的convertView的值。当往下滚屏时,发现第0行的容器用来容纳第14行,第1行的容器用来容纳第15行。也就是说convertView相当于一个缓存,当有条目变为不可见,它缓存了它的数据,后面再出来的条目只需要更新数据就可以了,这样大大节省了系统资料的开销。
  虽然重复利用了已经绘制的view,但是要得到其中的控件,需要在控件的容器中通过findViewById的方法来获得。如果这个容器非常复杂,这显然会增加系统资源的开销。在上面的例子中,引入了Tag的概念。或许不是最好的办法,但是它确实能使ListView变得更流畅。代码中,当convertView为空时,用setTag()方法为每个View绑定一个存放控件的ViewHolder对象。当convertView不为空,重复利用已经创建的view的时候,使用getTag()方法获取绑定的ViewHolder对象,这样就避免了findViewById对控件的层层查询,而是快速定位到控件。

当listView中的每个Item不同时怎么办?
利用getItemViewType方法。

listView的常用属性
- listSelector
- scrollingCache
- cacheColorHint
- fastScrollEnabled

listView的常用方法
- addHeaderView
- addFooterView

你可能感兴趣的:(android listView)