当我更改已经可见的 TextView 的文本时,我的 ListView 中的第一项不会更新
The first item in my ListView will not update when I change the text of an already visible TextView
我有一个 ListView,它由我的自定义 BaseAdapter 填充,其中的视图包含几个 TextView 和 ImageButton。如果用户从其中一个列表项的 PopupMenu 中选择一个项目,它会更改该列表项上关联的 TextView 的文本。这对我的 ListView 中的所有项目都非常有用,除了第一个。由于某种原因,第一项从不更新 TextView。我怀疑它与我的 convertView 有关,因为该项目没有被重置,但我不确定如何修复它。
这是我的设置。我有一个由自定义 BaseAdapter 填充的 ListView。在我的 BaseAdapter 中,我的 getView()
:
public View getView(int position, View convertView, ViewGroup parent) {
if(convertView == null) {
IMOModGroup modGroup = modGroups.get(position);
convertView = modGroup.getConvertView(parent);
}
return convertView;
}
IMOModGroup 是一种特殊的数据结构,它包含每个列表项的所有状态和数据。因此,当 convertView 变为 null 时,BaseAdapter 会从 IMOModGroup 为该位置请求一个新的 convertView。这是创建新的 convertView 的 IMOModGroup 方法:
public View getConvertView(ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.modifier_list_item, parent, false);
//...
TextView selectedModText = (TextView) convertView.findViewById(R.id.modName);
selectedModText.setText(R.string.no_modifier_selected);
selectedModText.setTextColor(context.getResources().getColor(R.color.grayed_out_text_color));
//...
return convertView;
}
用户可以在每个列表项上打开一个 PopupMenu 并进行选择。当他们这样做时,上面代码中的 TextView 被更改以反映新文本的选择。该代码如下所示:
public boolean onMenuItemClick(MenuItem item) {
String selectedMod = item.getTitle().toString();
TextView selectedModText = (TextView) convertView.findViewById(R.id.modName);
selectedModText.setText(selectedMod);
selectedModText.setTextColor(context.getResources().getColor(R.color.imo_blue_light));
//...
return false;
}
除第一个项目外,所有这些都适用于 ListView 中的每个项目,它从不更新以反映 TextView 的变化。我已经过测试以确保 TextView 文本确实发生了变化,并且可以在以后调用和查看,并且设置正确。所以看起来数据是正确的,但视图没有得到更新。
我很确定问题是我的 convertView 没有被重置,所以 ListView 只是继续使用带有旧文本的旧视图。设置文本后,我尝试在 BaseAdapter 上调用 notifyDataSetChanged()
,但这没有用。
这个问题看起来很有希望:First item in list view is not displaying correctly
但我不确定这是否是我的问题,而且我不确定如何将其应用到我的场景中。
有什么想法吗?我已经阅读了一些关于 SO 和 Google 的类似问题,但其中 none 似乎确实与我的特定场景相关。我使用 ListViews 和 Adapters 已经有一段时间了,这是我第一次看到这样的东西。感谢您的帮助。
根据评论进行编辑
这是完整的 BaseAdapter 代码...
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import java.util.ArrayList;
public class IMOModListAdapter extends BaseAdapter {
private Context context;
private ArrayList<IMOModGroup> modGroups;
public IMOModListAdapter(Context context, ArrayList<IMOModGroup> modGroups) {
this.context = context;
this.modGroups = modGroups;
}
public View getView(int position, View convertView, ViewGroup parent) {
if(convertView == null) {
IMOModGroup modGroup = modGroups.get(position);
convertView = modGroup.getConvertView(parent);
}
return convertView;
}
public Object getItem(int position) {
return 0;
}
public long getItemId(int position) {
return 0;
}
public int getCount() {
return modGroups.size();
}
}
进一步测试
随着我深入调试,我意识到当列表中元素 1 的 TextView 发生变化时,甚至根本没有调用 getView()
。但是当任何其他元素发生变化时, getView()
将被正常调用。
作为完整性检查,我尝试从外部更改第一个列表项的文本,从父 Activity 本身。如果我尝试更改第一项,它什么都不做,但如果我尝试更改任何其他列表项,它工作正常!这太奇怪了!这是我试过的:
//Set the text of the first item. This does NOTHING and the TextView
//in the item remains unchanged
IMOModGroup modGroup = modGroups.get(0);
modGroup.setTestText("This is some test text");
modList.notifyDataSetChanged();
//Set the text of the second item. This works completely fine!!!!!
IMOModGroup modGroup = modGroups.get(1);
modGroup.setTestText("This is some test text");
modList.notifyDataSetChanged();
嗯,我找到了解决这个问题的方法。只是研究关于 BaseAdapters 和 getView()
的随机 SO 问题,我终于遇到了 this post by Romain Guy,他在其中声明你永远不应该将 wrap_content
用于 ListView 的 layout_height
,因为它导致 ListView 多次调用适配器的 getView()
只是为了创建用于测量内容高度的虚拟视图。原来这是导致我的问题的原因,所以将我的 layout_height
更改为 match_parent
就成功了。
不幸的是,现在这导致了一个新问题,因为我无法使用 match_parent
,因为它会隐藏我父布局中的其他视图。但至少第一个列表项现在正在正确更新。
更新:
我了解了如何在填充适配器后手动和动态设置 ListView 的高度。我使用了下面的方法,希望这能帮助 运行 遇到类似问题的人。这样我就不必使用 wrap_content
但我仍然可以将 ListView
高度设置为与其内容一样大。
private void setModListHeight() {
int numberOfItems = modGroups.size(); //the ArrayList providing data for my ListView
int totalItemsHeight = 0;
for(int position = 0; position < numberOfItems; position++) {
View item = modListAdapter.getView(position, null, modList);
item.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
totalItemsHeight += item.getMeasuredHeight();
}
int totalDividersHeight = modList.getDividerHeight() * (numberOfItems - 1);
ViewGroup.LayoutParams params = modList.getLayoutParams();
params.height = totalItemsHeight + totalDividersHeight;
modList.setLayoutParams(params);
modList.requestLayout();
}
我有一个 ListView,它由我的自定义 BaseAdapter 填充,其中的视图包含几个 TextView 和 ImageButton。如果用户从其中一个列表项的 PopupMenu 中选择一个项目,它会更改该列表项上关联的 TextView 的文本。这对我的 ListView 中的所有项目都非常有用,除了第一个。由于某种原因,第一项从不更新 TextView。我怀疑它与我的 convertView 有关,因为该项目没有被重置,但我不确定如何修复它。
这是我的设置。我有一个由自定义 BaseAdapter 填充的 ListView。在我的 BaseAdapter 中,我的 getView()
:
public View getView(int position, View convertView, ViewGroup parent) {
if(convertView == null) {
IMOModGroup modGroup = modGroups.get(position);
convertView = modGroup.getConvertView(parent);
}
return convertView;
}
IMOModGroup 是一种特殊的数据结构,它包含每个列表项的所有状态和数据。因此,当 convertView 变为 null 时,BaseAdapter 会从 IMOModGroup 为该位置请求一个新的 convertView。这是创建新的 convertView 的 IMOModGroup 方法:
public View getConvertView(ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.modifier_list_item, parent, false);
//...
TextView selectedModText = (TextView) convertView.findViewById(R.id.modName);
selectedModText.setText(R.string.no_modifier_selected);
selectedModText.setTextColor(context.getResources().getColor(R.color.grayed_out_text_color));
//...
return convertView;
}
用户可以在每个列表项上打开一个 PopupMenu 并进行选择。当他们这样做时,上面代码中的 TextView 被更改以反映新文本的选择。该代码如下所示:
public boolean onMenuItemClick(MenuItem item) {
String selectedMod = item.getTitle().toString();
TextView selectedModText = (TextView) convertView.findViewById(R.id.modName);
selectedModText.setText(selectedMod);
selectedModText.setTextColor(context.getResources().getColor(R.color.imo_blue_light));
//...
return false;
}
除第一个项目外,所有这些都适用于 ListView 中的每个项目,它从不更新以反映 TextView 的变化。我已经过测试以确保 TextView 文本确实发生了变化,并且可以在以后调用和查看,并且设置正确。所以看起来数据是正确的,但视图没有得到更新。
我很确定问题是我的 convertView 没有被重置,所以 ListView 只是继续使用带有旧文本的旧视图。设置文本后,我尝试在 BaseAdapter 上调用 notifyDataSetChanged()
,但这没有用。
这个问题看起来很有希望:First item in list view is not displaying correctly
但我不确定这是否是我的问题,而且我不确定如何将其应用到我的场景中。
有什么想法吗?我已经阅读了一些关于 SO 和 Google 的类似问题,但其中 none 似乎确实与我的特定场景相关。我使用 ListViews 和 Adapters 已经有一段时间了,这是我第一次看到这样的东西。感谢您的帮助。
根据评论进行编辑
这是完整的 BaseAdapter 代码...
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import java.util.ArrayList;
public class IMOModListAdapter extends BaseAdapter {
private Context context;
private ArrayList<IMOModGroup> modGroups;
public IMOModListAdapter(Context context, ArrayList<IMOModGroup> modGroups) {
this.context = context;
this.modGroups = modGroups;
}
public View getView(int position, View convertView, ViewGroup parent) {
if(convertView == null) {
IMOModGroup modGroup = modGroups.get(position);
convertView = modGroup.getConvertView(parent);
}
return convertView;
}
public Object getItem(int position) {
return 0;
}
public long getItemId(int position) {
return 0;
}
public int getCount() {
return modGroups.size();
}
}
进一步测试
随着我深入调试,我意识到当列表中元素 1 的 TextView 发生变化时,甚至根本没有调用 getView()
。但是当任何其他元素发生变化时, getView()
将被正常调用。
作为完整性检查,我尝试从外部更改第一个列表项的文本,从父 Activity 本身。如果我尝试更改第一项,它什么都不做,但如果我尝试更改任何其他列表项,它工作正常!这太奇怪了!这是我试过的:
//Set the text of the first item. This does NOTHING and the TextView
//in the item remains unchanged
IMOModGroup modGroup = modGroups.get(0);
modGroup.setTestText("This is some test text");
modList.notifyDataSetChanged();
//Set the text of the second item. This works completely fine!!!!!
IMOModGroup modGroup = modGroups.get(1);
modGroup.setTestText("This is some test text");
modList.notifyDataSetChanged();
嗯,我找到了解决这个问题的方法。只是研究关于 BaseAdapters 和 getView()
的随机 SO 问题,我终于遇到了 this post by Romain Guy,他在其中声明你永远不应该将 wrap_content
用于 ListView 的 layout_height
,因为它导致 ListView 多次调用适配器的 getView()
只是为了创建用于测量内容高度的虚拟视图。原来这是导致我的问题的原因,所以将我的 layout_height
更改为 match_parent
就成功了。
不幸的是,现在这导致了一个新问题,因为我无法使用 match_parent
,因为它会隐藏我父布局中的其他视图。但至少第一个列表项现在正在正确更新。
更新:
我了解了如何在填充适配器后手动和动态设置 ListView 的高度。我使用了下面的方法,希望这能帮助 运行 遇到类似问题的人。这样我就不必使用 wrap_content
但我仍然可以将 ListView
高度设置为与其内容一样大。
private void setModListHeight() {
int numberOfItems = modGroups.size(); //the ArrayList providing data for my ListView
int totalItemsHeight = 0;
for(int position = 0; position < numberOfItems; position++) {
View item = modListAdapter.getView(position, null, modList);
item.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
totalItemsHeight += item.getMeasuredHeight();
}
int totalDividersHeight = modList.getDividerHeight() * (numberOfItems - 1);
ViewGroup.LayoutParams params = modList.getLayoutParams();
params.height = totalItemsHeight + totalDividersHeight;
modList.setLayoutParams(params);
modList.requestLayout();
}