I implemented a ListView that worked correctly until I added more than 5 items and 2 headers. Im not entirely sure why some items are not appearing and others are appearing multiple times. Any assistance in fixing this will be much appreciated. Code is include below.
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;
}
}
Brief background:
Actually I came through this same error today. As it is described I've also done 90-95% identical implementation. However I saw the accepted answer of this Q&A thread also. But I couldn't agree with you! So I searched and searched why my simpler
ArrayAdapter
implementation wasn't working properly. At the first time it loads Okay. But soon after I scroll it once? All the types of rows go jumbled-bumbled!Summarized Answer:
Read the Android Developer Doc for further information you might be find the clue directly. To implement multiple types of
Views
forListView
's rows we have to essentially implement,getItemViewType()
andgetViewTypeCount()
methods. AndgetItemViewType()
documentation gives us a Note as follows:So in your
getItemViewType()
you should return values for the View Type, starting from 0, to the last type as (number of types - 1). For example, let's say you only have two types of views? So depending on the data object for the view, you could only return 0 or 1 in thegetItemViewType()
method.If you didn't get the clue or couldn't still resolve and need further information please read it down the bellow:
Detailed Explanation:
And what I had done was, I had defined the types as follows:
And at first I haven't implemented,
getViewTypeCount()
, so I implemented it as follows:Then soon after I run the app and try to load that view with the ListView, it crashed giving me the following exception:
Say what? :O
It indirectly says that it's related with the integer I'm returning from the method
getViewTypeCount()
, as I'm returning 2. And I return 2 because I have only two types. So I went back to the Android Documentation seeking the final help. And it really helped me and resolved my question, by reading the doc which relates to the methods I have implemented.For a quick snap, have a look at the image bellow:
With a tiny little font, it has given a NOTE for us.
What does the note of
getItemViewType (int position)
method says?So right after seeing this I understood that I need to re-assign the
type integers
I'm sending to starting from 0 to whatever the number of types I have, like zero based array index. So I re-corrected those two lines and it worked like a charm afterwards!Sorry, I know my answer went a little longer than I expected, almost like a mini tutorial. But it's okay for me I spent around an hour+ to write this because I'd to spend around 5 hours to figure out this silly mistake. Who thinks that types should be assigned starting from 0, like 0-based array index?
So I hope my answer might be helpful to somebody out there to resolve this error.
Cheers!!!
You are only populating your row widgets in the
if (convertView == null)
case. IfconvertView
is notnull
, you are simply returning it unchanged, meaning it will have data from some priorposition
, not theposition
that is being requested.IOW, you need to call methods like
setText()
andsetImageResource()
on everygetView()
call, to fill in the row's widgets with the data for the requestedposition
.Rather than using positions, you should use some form of id for uniqueness.
Also as the views are recycled, you need to update them with new data.