public class MyArrayAdapter extends ArrayAdapter{
int resource;
final List listItems; // 用这个list来指向最初的数据list
private class ViewHolder {
int position;
}
public MyArrayAdapter(Context context, int textViewResourceId, List objects) {
super(context, textViewResourceId, objects);
resource = textViewResourceId;
listItems = objects; // 相信这个objects就是构建Adapter的最初的数据list,赶紧保存
}
public View getView (int position, View convertView, ViewGroup parent){
LinearLayout fileView;
// 方案一,使用final变量
// 进入方法都会生成一个final p,原本方法退出之后会被回收,
// 但由于后面兼听类的引用,使其不会被回收,除非兼听类先被回收。
// 下次进入本方法会生成另外一个final p。
// 因此,我们可以用final p来保存position。
// 这是我的理解,不肯定对。
final Integer p;
p = position;
LvRow lr = (LvRow)getItem(position);
if (convertView == null){
fileView = new LinearLayout(getContext());
String inflater = Context.LAYOUT_INFLATER_SERVICE;
LayoutInflater li;
li = (LayoutInflater)getContext().getSystemService(inflater);
li.inflate(resource, fileView, true);
}else{
fileView = (LinearLayout)convertView;
}
TextView name = (TextView)fileView.findViewById(R.id.text1);
name.setText(lr.getName());
TextView length = (TextView)fileView.findViewById(R.id.text2);
length.setText(lr.getLength());
TextView date = (TextView)fileView.findViewById(R.id.text3);
date.setText(lr.getDate());
// 这是我们ListView的宝贝CheckBox
CheckBox cb = (CheckBox)fileView.findViewById(R.id.checkbox);
if (lr.getName().charAt(0) == '/') // 这是一个文件管理项目的代码,不允许check目录,故隐藏CheckBox
{
cb.setVisibility(cb.INVISIBLE);
}else{
cb.setVisibility(cb.VISIBLE);
cb.setChecked(listItems.get(position).getSelected()); // 从数据list中恢复checked状态
cb.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
CheckBox cb = (CheckBox)view;
listItems.get(p).setSelected(cb.isChecked()); // 保持对final p的引用,使其不被回收
}
});
}
return fileView;
}
}
// 注意增加了selected标志位
public class LvRow{
private String name;
private String length;
private String date;
private File file;
private boolean selected;
// 各种set/get/constructer略掉
}
private void shareFiles() { // 共享被选择的文件
List selectedItems = new ArrayList();
// 这个viewListItems就是最初构建Adapter的数据list
// 在那边修改后就反映到这里
int size = viewListItems.size();
for(int count = 0;count
public class MyArrayAdapter extends ArrayAdapter{
int resource;
final List listItems;
private class ViewHolder {
int position;
}
public MyArrayAdapter(Context context, int textViewResourceId, List objects) {
super(context, textViewResourceId, objects);
resource = textViewResourceId;
listItems = objects;
}
public View getView (int position, View convertView, ViewGroup parent){
LinearLayout fileView;
LvRow lr = (LvRow)getItem(position);
if (convertView == null){
fileView = new LinearLayout(getContext());
String inflater = Context.LAYOUT_INFLATER_SERVICE;
LayoutInflater li;
li = (LayoutInflater)getContext().getSystemService(inflater);
li.inflate(resource, fileView, true);
}else{
fileView = (LinearLayout)convertView;
}
TextView name = (TextView)fileView.findViewById(R.id.text1);
name.setText(lr.getName());
TextView length = (TextView)fileView.findViewById(R.id.text2);
length.setText(lr.getLength());
TextView date = (TextView)fileView.findViewById(R.id.text3);
date.setText(lr.getDate());
CheckBox cb = (CheckBox)fileView.findViewById(R.id.checkbox);
// 方案二,直接在View中添加tag来保存position信息
cb.setTag(position);
if (lr.getName().charAt(0) == '/')
{
cb.setVisibility(cb.INVISIBLE);
}else{
cb.setVisibility(cb.VISIBLE);
cb.setChecked(listItems.get(position).getSelected()); // 恢复checked状态
cb.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
CheckBox cb = (CheckBox)view;
// 下面直接用View的tag中获取position信息,并修改对应的最初的数据list
listItems.get((Integer)view.getTag()).setSelected(cb.isChecked());
}
});
}
return fileView;
}
}
以下是截图。注意在行View布局中我设置了android:focusable="false",使得可以分别点击List的item和CheckBox。
测试的时候要小心,在选择了若干CheckBox之后要将其来回拖出屏幕,检查checked状态是否正常,最后再在最初的数据list中统计checked状态。
---------修正------------------------
2014年4月14日
写本文时对某些地方理解不清,并没说到要点,这里给予补充。
主要是两个问题。问题之一题是View被复用,但却没有被清空:
1、当ListView中的一行条目移出显示屏时,其View并未被回收,而是放在旁边,准备复用。注意此刻并不清空View里面的垃圾数据。
2、当有一行条目要进入显示屏时,刚才被回收的View立即被取过来重用,但是由于之前被用过,且没有重新清空的步骤,所以凡是没有被设置的地方均是View上次的数据。
解决方案是,任何表示数据的地方都要做清空或数据设置。
问题之二是,怎样向View的监听函数传递数据,方式之一是通过final类型变量,方式之二是设置在View里面。后者好。
在本例中,向监听函数传递CheckButton所在item在list列表中的位置,于是CheckButton被点击时能够将变动修改到list列表。
2015年1月1日
ListView里面常用findViewById()来寻找view中的子view,这种操作往往很费时,通常可以使用一个静态类来标识View的结构:
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.your_layout, null);
holder = new ViewHolder();
holder.text = (TextView) convertView.findViewById(R.id.text);
convertView.setTag(holder);
} else {
holder = convertView.getTag();
}
holder.text.setText("Position " + position);
return convertView;
}
private static class ViewHolder {
public TextView text;
}
以上例子来自:http://lucasr.org/2012/04/05/performance-tips-for-androids-listview/