Reputation: 3909
I'm trying to create an ExpandableListView
in my app. It has 2 groups: the first is colours, and the second is symbols. The first group clicked on works fine. The second group, however, shows the rows from the first group (if the second group has more items in it, then the 'extra' ones will be correct).
Eg let's say the 'colours' are white, black, red and blue, and the symbols are '/' and '.'.
If I start the activity and click on colours, then they appear correctly. If I then click on 'symbols', I see white and black.
If I click 'symbols' first, then I see '/' and '.', but when I then click on colours I see '/', '.', red, blue.
I searched online and have established that I need to use ViewHolders to avoid it reusing the same view when I change groups. I haven't been able to implement it though. At first it didn't make any difference, and the current version is crashing out when I click the second group. I think part of the issue is that I have a different child layout for each group (ie the symbols are shown differently from the colours).
Currently here's what I have (I've shown what I think are the relevant bits; if I've left out anything important, I can add it in):
public class ColourSymbolKeyAdapter extends BaseExpandableListAdapter {
private Context context;
private HashMap<String, List<KeyItem>> childDataSource;
private List<String> parentDataSource;
public ColourSymbolKeyAdapter(Context context,
List<String> childParent,
HashMap<String, List<KeyItem>> child) {
this.context = context;
this.parentDataSource = childParent;
this.childDataSource = child;
}
... Left out various override functions ...
@Override
public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
View v = convertView;
GroupViewHolder holder;
if(v == null) {
LayoutInflater inflater =
(LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = inflater.inflate(R.layout.expandablelist_parent, parent, false);
holder = new GroupViewHolder();
holder.mGroupName = (TextView) v.findViewById(R.id.textViewParent);
v.setTag(holder);
}else {
holder = (GroupViewHolder) v.getTag();
}
String parentHeader = (String) getGroup(groupPosition);
holder.mGroupName.setText(parentHeader);
return v;
}
@Override
public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
View row = convertView;
KeyItem childItem = (KeyItem) getChild(groupPosition, childPosition);
ColourViewHolder colourviewholder;
SymbolViewHolder symbolviewholder;
LayoutInflater inflater =
(LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if(childItem.getPatternColour() == null) { // This is a symbol row
if(row == null) {
symbolviewholder = new SymbolViewHolder();
row = inflater.inflate(R.layout.expandablelist_child_symbol, parent, false);
symbolviewholder.mChildName = (TextView) row.findViewById(R.id.symbol_desc);
symbolviewholder.mSymbolCell = (SymbolCell) row.findViewById(R.id.symbol_cell);
row.setTag(symbolviewholder);
}else {
symbolviewholder = (SymbolViewHolder) row.getTag();
}
String drawableName = childItem.getPatternSymbol().getDrawable();
final int resourceId = context.getResources().getIdentifier(drawableName, "drawable", context.getPackageName());
Drawable drawable;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
drawable = context.getResources().getDrawable(resourceId, context.getTheme());
} else {
drawable = context.getResources().getDrawable(resourceId);
}
symbolviewholder.mSymbolCell.setDrawable(drawable);
symbolviewholder.mChildName.setText(childItem.getPatternSymbol().getSymbolDescription());
}else { // This is a colour row
if(row == null) {
colourviewholder = new ColourViewHolder();
row = inflater.inflate(R.layout.expandablelist_child_colour, parent, false);
colourviewholder.mChildName = (TextView) row.findViewById(R.id.colour_name);
colourviewholder.mChildDesc = (TextView) row.findViewById(R.id.colour_desc);
colourviewholder.mColourCell = (ColourCell) row.findViewById(R.id.colour_cell);
row.setTag(colourviewholder);
}else {
colourviewholder = (ColourViewHolder) row.getTag();
}
colourviewholder.mColourCell.setColour(childItem.getPatternColour());
colourviewholder.mChildName.setText(childItem.getPatternColour().getName());
colourviewholder.mChildDesc.setText(childItem.getPatternColour().getDescription());
}
return row;
}
public final class GroupViewHolder {
TextView mGroupName;
}
public final class ColourViewHolder {
ColourCell mColourCell;
TextView mChildName, mChildDesc;
}
public final class SymbolViewHolder {
SymbolCell mSymbolCell;
TextView mChildName;
}
}
Layout for the parent (expandablelist_parent.xml):
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/parentView">
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/textViewParent"
android:textColor="#000000"
android:textAppearance="@style/ParagraphBold">
</TextView>
</LinearLayout>
Layout for the colour rows (expandablelist_child_colour.xml):
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="@dimen/small_padding"
android:id="@+id/childViewColour"
android:orientation="horizontal">
<com.myname.appname.ColourCell xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="@dimen/grid_cell_column_width"
android:layout_height="@dimen/grid_cell_column_width"
android:id="@+id/colour_cell"
android:layout_marginRight="@dimen/small_padding" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="@style/ParagraphBold"
android:textColor="@color/colorPrimaryDark"
android:id="@+id/colour_name">
</TextView>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="@style/Paragraph"
android:textColor="@color/colorPrimaryDark"
android:id="@+id/colour_desc">
</TextView>
</LinearLayout>
</LinearLayout>
and for the symbol rows (expandablelist_child_symbol.xml):
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/childViewSymbol"
android:padding="@dimen/small_padding">
<com.myname.appname.SymbolCell xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="@dimen/grid_cell_column_width"
android:layout_height="@dimen/grid_cell_column_width"
android:textAppearance="@style/Paragraph"
android:id="@+id/symbol_cell"
android:layout_marginRight="@dimen/small_padding" />
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:textColor="@color/colorPrimaryDark"
android:id="@+id/symbol_desc">
</TextView>
</LinearLayout>
If I click the 'colour' group first, and then the 'symbol' group, it crashes out on the line: symbolviewholder = (SymbolViewHolder) row.getTag()
.
Upvotes: 4
Views: 2867
Reputation: 6988
You can easily achieve it with this library, there is a full example here.
Basically you group your items into sections:
class MySection extends StatelessSection {
String header;
List<String> list;
boolean expanded = true;
public MySection(String header, List<String> list) {
// call constructor with layout resources for this Section header and items
super(R.layout.section_header, R.layout.section_item);
this.myHeader = header;
this.myList = list;
}
@Override
public int getContentItemsTotal() {
return expanded? list.size() : 0;
}
@Override
public RecyclerView.ViewHolder getHeaderViewHolder(View view) {
return new HeaderViewHolder(view);
}
@Override
public void onBindHeaderViewHolder(RecyclerView.ViewHolder holder) {
final HeaderViewHolder headerHolder = (HeaderViewHolder) holder;
headerHolder.tvTitle.setText(title);
headerHolder.rootView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
expanded = !expanded;
headerHolder.imgArrow.setImageResource(
expanded ? R.drawable.ic_keyboard_arrow_up_black_18dp : R.drawable.ic_keyboard_arrow_down_black_18dp
);
sectionAdapter.notifyDataSetChanged();
}
});
}
@Override
public RecyclerView.ViewHolder getItemViewHolder(View view) {
// return a custom instance of ViewHolder for the items of this section
return new MyItemViewHolder(view);
}
@Override
public void onBindItemViewHolder(RecyclerView.ViewHolder holder, int position) {
MyItemViewHolder itemHolder = (MyItemViewHolder) holder;
// bind your view here
itemHolder.tvItem.setText(list.get(position));
}
}
Then create instance of your sections and set up your adapter:
// Create an instance of SectionedRecyclerViewAdapter
SectionedRecyclerViewAdapter sectionAdapter = new SectionedRecyclerViewAdapter();
// Add your Sections
sectionAdapter.addSection(new MySection("Colours", Arrays.asList(new String[] {"white", "red", "black", "blue" })));
sectionAdapter.addSection(new MySection("Symbols", Arrays.asList(new String[] {"/", "." })));
// Set up your RecyclerView with the SectionedRecyclerViewAdapter
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerview);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
recyclerView.setAdapter(sectionAdapter);
Upvotes: 1
Reputation: 31
I think it will be better to use Expendable Recyclerview to solve your problem using custom viewholder.for more information see the link. https://guides.codepath.com/android/Heterogenous-Layouts-inside-RecyclerView
Upvotes: 1
Reputation: 12929
Since you have 2 different child layouts, you need to override getChildTypeCount()
and getChildType()
so the adapter will receive the proper view type to reuse. Otherwise, in some cases you will get a ClassCastException when trying to retrieve your ViewHolder.
@Override
public int getChildTypeCount() {
return 2;
}
@Override
public int getChildType(int groupPosition, int childPosition) {
KeyItem childItem = (KeyItem) getChild(groupPosition, childPosition);
return (childItem.getPatternColour() == null) ? 0 : 1;
}
For more info, see HeterogeneousExpandableList
documentation.
Upvotes: 2