I'm using MergeAdapter because I need a listview of dynamic added/removed headers(sections) and their relevant custom adapters. When adding a new String or photo or video or a group of data content at one time, they will be added into this merged list and show relevant headers. But the problem is when adding content, MergeAdapter.getView will return a TextView not RelativeLayout which is from my custom adapter's resource, then views from this custom adapter get null value and throws NullPointerException. Custom adapter named "CustomContentListAdapter" which extends from BaseAdapter Code:
public View getView(int position, View convertView, ViewGroup parent)
{
View rowView = convertView;
Content content = getItem(position);
///*
if(rowView == null)
{
mCacheView = new CustomListCachedViewHolder();//a view holder
rowView = mLayoutInfator.inflate(mResourceId, root, false);
mCacheView.mToggleButton = (Button) rowView.findViewById(R.id.toggle_button);
mCacheView.mAudioIcon = (ImageView) rowView.findViewById(R.id.audio_icon);
mCacheView.mTextInput = (TextView) rowView.findViewById(R.id.content);
mCacheView.mThumbnail = (ImageView) rowView.findViewById(R.id.photo_thumbnail);
mCacheView.mCreateTimeLabel = (TextView) rowView.findViewById(R.id.created_time);
mCacheView.mExpandButton = (ImageView) rowView.findViewById(R.id.expand_button);
rowView.setTag(mCacheView);
}
else
{
mCacheView = (CustomListCachedViewHolder) rowView.getTag();
}
if(View.GONE != mCacheView.mToggleButton.getVisibility() ||
View.GONE != mCacheView.mAudioIcon.getVisibility())
{
mCacheView.mToggleButton.setVisibility(View.GONE);
mCacheView.mAudioIcon.setVisibility(View.GONE);
}
mCacheView.mExpandButton.setOnLongClickListener(new OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
return false;
}
});
mCacheView.mThumbnail.setImageBitmap(null);
mCacheView.mThumbnail.setVisibility(View.GONE);
mCacheView.mCreateTimeLabel.setText("");
mCacheView.mNoteEntry.setText("");
return rowView;
}
And its resource xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="5dp" >
<RelativeLayout
android:id="@+id/content_list_wrapper"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="5dp">
<RelativeLayout
android:id="@+id/detail_content_wrapper"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="5dp">
<ImageView
android:id="@+id/audio_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/demo_img"
android:background="@drawable/audio_thumb"
android:layout_centerVertical="true"
android:layout_alignParentLeft="true"
android:visibility="gone"
android:layout_margin="5dp"/>
<Button
android:id="@+id/toggle_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/demo_img"
android:layout_centerVertical="true"
android:text="@string/dummy_text"
android:layout_toRightOf="@id/audio_icon"
style="@style/cutomEditTextStyle"
android:visibility="visible"
android:layout_margin="5dp"/>
<ImageView
android:id="@+id/photo_thumbnail"
android:layout_width="100dp"
android:layout_height="100dp"
android:contentDescription="@string/demo_img"
android:layout_toRightOf="@id/toggle_button"
android:visibility="gone"
android:layout_margin="5dp"/>
<TextView
android:id="@+id/note_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/photo_thumbnail"
android:padding="5dp"/>
</RelativeLayout>
<LinearLayout
android:id="@+id/expand_delete_combo_layout"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:orientation="horizontal"
android:padding="5dp">
<ImageView
android:id="@+id/expand_right_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/demo_img"
android:background="@drawable/ic_ab_forward_holo_light"
android:layout_gravity="center_vertical"
android:layout_marginRight="10dp"/>
<Button
android:id="@+id/delete_button"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:contentDescription="@string/demo_img"
android:background="@color/color_red"
android:visibility="gone"
android:layout_margin="5dp"/>
</LinearLayout>
</RelativeLayout>
<TextView
android:id="@+id/created_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/content_list_wrapper"
android:layout_marginLeft="5dp"
android:layout_marginTop="5dp"
android:padding="5dp"/>
</RelativeLayout>
I use fragment to handle all the data, please see the below:
for(Content content : contents)
{
List<SubContent> subContents = mContentManager.getListCopy(content.getUUID());
if(!subContents.isEmpty())
{
TextView label = (TextView) View.inflate(mContext,
R.layout.titlebar_layout, null);
label.setText(mContext.getResources().getString(
R.string.content, content.getSectionNum() + " " + content.getTitle()));
mMergeAdapter.addView(label);
adapter = new CustomContentListAdapter(
mContext, R.layout.note_list_item_layout, subContents);
mCacheAdapterMap.put(content.getUUID(), adapter); // store existing adapter
mMergeAdapter.addAdapter(adapter);
mMergetAdapter2.setActive(adapter, true);
}
}
mMergedListView.setDivider(
mContext.getResources().getDrawable(R.drawable.devider_line));
mMergedListView.setDividerHeight(1);
mMergedListView.setAdapter(mMergeAdapter);
mMergedListView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
When adding new content, I did like this:
public void notifyNewContentAdded(SubContent subContent)
{
String id = subContent.getParentId();
CustomContentListAdapter adapter = mCacheAdapterMap.get(id);
List<Content> subContents = mContentManager.getCurrentListCopy(id);
Content content = mContentManager.getInstance().getCurrentContent(id);
String title = "";
if(null == adapter)
{
TextView label = (TextView) View.inflate(
mContext, R.layout.titlebar_layout, null);
if(content != null)
{
title = content.getSectionNum() + " " + content.getTitle();
}
label.setText(
mContext.getResources().getString(R.string.content, title));
mMergeAdapter.addView(stepLabel);
if(!subContents.contains(subContent))
{
subContents.add(subContent);
}
adapter = new CustomNoteListAdapter(
mContext, R.layout.list_item_layout, subContents);
mCacheAdapterMap.put(id, adapter);
mMergeAdapter.addAdapter(noteAdapter);
mMergetAdapter.setActive(noteAdapter, true);
}
else
{
if(!adapter.contains(subContent))
{
adapter.addItem(subContent);
}
adapter.notifyAdapterChange();
}
mMergeAdapter.notifyDataSetChanged();
mMergeAdapter.notifyDataSetInvalidated();
}
I don't know why convertView in MergeAdapter.getView() will be TextView when the position comes to my custom adapter. And if I changed if(rowView == null) to if(rowView == null || rowView instanceof TextView), the NullPointerException will be gone. But another blocker jumps out when scrolling down the merged listview. It throws ArrayIndexOutOfBoundException. Log info as below:
11-18 13:26:24.687: E/ApplicationErrorHandler(10971): Entered uncaughtException
11-18 13:26:24.687: E/ApplicationErrorHandler(10971): java.lang.ArrayIndexOutOfBoundsException: length=8; index=9
11-18 13:26:24.687: E/ApplicationErrorHandler(10971): at android.widget.AbsListView$RecycleBin.addScrapView(AbsListView.java:8269)
11-18 13:26:24.687: E/ApplicationErrorHandler(10971): at android.widget.AbsListView.trackMotionScroll(AbsListView.java:6629)
11-18 13:26:24.687: E/ApplicationErrorHandler(10971): at android.widget.AbsListView.scrollIfNeeded(AbsListView.java:3910)
11-18 13:26:24.687: E/ApplicationErrorHandler(10971): at android.widget.AbsListView.onTouchMove(AbsListView.java:4762)
11-18 13:26:24.687: E/ApplicationErrorHandler(10971): at android.widget.AbsListView.onTouchEvent(AbsListView.java:4590)
11-18 13:26:24.687: E/ApplicationErrorHandler(10971): at android.view.View.dispatchTouchEvent(View.java:8135)
11-18 13:26:24.687: E/ApplicationErrorHandler(10971): at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2416)
11-18 13:26:24.687: E/ApplicationErrorHandler(10971): at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2140)
11-18 13:26:24.687: E/ApplicationErrorHandler(10971): at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2422)
11-18 13:26:24.687: E/ApplicationErrorHandler(10971): at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2155)
11-18 13:26:24.687: E/ApplicationErrorHandler(10971): at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2422)
11-18 13:26:24.687: E/ApplicationErrorHandler(10971): at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2155)
11-18 13:26:24.687: E/ApplicationErrorHandler(10971): at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2422)
11-18 13:26:24.687: E/ApplicationErrorHandler(10971): at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2155)
11-18 13:26:24.687: E/ApplicationErrorHandler(10971): at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2422)
11-18 13:26:24.687: E/ApplicationErrorHandler(10971): at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2155)
11-18 13:26:24.687: E/ApplicationErrorHandler(10971): at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2422)
11-18 13:26:24.687: E/ApplicationErrorHandler(10971): at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2155)
11-18 13:26:24.687: E/ApplicationErrorHandler(10971): at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2422)
11-18 13:26:24.687: E/ApplicationErrorHandler(10971): at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2155)
11-18 13:26:24.687: E/ApplicationErrorHandler(10971): at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2422)
11-18 13:26:24.687: E/ApplicationErrorHandler(10971): at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2155)
11-18 13:26:24.687: E/ApplicationErrorHandler(10971): at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2422)
11-18 13:26:24.687: E/ApplicationErrorHandler(10971): at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2155)
11-18 13:26:24.687: E/ApplicationErrorHandler(10971): at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2422)
11-18 13:26:24.687: E/ApplicationErrorHandler(10971): at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2155)
It's surely highly appreciated with any constructive advice.