Android:不确定为什么我的列表视图变得混乱?
Android: Not sure why my listview is getting all jumbled up?
我实现了一个可以正常工作的 ListView,直到我添加了超过 5 个项目和 2 个 headers。我不完全确定为什么有些项目没有出现而其他项目多次出现。解决此问题的任何帮助将不胜感激。代码包含在下面。
Toolbox.java
public class Toolbox extends Fragment {
private ListView lstView;
private View rootView;
List<Tools> tools;
public static Toolbox newInstance(Context context) {
Toolbox fragment = new Toolbox();
return fragment;
}
public Toolbox() {
// Required empty public constructor
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
((MainActivity) getActivity()).setActionBarTitle("Technical Toolbox");
rootView = inflater.inflate(R.layout.fragment_toolbox, container, false);
lstView =(ListView) rootView.findViewById(R.id.list);
final FragmentActivity c = getActivity();
//final RecyclerView recyclerView = (RecyclerView) recView.findViewById(R.id.reView);
setToolBoxData();
setToolBoxAdapter();
return rootView;
}
private void setToolBoxAdapter() {
ListArrayAdapter adapter = new ListArrayAdapter(getActivity(),tools);
lstView.setAdapter(adapter);
}
private void setToolBoxData(){
tools=new ArrayList<>();
tools.add(new Header("Languages"));
tools.add(new ListItem("Android",R.drawable.ic_android));
tools.add(new ListItem("XML", R.drawable.ic_xml));
tools.add(new ListItem("Java",R.drawable.ic_java));
tools.add(new ListItem("JavaScript", R.drawable.ic_javascript));
tools.add(new ListItem("C++", R.drawable.ic_cpp));
tools.add(new ListItem("Visual Basic", R.drawable.ic_vb));
tools.add(new ListItem("HTML", R.drawable.ic_html));
tools.add(new ListItem("CSS", R.drawable.ic_css));
tools.add(new Header("Source Control"));
tools.add(new ListItem("Git", R.drawable.ic_git));
tools.add(new ListItem("GitHub", R.drawable.ic_github_cat));
tools.add(new ListItem("SourceTree", R.drawable.ic_sourcetree));
tools.add(new ListItem("BitBucket", R.drawable.ic_bitbucket));
tools.add(new Header("DataBase"));
tools.add(new ListItem("Parse", R.drawable.ic_parse));
tools.add(new ListItem("MS Access", R.drawable.ic_access));
tools.add(new ListItem("SQL", R.drawable.ic_sql));
tools.add(new Header("Design & IDE Tools"));
tools.add(new ListItem("Android Studio", R.drawable.ic_androidstudio));
tools.add(new ListItem("Visual Studio", R.drawable.ic_visual_studio));
tools.add(new ListItem("Genymotion", R.drawable.ic_genymotion));
tools.add(new ListItem("Ionic", R.drawable.ic_ionic));
tools.add(new Header("Office Tools"));
tools.add(new ListItem("MS Project", R.drawable.ic_project));
tools.add(new ListItem("MS Visio", R.drawable.ic_visio));
tools.add(new ListItem("MS Excel", R.drawable.ic_excel));
tools.add(new ListItem("MS Word", R.drawable.ic_word));
}
}
ListArrayAdapter.java
public class ListArrayAdapter extends ArrayAdapter<Tools> {
private LayoutInflater mInflater;
public enum RowType{ LIST_ITEM, HEADER_ITEM }
public ListArrayAdapter(Context context, List<Tools> tools){
super(context, 0, tools);
mInflater = LayoutInflater.from(context);
}
@Override
public int getViewTypeCount(){return RowType.values().length;}
@Override
public int getItemViewType(int pos){return getItem(pos).getViewType();}
private static final int TYPE_ITEM = 0;
private static final int TYPE_SEPARATOR = 1;
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
int rowType = getItemViewType(position);
View View;
if (convertView == null) {
holder = new ViewHolder();
switch (rowType) {
case TYPE_ITEM:
convertView = mInflater.inflate(R.layout.listview_item, null);
holder.View=getItem(position).getView(mInflater, convertView);
break;
case TYPE_SEPARATOR:
convertView = mInflater.inflate(R.layout.list_title, null);
holder.View=getItem(position).getView(mInflater, convertView);
break;
}
convertView.setTag(holder);
}
else
{
holder = (ViewHolder) convertView.getTag();
}
return convertView;
}
public static class ViewHolder {
public View View;
}
}
Tools.java
public interface Tools {
public int getViewType();
public View getView(LayoutInflater inflater, View convertView);
}
ListItem.java
public class ListItem implements Tools {
private final String str1;
private final int pic1;
public ListItem(String text1, int pic1) {
this.str1 = text1;
this.pic1 = pic1;
}
@Override
public int getViewType() {
return ListArrayAdapter.RowType.LIST_ITEM.ordinal();
}
@Override
public View getView(LayoutInflater inflater, View convertView) {
View view;
if (convertView == null) {
view = (View) inflater.inflate(R.layout.listview_item, null);
// Do some initialization
} else {
view = convertView;
}
TextView text1 = (TextView) view.findViewById(R.id.listText);
ImageView picture1 = (ImageView) view.findViewById(R.id.listIcon);
text1.setText(str1);
picture1.setImageResource(pic1);
return view;
}
}
Header.java
public class Header implements Tools {
private final String name;
public Header(String name) {
this.name = name;
}
@Override
public int getViewType() {
return ListArrayAdapter.RowType.HEADER_ITEM.ordinal();
}
@Override
public View getView(LayoutInflater inflater, View convertView) {
View view;
if (convertView == null) {
view = (View) inflater.inflate(R.layout.header, null);
// Do some initialization
} else {
view = convertView;
}
TextView text = (TextView) view.findViewById(R.id.separator);
text.setText(name);
return view;
}
}
您只是在 if (convertView == null)
情况下填充行小部件。如果 convertView
不是 null
,您只是将其原封不动地返回,这意味着它将包含来自先前 position
的数据,而不是请求的 position
。
IOW,您需要在 每个 getView()
调用中调用 setText()
和 setImageResource()
等方法,以填充行的小部件请求的数据 position
.
与其使用职位,不如使用某种形式的 ID 来确保唯一性。
此外,由于视图被回收,您需要使用新数据更新它们。
if (convertView == null) {
holder = new ViewHolder();
switch (rowType) {
case TYPE_ITEM:
convertView = mInflater.inflate(R.layout.listview_item, null);
holder.View=getItem(position).getView(mInflater, convertView);
break;
case TYPE_SEPARATOR:
convertView = mInflater.inflate(R.layout.list_title, null);
holder.View=getItem(position).getView(mInflater, convertView);
break;
}
convertView.setTag(holder);
}
else
{
holder = (ViewHolder) convertView.getTag();
//here update the holder in else case also.
}
return convertView;
简要背景:
实际上我今天也遇到了同样的错误。正如所描述的那样,我也完成了 90-95% 的相同实现。但是我也看到了这个问答线程的公认答案。但我不能同意你的看法!因此,我搜索并搜索了为什么我的更简单的 ArrayAdapter
实现无法正常工作。在第一次它加载好的。但是在我滚动一次之后不久?所有类型的行都变得混乱不堪!
总结答案:
阅读 Android Developer Doc 以获取更多信息,您可能会直接找到线索。要为 ListView
的行实现多种类型的 Views
,我们基本上必须实现 getItemViewType()
和 getViewTypeCount()
方法。并且 getItemViewType()
文档给了我们如下注释:
Note: Integers must be in the range 0
to getViewTypeCount() - 1
.
IGNORE_ITEM_VIEW_TYPE
can also be returned.
因此,在您的 getItemViewType()
中,您应该 return 视图类型的值,从 0 开始,到最后一个类型为(类型数量 - 1)。例如,假设您只有两种类型的视图?因此,根据视图的数据对象,您只能在 getItemViewType()
方法中使用 return 0 或 1。
如果您没有得到线索或仍然无法解决并需要更多信息,请阅读下面的内容:
详细解释:
According to the Android Developer Documentation or the API Doc, it
has mentioned that in order to implement multiple types of views within
ListView
adapter, we have to implement the following methods:
public int getItemViewType (int position)
public int getViewTypeCount ()
And out of the above two methods, getItemViewType
is essential in our
case (where we have multiple view types). That's true right? So what?
That's where me and @8BitYoda has gone wrong. The OP's wrong thing was he had defined an Enum
where it doesn't give types as range of integers
within the range of 0 to (getViewTypeCount() - 1)
.
我所做的是,我定义了如下类型:
private static final int TYPE_ITEM = 1;
private static final int TYPE_ITEM = 2;
而且一开始我没有实现,getViewTypeCount()
,所以实现如下:
public int getViewTypeCount() {
return 2;
}
然后在我 运行 应用程序并尝试使用 ListView 加载该视图后不久,它崩溃了并出现以下异常:
java.lang.ArrayIndexOutOfBoundsException: length=2; index=2
at
android.widget.AbsListView$RecycleBin.addScrapView(AbsListView.java:6643)
at android.widget.ListView.measureHeightOfChildren(ListView.java:1292)
at android.widget.ListView.onMeasure(ListView.java:1188)
:
:
:
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
说什么? :O
它间接表示它与我从方法 getViewTypeCount()
中 returning 的整数有关,因为我 returning 2。我 return 2 因为我只有两种类型。所以我回到 Android 文档寻求最后的帮助。通过阅读与我实施的方法相关的文档,它确实帮助了我并解决了我的问题。
快速浏览一下,看看下面的图片:
With a tiny little font, it has given a NOTE for us.
getItemViewType (int position)
方法的注释说了什么?
Note: Integers must be in the range 0
to getViewTypeCount() - 1
.
IGNORE_ITEM_VIEW_TYPE
can also be returned.
所以在看到这个之后我明白我需要重新分配 type integers
我发送到从 0 到我拥有的任何类型的数量,比如基于零的数组索引。所以我重新纠正了这两行,之后它就像一个魅力!
private static final int TYPE_ITEM = 0;
private static final int TYPE_ITEM = 1;
抱歉,我知道我的回答比我预期的要长一些,几乎就像一个迷你教程。但对我来说没关系,我花了大约一个多小时来写这篇文章,因为我要花大约 5 个小时来弄清楚这个愚蠢的错误。谁认为类型应该从 0 开始分配,例如基于 0 的数组索引?
所以我希望我的回答可能对解决此错误的人有所帮助。
干杯!!!
我实现了一个可以正常工作的 ListView,直到我添加了超过 5 个项目和 2 个 headers。我不完全确定为什么有些项目没有出现而其他项目多次出现。解决此问题的任何帮助将不胜感激。代码包含在下面。
Toolbox.java
public class Toolbox extends Fragment {
private ListView lstView;
private View rootView;
List<Tools> tools;
public static Toolbox newInstance(Context context) {
Toolbox fragment = new Toolbox();
return fragment;
}
public Toolbox() {
// Required empty public constructor
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
((MainActivity) getActivity()).setActionBarTitle("Technical Toolbox");
rootView = inflater.inflate(R.layout.fragment_toolbox, container, false);
lstView =(ListView) rootView.findViewById(R.id.list);
final FragmentActivity c = getActivity();
//final RecyclerView recyclerView = (RecyclerView) recView.findViewById(R.id.reView);
setToolBoxData();
setToolBoxAdapter();
return rootView;
}
private void setToolBoxAdapter() {
ListArrayAdapter adapter = new ListArrayAdapter(getActivity(),tools);
lstView.setAdapter(adapter);
}
private void setToolBoxData(){
tools=new ArrayList<>();
tools.add(new Header("Languages"));
tools.add(new ListItem("Android",R.drawable.ic_android));
tools.add(new ListItem("XML", R.drawable.ic_xml));
tools.add(new ListItem("Java",R.drawable.ic_java));
tools.add(new ListItem("JavaScript", R.drawable.ic_javascript));
tools.add(new ListItem("C++", R.drawable.ic_cpp));
tools.add(new ListItem("Visual Basic", R.drawable.ic_vb));
tools.add(new ListItem("HTML", R.drawable.ic_html));
tools.add(new ListItem("CSS", R.drawable.ic_css));
tools.add(new Header("Source Control"));
tools.add(new ListItem("Git", R.drawable.ic_git));
tools.add(new ListItem("GitHub", R.drawable.ic_github_cat));
tools.add(new ListItem("SourceTree", R.drawable.ic_sourcetree));
tools.add(new ListItem("BitBucket", R.drawable.ic_bitbucket));
tools.add(new Header("DataBase"));
tools.add(new ListItem("Parse", R.drawable.ic_parse));
tools.add(new ListItem("MS Access", R.drawable.ic_access));
tools.add(new ListItem("SQL", R.drawable.ic_sql));
tools.add(new Header("Design & IDE Tools"));
tools.add(new ListItem("Android Studio", R.drawable.ic_androidstudio));
tools.add(new ListItem("Visual Studio", R.drawable.ic_visual_studio));
tools.add(new ListItem("Genymotion", R.drawable.ic_genymotion));
tools.add(new ListItem("Ionic", R.drawable.ic_ionic));
tools.add(new Header("Office Tools"));
tools.add(new ListItem("MS Project", R.drawable.ic_project));
tools.add(new ListItem("MS Visio", R.drawable.ic_visio));
tools.add(new ListItem("MS Excel", R.drawable.ic_excel));
tools.add(new ListItem("MS Word", R.drawable.ic_word));
}
}
ListArrayAdapter.java
public class ListArrayAdapter extends ArrayAdapter<Tools> {
private LayoutInflater mInflater;
public enum RowType{ LIST_ITEM, HEADER_ITEM }
public ListArrayAdapter(Context context, List<Tools> tools){
super(context, 0, tools);
mInflater = LayoutInflater.from(context);
}
@Override
public int getViewTypeCount(){return RowType.values().length;}
@Override
public int getItemViewType(int pos){return getItem(pos).getViewType();}
private static final int TYPE_ITEM = 0;
private static final int TYPE_SEPARATOR = 1;
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
int rowType = getItemViewType(position);
View View;
if (convertView == null) {
holder = new ViewHolder();
switch (rowType) {
case TYPE_ITEM:
convertView = mInflater.inflate(R.layout.listview_item, null);
holder.View=getItem(position).getView(mInflater, convertView);
break;
case TYPE_SEPARATOR:
convertView = mInflater.inflate(R.layout.list_title, null);
holder.View=getItem(position).getView(mInflater, convertView);
break;
}
convertView.setTag(holder);
}
else
{
holder = (ViewHolder) convertView.getTag();
}
return convertView;
}
public static class ViewHolder {
public View View;
}
}
Tools.java
public interface Tools {
public int getViewType();
public View getView(LayoutInflater inflater, View convertView);
}
ListItem.java
public class ListItem implements Tools {
private final String str1;
private final int pic1;
public ListItem(String text1, int pic1) {
this.str1 = text1;
this.pic1 = pic1;
}
@Override
public int getViewType() {
return ListArrayAdapter.RowType.LIST_ITEM.ordinal();
}
@Override
public View getView(LayoutInflater inflater, View convertView) {
View view;
if (convertView == null) {
view = (View) inflater.inflate(R.layout.listview_item, null);
// Do some initialization
} else {
view = convertView;
}
TextView text1 = (TextView) view.findViewById(R.id.listText);
ImageView picture1 = (ImageView) view.findViewById(R.id.listIcon);
text1.setText(str1);
picture1.setImageResource(pic1);
return view;
}
}
Header.java
public class Header implements Tools {
private final String name;
public Header(String name) {
this.name = name;
}
@Override
public int getViewType() {
return ListArrayAdapter.RowType.HEADER_ITEM.ordinal();
}
@Override
public View getView(LayoutInflater inflater, View convertView) {
View view;
if (convertView == null) {
view = (View) inflater.inflate(R.layout.header, null);
// Do some initialization
} else {
view = convertView;
}
TextView text = (TextView) view.findViewById(R.id.separator);
text.setText(name);
return view;
}
}
您只是在 if (convertView == null)
情况下填充行小部件。如果 convertView
不是 null
,您只是将其原封不动地返回,这意味着它将包含来自先前 position
的数据,而不是请求的 position
。
IOW,您需要在 每个 getView()
调用中调用 setText()
和 setImageResource()
等方法,以填充行的小部件请求的数据 position
.
与其使用职位,不如使用某种形式的 ID 来确保唯一性。
此外,由于视图被回收,您需要使用新数据更新它们。
if (convertView == null) {
holder = new ViewHolder();
switch (rowType) {
case TYPE_ITEM:
convertView = mInflater.inflate(R.layout.listview_item, null);
holder.View=getItem(position).getView(mInflater, convertView);
break;
case TYPE_SEPARATOR:
convertView = mInflater.inflate(R.layout.list_title, null);
holder.View=getItem(position).getView(mInflater, convertView);
break;
}
convertView.setTag(holder);
}
else
{
holder = (ViewHolder) convertView.getTag();
//here update the holder in else case also.
}
return convertView;
简要背景:
实际上我今天也遇到了同样的错误。正如所描述的那样,我也完成了 90-95% 的相同实现。但是我也看到了这个问答线程的公认答案。但我不能同意你的看法!因此,我搜索并搜索了为什么我的更简单的 ArrayAdapter
实现无法正常工作。在第一次它加载好的。但是在我滚动一次之后不久?所有类型的行都变得混乱不堪!
总结答案:
阅读 Android Developer Doc 以获取更多信息,您可能会直接找到线索。要为 ListView
的行实现多种类型的 Views
,我们基本上必须实现 getItemViewType()
和 getViewTypeCount()
方法。并且 getItemViewType()
文档给了我们如下注释:
Note: Integers must be in the range
0
togetViewTypeCount() - 1
.IGNORE_ITEM_VIEW_TYPE
can also be returned.
因此,在您的 getItemViewType()
中,您应该 return 视图类型的值,从 0 开始,到最后一个类型为(类型数量 - 1)。例如,假设您只有两种类型的视图?因此,根据视图的数据对象,您只能在 getItemViewType()
方法中使用 return 0 或 1。
如果您没有得到线索或仍然无法解决并需要更多信息,请阅读下面的内容:
详细解释:
According to the Android Developer Documentation or the API Doc, it has mentioned that in order to implement multiple types of views within
ListView
adapter, we have to implement the following methods:
public int getItemViewType (int position)
public int getViewTypeCount ()
And out of the above two methods,
getItemViewType
is essential in our case (where we have multiple view types). That's true right? So what?That's where me and @8BitYoda has gone wrong. The OP's wrong thing was he had defined an
Enum
where it doesn't give types as range ofintegers
within the range of 0 to(getViewTypeCount() - 1)
.
我所做的是,我定义了如下类型:
private static final int TYPE_ITEM = 1;
private static final int TYPE_ITEM = 2;
而且一开始我没有实现,getViewTypeCount()
,所以实现如下:
public int getViewTypeCount() {
return 2;
}
然后在我 运行 应用程序并尝试使用 ListView 加载该视图后不久,它崩溃了并出现以下异常:
java.lang.ArrayIndexOutOfBoundsException: length=2; index=2 at android.widget.AbsListView$RecycleBin.addScrapView(AbsListView.java:6643) at android.widget.ListView.measureHeightOfChildren(ListView.java:1292) at android.widget.ListView.onMeasure(ListView.java:1188) :
:
:
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
说什么? :O
它间接表示它与我从方法 getViewTypeCount()
中 returning 的整数有关,因为我 returning 2。我 return 2 因为我只有两种类型。所以我回到 Android 文档寻求最后的帮助。通过阅读与我实施的方法相关的文档,它确实帮助了我并解决了我的问题。
快速浏览一下,看看下面的图片:
With a tiny little font, it has given a NOTE for us.
getItemViewType (int position)
方法的注释说了什么?
Note: Integers must be in the range
0
togetViewTypeCount() - 1
.IGNORE_ITEM_VIEW_TYPE
can also be returned.
所以在看到这个之后我明白我需要重新分配 type integers
我发送到从 0 到我拥有的任何类型的数量,比如基于零的数组索引。所以我重新纠正了这两行,之后它就像一个魅力!
private static final int TYPE_ITEM = 0;
private static final int TYPE_ITEM = 1;
抱歉,我知道我的回答比我预期的要长一些,几乎就像一个迷你教程。但对我来说没关系,我花了大约一个多小时来写这篇文章,因为我要花大约 5 个小时来弄清楚这个愚蠢的错误。谁认为类型应该从 0 开始分配,例如基于 0 的数组索引?
所以我希望我的回答可能对解决此错误的人有所帮助。
干杯!!!