技术总结
——AJSZJ01131221-自定义ListView
12月21日
ListView是Android的UI系统中的重要组件之一,也是Android MVC模型的典型体现。
ListView的滑动、点击方式非常适合小屏幕移动设备的交互方式,自然也成为Android系统交互开发的关键组件。对ListView的掌握意味着对Android平台的内容展现由简单向复杂的提升。
这篇文章我们重点讨论对ListView的自定义。
实例效果为:显示一个用户列表,包用户头像、用户名、用户描述。用户名为Frank的用户为VIP,在其名称之前显示VIP标识。
效果图:
提到ListView就一定要讨论Adapter。Adapter是适配器模式在ListView上实现的关键,是数据和视图之间的适配器。自定义ListView的每个项目,实际上就是自定义Adapter。
Android的SDK为我们提供了很多Adapter,其继承关系如下图:
我们在这里要继承BaseAdapter,以获得最大的自定义空间。
CustomAdapter的代码如下:
package com.ajszj.demolistview;
import java.util.List;
import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
publicclass CustomAdapter extends BaseAdapter {
private LayoutInflater mInflater=null;
private List<User> mData =null;
private Context mContext =null;
private ViewHolder mHolder;
public CustomAdapter(Contextcontext, List<User> userList)throws Exception {
mContext = context;
mInflater = LayoutInflater.from(context);
if (userList ==null){
throw newException("no user");
} else {
mData = userList;
}
}
private class ViewHolder {
ImageView uPortrait;
TextView uName;
TextView uDspt;
}
@Override
public int getCount(){
return mData.size();
}
@Override
public ObjectgetItem(intposition) {
return mData.get(position);
}
@Override
public long getItemId(intposition) {
return ((User) getItem(position)).getId();
}
@Override
public ViewgetView(intposition, View convertView, ViewGroup parent) {
if (convertView ==null){
convertView = mInflater.inflate(R.layout.custom_list_item,null);
mHolder =newViewHolder();
mHolder.uPortrait = (ImageView)convertView
.findViewById(R.id.item_portrait);
mHolder.uName = (TextView)convertView.findViewById(R.id.item_name);
mHolder.uDspt = (TextView)convertView.findViewById(R.id.item_dspt);
convertView.setTag(mHolder);
} else {
mHolder = (ViewHolder)convertView.getTag();
}
User user = (User) getItem(position);
mHolder.uPortrait.setImageResource(user.getmPortraitId());
mHolder.uName.setText(user.getmName());
mHolder.uDspt.setText(user.getmDspt());
if (user.getmName().equals("Frank")){
Drawable drawable = mContext.getResources().getDrawable(
R.drawable.vip_icon_small);
drawable.setBounds(0,0, drawable.getMinimumWidth(), drawable.getMinimumHeight());
mHolder.uName.setTextColor(Color.RED);
mHolder.uName.setCompoundDrawablePadding(5);
mHolder.uName.setCompoundDrawables(drawable,null,null, null);
} else {
mHolder.uName.setTextColor(Color.parseColor("#666666"));
mHolder.uName.setCompoundDrawablePadding(0);
mHolder.uName.setCompoundDrawables(null,null,null, null);
}
return convertView;
}
}
在CustomAdapter类中,我们复写了BaseAdapter的四个方法:getCount()、getItem()、getItemId()、getView(),并定义了一个私有内部类ViewHolder。下面逐个解释:
1. getCount()
这个方法返回ListView的大小,以便计算ListView的高度以及其他用途。
需要返回的是item的数量,由数据决定。
2. getItem(int postion)
这个方法返回对应position的item,为单个数据对象,返回值类型是Object,即可以返回任意对象。这也要求在使用getItem()的时候要注意类型的转换。
3. getItemId(int position)
这个方法返回对应position的item的id值,要注意区分position和id:
ListView中的每个item都会有一个item和一个position值,position为该item在ListView中的位置索引;id为该item包含的数据对象的id属性,两个值都是从0开始。最重要区别为,id根据数据不同而不同,即id是数据的属性;position根据数据在ListView中的位置不同而不同。数据的id属性一般在设定之后不会改变。
getItemId()即是让开发者根据position返回对应此position的数据的id属性。
4. getView(int position, ViewconvertView, ViewGroup parent)
这是最重要的方法,Adapter就是使用这个方法将内容和显示方式进行对接,达到将数据显示到ListView中的目的。
这个方法的返回值类型是View,即是当前position的item对应的显示到ListView中的内容,getView方法要做的,就是用数据填充这个item的内容。
下面对getView方法和ViewHolder类进行解析:
getView的第二个参数convertView是Android虚拟机传入的缓存View,这是为了提升效率进行的缓存处理。在使用ListView的时候要充分利用Android提供的缓存机制,减少视图对象的创建进而减少资源消耗。
在使用convertView时,需要定义对应布局的ViewHolder类,来进行对视图中的控件的标记。
定义ViewHolder类的方式就是定义属性以对应布局中的控件,本例中:
重写getView()方法的步骤为:
l 判断convertView是否为空。如果为空,则使用LayoutInflater对item布局进行加载,同时新建ViewHolder对象并初始化。最后调用convertView的setTag()方法将ViewHolder对象和convertView进行关联。
如果不为空,则从convertView中获得暂存的ViewHolder对象。
l 得到和convertView关联的ViewHolder对象之后,开始对ViewHolder对象进行内容填充,即是对ViewHolder的属性进行赋值操作。
l 要注意对ViewHolder对象的操作,实际上就是对ListView中最终显示的View的操作。所以,如果需要对ListView中的View进行动态样式改变和编辑,需要在此处进行。如本例中的Vip标识显示,即是在此处进行定义。
l 最后是函数的返回值,返回值应该是convertView,它已经和ViewHolder进行了关联,所以以上对ViewHolder对象的赋值操作,都反映在当前的convertView对象中。
现在,已经完成了对BaseAdapter的自定义,测试代码如下:
Activity代码:
package com.ajszj.demolistview;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import android.app.Activity;
import android.os.Bundle;
import android.widget.ListView;
publicclass MainActivity extends Activity {
private ListView mListView =null;
private int[] portraits = { R.drawable.img01,R.drawable.img02,
R.drawable.img03, R.drawable.img04,R.drawable.img05 };
private String[] names = { "Frank","Tom", "John", "Jobs", "Stark" };
private String[] dspts = { "long,long day", "nothing to say",
"merry Xmas", "home","i need a feast" };
private List<User> list =null;
@Override
protected void onCreate(BundlesavedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mListView = (ListView) findViewById(R.id.list);
Random random = new Random();
list = new ArrayList<User>();
for (int i = 0; i < 15;i++) {
list.add(new User(random.nextLong(),portraits[random.nextInt(5)],
names[random.nextInt(5)],dspts[random.nextInt(5)]));
}
try {
CustomAdapter adapter =newCustomAdapter(this, list);
mListView.setAdapter(adapter);
} catch (Exception e) {
e.printStackTrace();
}
}
}
item布局代码:
<?xml version="1.0"encoding="utf-8"?>
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="5dp" >
<ImageView
android:id="@+id/item_portrait"
android:layout_width="60dp"
android:layout_height="60dp"
android:src="@drawable/ic_launcher"/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="top"
android:layout_marginLeft="5dp"
android:orientation="vertical">
<TextView
android:id="@+id/item_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Frank"
android:textColor="#000000"
android:textSize="18sp"
android:singleLine="true"
android:textStyle="bold" />
<TextView
android:id="@+id/item_dspt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="description"
android:singleLine="true"
android:textColor="#666666"
android:textSize="14sp"/>
</LinearLayout>
</LinearLayout>