ExpandableListView + custom ExpandableListAdapter to implement complex lists

ExpandableListView is a subclass of ListView. It extends ListView and implements item grouping of list items. Its list items are provided by ExpandableListAdapter. ExpandableListAdapter is an interface, and the implementation of this interface is an abstract class BaseExpandableListAdapter.

1. Goal Realize a list of complex functions on mainstream APPs through ExpandableListView and customized ExpandableListAdapter.

2. Final renderings

3. Implementation

3.1. Layout.xml The main layout only requires one view.

3.2, To implement a custom ExpandableListAdapter you need to inherit BaseExpandableListAdapter and implement all abstract methods.

public class MyExpandableListAdapter extends BaseExpandableListAdapter {
    private Context context;
    private List<Item> mData;

    public MyExpandableListAdapter(Context context, List<Item> mData){
        this.context = context;
        this.mData = mData;
    }
    @Override
    public int getGroupCount() {
        return mData.size();
    }
    @Override
    public int getChildrenCount(int groupPosition) {
        return mData.get(groupPosition).sonText.length;
    }
    @Override
    public Object getGroup(int groupPosition) {
        return mData.get(groupPosition).groupText;
    }
    @Override
    public Object getChild(int groupPosition, int childPosition) {
        return mData.get(groupPosition).sonText[childPosition];
    }
    @Override
    public long getGroupId(int groupPosition) {
        return groupPosition;
    }
    @Override
    public long getChildId(int groupPosition, int childPosition) {
        return childPosition;
    }
    @Override
    public boolean hasStableIds() {
        return false;
    }
    @Override
    public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
        LinearLayout ll;
        ViewHolder vh;
        if (convertView == null){
            ll = new LinearLayout(context);
            ll.setOrientation(LinearLayout.HORIZONTAL);
            ImageView imv = new ImageView(context);
            ViewGroup.LayoutParams lp1 = new ViewGroup.LayoutParams(250, 250);
            imv.setLayoutParams(lp1);
            imv.setPadding(100, 0, 0, 0);
            ll.addView(imv);
            TextView tv = new TextView(context);
            tv.setPadding(40, 0, 0, 0);
            ViewGroup.LayoutParams lp2 = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
            tv.setLayoutParams(lp2);
            tv.setGravity(Gravity.CENTER_VERTICAL);
            tv.setTextSize(20f);
            ll.addView(tv);
            vh = new ViewHolder(imv, tv);
            ll.setTag(vh);
        } else {
            ll = (LinearLayout) convertView;
            vh = (ViewHolder) ll.getTag();
        }
        vh.imv.setImageResource(mData.get(groupPosition).imgId);
        vh.tv.setText(getGroup(groupPosition).toString());
        return ll;
    }
    @Override
    public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
        TextViewtv;
        if (convertView == null){
            tv = new TextView(context);
            ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
            tv.setLayoutParams(lp);
            tv.setGravity(Gravity.CENTER);
            tv.setTextSize(18f);
        }else{
            tv = (TextView) convertView;
        }
        tv.setText(getChild(groupPosition, childPosition).toString());
        return tv;
    }
    @Override
    public boolean isChildSelectable(int groupPosition, int childPosition) {
        return true;
    }
}
class ViewHolder{
    ImageView imv;
    TextViewtv;
    public ViewHolder(ImageView imv, TextView tv) {
        this.imv = imv;
        this.tv = tv;
    }
}

Introduction to related rewriting method functions:

public int getGroupCount(): Returns the number of grouped items

public int getChildrenCount(int groupPosition): Returns the number of child items in a specific group

public Object getGroup(int groupPosition): Returns a specific group item

public Object getChild(int groupPosition, int childPosition): Returns the child item at a specific position in a specific group

itempublic long getGroupId(int groupPosition): Returns the ID of the grouped item

public long getChildId(int groupPosition, int childPosition): Returns the child item ID of a specific position in a specific group

public boolean hasStableIds(): Indicates whether the grouping and sub-options hold stable ids. Return false, and use getxxxId to determine which ones require getxxxView to achieve the effect of partial refresh, which can optimize the effect when getxxxView is time-consuming.

public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent): is the most important method. Get the parent list item view. convertView parameter: Commonly used Adapters in Android will involve the use of convertView. The main purpose of using convertView is to cache the view view and increase the loading efficiency of the item view of the ListView. In the getxxxView of the Adapter, first determine whether the convertView is null. If it is not null, directly reuse the convertView, otherwise a new View will be created. The ViewHolder class is the holder of the view component and encapsulates the related components in the layout for easy use. setTag() and getTag() associate the ViewHolder object with the layout, which can be understood as labeling the ViewHolder object so that it belongs to the corresponding layout. Because the ViewHolder used by different layouts may be different.

public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent): Refer to the above to return the view of the child item.

public boolean isChildSelectable(int groupPosition, int childPosition): Whether the child item is clickable.

3.3. Encapsulated data class

public class Item {
    int imgId;
    String groupText;
    String[] sonText;

    public Item(int imgId, String groupText, String[] sonText) {
        this.imgId = imgId;
        this.groupText = groupText;
        this.sonText = sonText;
    }
}

3.4, MainActivity

public class MainActivity extends AppCompatActivity {

    public int[] imgs = new int[] {R.drawable.book, R.drawable.clothes, R.drawable.foot, R.drawable.movie};
    public String[] gt = new String[]{"Collection Bookshelf", "Fashion Clothing", "Delicious Snacks", "Hot Movies"};
    public String[][] items = new String[][] {
            new String[]{"Special Theory of Relativity", "A Brief History of Time", "Robinson Crusoe", "Andersen's Fairy Tales", "Qiongmeka's Random Notes"},
            new String[]{"cardigan", "vintage denim", "silk cheongsam", "mid-length wool coat"},
            new String[]{"Donkey meat roasted on fire", "Really fragrant roast duck", "Three squirrels", "Stull spicy strips", "Potato chips", "Wahaha drinks"},
            new String[]{"Feng Shen Bang", "Predecessor 3", "Wandering Earth", "Above the Cliff", "Avengers", "King Kong vs. Godzilla"}
    };
    private ExpandableListView elv;
    private MyExpandableListAdapter mela;
    public List<Item> data = new ArrayList<>();
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        elv = findViewById(R.id.elv);
        for (int i = 0; i < imgs.length; i + + ) {
            Item it = new Item(imgs[i], gt[i], items[i]);
            data.add(it);
        }
        mela = new MyExpandableListAdapter(this, data);
        elv.setAdapter(mela);

    }
}