大部分应用的内容都是是纵向的列表,因为手机上下划屏成本很低,而且用户对下面的内容长度是有心理预期的,所以大部分应用都是使用这个设计的。对应这个需求,安卓里面提供了ListView这个控件,是做安卓开发打交道最多的,类似iOS的TableView。
ListView是一个多行的列表,每行是一个item,item本身对应一个Layout。ListView本身是AdapterView的派生类,需要绑定Adapter来负责管理生成对应每行的item。item的View是复用的,即使Adapter里面的item有成百上千,但是真正生成的View一般只有屏幕显示的那么多个。这和iOS的TableCell的复用是一致的。
//下面是一个ListView在layout里的样子
<ListView
android:id="@id/list"
android:layout_height="match_parent"
android:layout_width="match_parent"
android:fadingEdge="none"
android:fastScrollEnabled="false"
android:cacheColorHint="#00000000"
/>
//下面是具体的一个item的layout,item_notify.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="10dp"
android:gravity="center_vertical"
>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_weight="1.0"
>
<TextView
android:id="@id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="16sp"
android:textColor="#000000"
android:paddingBottom="2dp"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
/>
<TextView
android:id="@id/time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="14sp"
android:textColor="#999999"
android:layout_below="@id/title"
android:layout_alignParentLeft="true"
android:layout_alignParentBottom="true"
/>
</RelativeLayout>
<ImageView
android:id="@id/icon"
android:layout_width="50dp"
android:layout_height="50dp"
android:scaleType="centerInside"
/>
</LinearLayout>
下面是典型的调用过程:
ListView list = (ListView)findViewById(R.id.list);
ListAdapter adapter = new ListAdapter();
list.setAdapter(adapter);
//假设数据是存在一个叫做data 的数组里的
class ListAdapter extends BaseAdapter{
//返回item的数量
public int getCount() {
return data.length;
}
//返回对应位置item的数据对象
public Object getItem(int position) {
return data[position];
}
//返回对应位置item的id,一般不直接用这个函数,而是getItem得到对象后,用对象的id
public long getItemId(int position) {
return 0;
}
//使用ViewHolder机制,避免findViewById的操作,优化性能
public View getView(int position, View convertView, ViewGroup parent){
//注意这里使用了View的tag属性来把一个ViewHolder对象绑定在一个convertView上面
//convertView就是对应item的真正View
ViewHolder holder = null;
//如果没有可以复用的convertView,也就是真正要创建item的layout
if (convertView == null) {
holder = new ViewHolder();
convertView = getLayoutInflater().inflate(R.layout.item_notify, null);
holder.icon = (ImageView)convertView.findViewById(R.id.icon);
holder.title = (TextView)convertView.findViewById(R.id.title);
holder.time = (TextView)convertView.findViewById(R.id.time);
//把holder对象绑定到convertView上
convertView.setTag(holder);
//如果convertView已经被创建过,也就是说该item被复用
} else {
//从tag里取出来holder对象
holder = (ViewHolder) convertView.getTag();
}
refreshView(position, holder);
return convertView;
}
private void refreshView(int position, ViewHolder h) {
//根据position对应的item数据对象,具体的更新UI
}
};
static class ViewHolder {
ImageView icon;
TextView title;
TextView time;
};
当adapter的数据有任何变化时,需要及时调用adapter.notifyDataSetChanged()来通知ListView来更新界面,否则会在滑动或UI重绘时,因为数据已经变了,导致fc。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
//这里的利用了Layout的descendantFocusability属性
//afterDescendants表示单条只有在里面的控件没有得到焦点时,最外面的item整个才获得焦点。
android:descendantFocusability="afterDescendants"
>
...
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
</LinearLayout>
//给ListView设置AbsListView.OnScrollListener
listView.setOnScrollListener(new AbsListView.OnScrollListener(){
public void onScrollStateChanged(AbsListView view, int scrollState){
if (lastScrollState == SCROLL_STATE_IDLE){
//加载图片
}
}
});