Reputation: 390
I am currently developing a content-creation application for Android. It´s main purpose is to let the user generate a list out of text and image entries (represented in the end by EditText and ImageView).
I hooked this list up to a ListView by writing a custom ArrayAdapter whilst using the ViewHolder Pattern
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// get current Entry
Entry e = getItem(position);
// variable for holding the view
ViewHolder holder;
// check if there is no view created
if (convertView == null) {
// prepare holder for view to be saved in
holder = new ViewHolder();
// get layout inflater
LayoutInflater inflater = (LayoutInflater) super.getContext()
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
// if current entry is an image entry
if (e.getType().equals(Entry.Type.IMAGE_ENTRY)) {
// inflate layout with ImageView
convertView = inflater.inflate(R.layout.image_list_item,
parent, false);
ImageView iView = (ImageView) convertView
.findViewById(R.id.imageDisplay);
// save inflated view in holder
holder.imageViewItem = iView;
// store the holder with the view
convertView.setTag(holder);
// if it is a text entry
} else if (e.getType().equals(Entry.Type.TEXT_ENTRY)) {
// inflate layout with EditText
convertView = inflater.inflate(R.layout.text_list_item, parent,
false);
EditText tView = (EditText) convertView
.findViewById(R.id.textField);
// save inflated view in holder
holder.editTextItem = tView;
// store the holder with the view
convertView.setTag(holder);
}
} else {
// get holder from existing view
holder = (ViewHolder) convertView.getTag();
}
if (e != null) {
if (e.getType().equals(Entry.Type.IMAGE_ENTRY)) {
// load corresponding image
holder.imageViewItem.setImageBitmap(((ImageEntry) e).getImage());
} else if (e.getType().equals(Entry.Type.TEXT_ENTRY)) {
// load corresponding text
holder.editTextItem.setText(((TextEntry) e).getText());
}
}
return convertView;
}
Contrary to tutorials and examples I´ve seen I need to distinguish between the layouts I inflate due to the difference between text and image entries. And this seems to be causing some trouble because sometimes after adding different entries after another I get these exceptions
E/AndroidRuntime(15812): java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.EditText.setText(java.lang.CharSequence)' on a null object reference
E/AndroidRuntime(15812): at <project>.util.EntryListAdapter.getView(EntryListAdapter.java:104)
So, it seems to me that the method gets called on the wrong view because otherwise there has to be an object of the type ‘EditText’. I´m not quite certain when the ListView triggers all its views to be redrawn and I´ve been experimenting with my adapter and list a lot but I can´t work out why it sometimes messes up the calls for my entries. Can anyone help me there?
Upvotes: 3
Views: 127
Reputation: 157457
if you don't override getViewTypeCount
, you will get only one null convertView
, so you will initialize it with image_list_item.xml
or text_list_item.xml
. If you want to handle differnt types of views override
public int getViewTypeCount() {
return 2;
}
to handle two different types of view, and getItemViewType
public int getItemViewType (int position) {
Entry e = getItem(position);
if (e.getType().equals(Entry.Type.IMAGE_ENTRY)) {
return 0;
}
return 1;
}
and in getView
check for the return value of getItemViewType
Upvotes: 2
Reputation: 6749
The ViewHolder pattern for ListView should be used when you know the view types of the positions before hand. If they are going to change dynamically when new positions are added (in essence if notifyDataSetChanged()
is being called) then there are problems with the recycling.
For dynamically changing collections with different types of views it is better to use RecyclerView
Upvotes: 1