情况:
我的应用程序包含ToDo列表样式列表视图(每行都有一个复选框)。列表视图行的结构为 2 textviews vertical 。最顶部的文本是标题,最底部是描述,但描述是隐藏的( View.GONE )。使用ListActivities onListItemClick方法,我可以设置被按行的高度和可见性。
@Override
protected void onListItemClick(ListView list, View view, int position, long id) {
super.onListItemClick(list, view, position, id);
view.getLayoutParams().height = 200;
view.requestLayout();
TextView desc = (TextView) view.findViewById(R.id.description);
desc.setVisibility(View.VISIBLE);
}
注意:代码被剥离到最基本的
上面的代码工作正常,只是它扩展了按下的行以及上面或下面的第10行(下一个卸载的视图?)。当列表被剔除时,行扩展也会改变位置。
背景
listview数据通过托管游标从SQLite数据库中检索,并由自定义CursorAdapter设置。托管游标按复选框值排序。
private void updateList() {
todoCursor = managedQuery(TaskContentProvider.CONTENT_URI, projection, null, null, TaskSQLDatabase.COL_DONE + " ASC");
startManagingCursor(todoCursor);
adapter = new TaskListAdapter(this, todoCursor);
taskList.setAdapter(adapter);
}
CursorAdapter由基本的newView()和bindView()组成。
问题:
我需要一些系统来跟踪哪些行被扩展。我已经尝试将光标id存储在数组中,然后检查适配器是否应该扩展行,但我似乎无法使其正常工作。任何建议将不胜感激!
答案 0 :(得分:2)
ListView
会在您向上和向下滚动视图时回收视图,当需要显示新的子项View
时,它会首先查看它是否还没有(如果找到)一个它会用它)。如果您修改ListView
的子项(在onListItemClick()
方法中),然后滚动列表,ListView
最终会重新使用该子View
你修改了,你最终会得到你不想要的某些观点(如果你继续滚动ListView
,你会看到由于View
回收而随机改变位置。
防止这种情况的一种方法是记住用户单击并在适配器中更改该特定行的布局但仅针对您想要的位置的那些位置。您可以将ids
存储在HashMap
(班级中的某个字段)中:
private HashMap<Long, Boolean> status = new HashMap<Long, Boolean>();
我使用了HashMap
但你可以使用其他容器(在下面的代码中我会看到为什么选择HashMap
)。接下来在onListItemClick()
方法中,您将更改单击的行,但也会存储该行id
,以便适配器知道(并采取措施,以便您最终得到错误的回收视图):< / p>
@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
// check and see if this row id isn't already in the status container,
// if it is then the row is already set, if it isn't we setup the row and put the id
// in the status container so the adapter will know what to do
// with this particular row
if (status.get(id) == null) {
status.put(id, true);
v.getLayoutParams().height = 200;
v.requestLayout();
TextView d = (TextView) v.findViewById(R.id.description);
d.setVisibility(View.VISIBLE);
}
}
然后在适配器中使用带有所有ids
的状态容器来正确设置行并防止回收混乱我们的行:
private class CustomAdapter extends CursorAdapter {
private LayoutInflater mInflater;
public CustomAdapter(Context context, Cursor c) {
super(context, c);
mInflater = LayoutInflater.from(context);
}
@Override
public void bindView(View view, Context context, Cursor cursor) {
TextView title = (TextView) view.findViewById(R.id.title);
title.setText(cursor.getString(cursor.getColumnIndex("name")));
TextView description = (TextView) view
.findViewById(R.id.description);
description.setText(cursor.getString(cursor
.getColumnIndex("description")));
// get the id for this row from the cursor
long rowId = cursor.getLong(cursor.getColumnIndex("_id"));
// if we don't have this rowId in the status container then we explicitly
// hide the TextView and setup the row to the default
if (status.get(rowId) == null) {
description.setVisibility(View.GONE);
// this is required because you could have a recycled row that has its
// height set to 200 and the description TextView visible
view.setLayoutParams(new AbsListView.LayoutParams(
AbsListView.LayoutParams.MATCH_PARENT,
AbsListView.LayoutParams.MATCH_PARENT));
} else {
// if we have the id in the status container then the row was clicked and
// we setup the row with the TextView visible and the height we want
description.setVisibility(View.VISIBLE);
view.getLayoutParams().height = 200;
view.requestLayout();
// this part is required because you did show the row in the onListItemClick
// but it will be lost if you scroll the list and then come back
}
}
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
View v = mInflater.inflate(R.layout.adapters_showingviews, null);
return v;
}
}
如果您要切换单击的行(并且有些内容告诉我您想要这个),在点击/隐藏另一行上的TextView
时显示TextView
然后简单地向else clause
方法添加onListItemClick()
并从状态容器中删除单击的行id
并还原该行:
//...
else {
status.remove(id);
v.setLayoutParams(new AbsListView.LayoutParams(
AbsListView.LayoutParams.MATCH_PARENT,
AbsListView.LayoutParams.MATCH_PARENT));
TextView d = (TextView) v.findViewById(R.id.description);
d.setVisibility(View.GONE);
}