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<T> | 用来绑定一个数组,支持泛型操作 |
SimpleCursorAdapter | 用来绑定游标得到的数据 |
listView使用BaseAdapter
首先在布局文件中定义一个listView控件
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.kevinwang.test.TestListActivity">
<ListView
android:id="@+id/baseList"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:divider="@color/grey"
android:dividerHeight="1dp"></ListView>
</RelativeLayout>
然后定义一个listView中每一行的布局
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/list_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="20sp"/>
</LinearLayout>
使用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()方法来告知数据变化。
那么getView如何使用呢?如果有10000行数据,就绘制10000次?这肯定会极大的消耗资源,导致ListView滑动非常的慢,那应该怎么做呢?
代码中,在getView()方法中加入了一行log输出view的内容。滚动ListView,输出信息如如下图
从图中可以看出,当启动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
网友评论