Giter Club home page Giter Club logo

expandable-recycler-view's Introduction

Build Quality Gate Status

ExpandableRecyclerView

This is an HarmonyOS library with Custom BaseItemProvider for expanding and collapsing groups.

Source

This library is inspired by version 1.5 of ExpandableRecyclerView library.

Features

This library allows us to add child items within the group items in a List View. It also demonstrates how we can add our favourite, single checker and multichecker child Items.

Dependency

  1. For using ExpandableRecyclerView module in sample app, include the source code and add the below dependencies in entry/build.gradle to generate hap/expandablerecyclerview.har.
dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar', '*.har'])
    testImplementation 'junit:junit:4.13'
    implementation project(':expandablerecyclerview')
}
  1. For using ExpandableRecyclerView in separate application using har file, add the har file in the entry/libs folder and add the dependencies in entry/build.gradle file.
dependencies {
	implementation fileTree(dir: 'libs', include: ['*.har'])
	testImplementation 'junit:junit:4.13'
}
  1. For using ExpandableRecyclerView from a remote repository in separate application, add the below dependencies in entry/build.gradle file.
dependencies {
	implementation 'dev.applibgroup:expandablerecyclerview:1.0.0'
	testCompile 'junit:junit:4.13'
}

Usage

Let's say you are a rock star ๐ŸŽธ and you want to build an app to show a list of your favorite Genres with a list of their top Artists.

First create the Ability Slice and call the respective controller class by passing view and context as arguments and then call the initViews method of that class.

public class ExpandAbilitySlice extends AbilitySlice {
    @Override
    public void onStart(Intent intent) {
        super.onStart(intent);
        ComponentContainer rootView = (ComponentContainer) LayoutScatter.getInstance(this)
                .parse(ResourceTable.Layout_ability_expandlist, null, false);
        super.setUIContent(rootView);
        ExpandAbilityController controller = new ExpandAbilityController(this, rootView);
        controller.initViews();
    }
}

Each controller class extends the common helper class. Inside the controller class, initViews method will call initializeViews method of the parent class to initialize the view components.

public void initViews() {
        super.initializeViews(ResourceTable.Id_root_expand, ResourceTable.Id_lcGroupItems_expand);
        toggleBtn = (Button) rootView.findComponentById(ResourceTable.Id_toogle);
        this.prepareExpandableListAdapter();
}
protected void initializeViews(int rootViewId, int containerId) {
	ScrollView parentLayout = (ScrollView) rootView.findComponentById(rootViewId);
	parentLayout.setBackground(getShapeElement(ResUtil.getColor(context, ResourceTable.Color_white)));
	mGroupContainer = (ExpandableListContainer) rootView.findComponentById(containerId);
	getGroupItems();
        getGroupIcons();
}

Then, Add all the Genre items in mGroupNameItem and their corresponding image in mGroupImageItem . We are maintaining a copy of the group items for finding the child items of each groups from a Hashmap by using their index value in mFinalGroupNameItem list.

private void getGroupItems() {
        mGroupNameItem.add(new ParentChild(null, ResUtil.getString(context, ResourceTable.String_item_Rock)));
        mGroupNameItem.add(new ParentChild(null, ResUtil.getString(context, ResourceTable.String_item_Jazz)));
        mGroupNameItem.add(new ParentChild(null, ResUtil.getString(context, ResourceTable.String_item_Classic)));
        mGroupNameItem.add(new ParentChild(null, ResUtil.getString(context, ResourceTable.String_item_Salsa)));
        mGroupNameItem.add(new ParentChild(null, ResUtil.getString(context, ResourceTable.String_item_Bluegrass)));
        mFinalGroupNameItem.add(ResUtil.getString(context, ResourceTable.String_item_Rock));
        mFinalGroupNameItem.add(ResUtil.getString(context, ResourceTable.String_item_Jazz));
        mFinalGroupNameItem.add(ResUtil.getString(context, ResourceTable.String_item_Classic));
        mFinalGroupNameItem.add(ResUtil.getString(context, ResourceTable.String_item_Salsa));
        mFinalGroupNameItem.add(ResUtil.getString(context, ResourceTable.String_item_Bluegrass));
}
private void getGroupIcons() {
	mGroupImageItem.add(ResourceTable.Media_rock);
	mGroupImageItem.add(ResourceTable.Media_jazz);
	mGroupImageItem.add(ResourceTable.Media_classic);
	mGroupImageItem.add(ResourceTable.Media_salsa);
	mGroupImageItem.add(ResourceTable.Media_bluegrass);
}

Now the binding of the data to the view is done inside the prepareExpandableListAdapter method. Here, we will decide whether the item is a child or a group item by calling handleListItem and checking whether it is present in mTempChildNameItem or not. After this, we will set the adapter and OnItemClicklistener.

protected void prepareExpandableListAdapter() {
	expandableListAdapter = new ExpandableListAdapter<ParentChild>(context,
		mGroupNameItem, mGroupImageItem, ResourceTable.Layout_ability_listview_item) {
	    @Override
	    protected void bind(ViewHolder holder, ParentChild text, Integer image, int position) {
		handleListItem(holder, text, image);
	    }
	};
	//setting the adapter
	setAdapter(expandableListAdapter);
	//setting the OnItemClickListener
	setListener(expandableListAdapter);
}
private void setAdapter(ExpandableListAdapter<ParentChild> expandableListAdapter) {
        mGroupContainer.setItemProvider(expandableListAdapter);
    }

If the item is not present in mTempChildNameItem, then it is a parent/group item, otherwise it is a child item.

private void handleListItem(ExpandableListAdapter.ViewHolder holder, ParentChild text, Integer image) {
	if (!mTempChildNameItem.contains(text.getChildItem())) {
	    handleParentItem(holder, text, image);
	} else {
	    handleChildItem(holder, text);
	}
}
private void handleParentItem(ExpandableListAdapter.ViewHolder holder, ParentChild text, Integer image) {
	// Set background for parent/Group
	holder.makeInvisibleButton(ResourceTable.Id_checkbtn);
	holder.makeInvisibleImage(ResourceTable.Id_childstar);
	holder.setGroupItemBackground(ResourceTable.Id_groupContainer, ResourceTable.Color_white);
	holder.setText(ResourceTable.Id_tvGroupTitle, text.getChildItem(), Color.GRAY,
		ResUtil.getIntDimen(context, ResourceTable.Float_group_text_size));
	holder.setGroupImage(ResourceTable.Id_ivGroupIcon, image,
		ShapeElement.RECTANGLE, Image.ScaleMode.STRETCH, ResourceTable.Color_white);
	if (!mTempGroupNameItem.contains(text.getChildItem())) {
	    // Set arrow down icon
	    holder.setGroupImage(ResourceTable.Id_ArrowIcon, ResourceTable.Media_arrow_Down,
		    ShapeElement.OVAL, Image.ScaleMode.CENTER, ResourceTable.Color_white);
	} else {
	    // Set arrow up icon
	    holder.setGroupImage(ResourceTable.Id_ArrowIcon, ResourceTable.Media_arrow_Up,
		    ShapeElement.OVAL, Image.ScaleMode.CENTER, ResourceTable.Color_white);
	}
}

handleChildItem method will be different for different ability. For expand ability-

protected void handleChildItem(ExpandableListAdapter.ViewHolder holder, ParentChild text) {
        // Add child items to list
        holder.makeInvisibleButton(ResourceTable.Id_checkbtn);
        holder.makeInvisibleImage(ResourceTable.Id_ArrowIcon);
        holder.setText(ResourceTable.Id_tvGroupTitle, text.getChildItem(), Color.GRAY,
                ResUtil.getIntDimen(context, ResourceTable.Float_child_text_size));
}

Inside the setOnItemClickListener, we call the handleClickedItem method to check if the clickedItem is a Group item (i.e. Genre) or the Child item (i.e. Artist).

private void setListener(ExpandableListAdapter<ParentChild> expandableListAdapter) {
        expandableListAdapter.setOnItemClickListener((component, position) ->
                handleClickedItem(expandableListAdapter, position));
}
protected void handleClickedItem(ExpandableListAdapter<ParentChild> expandableListAdapter, int position) {
        ParentChild value = mGroupNameItem.get(position);
        String clickedItem = value.getChildItem();
        if (!mTempChildNameItem.contains(clickedItem)) {
            handleClickedParentItem(expandableListAdapter, position);
        } else {
            handleClickedChildItem(expandableListAdapter, value);
        }
}
private void handleClickedParentItem(ExpandableListAdapter<ParentChild> expandableListAdapter, int position) {
        checkChild(position);
        expandableListAdapter.setData(mGroupNameItem);
}

handleClickedChildItem method will be different for different ability slice. For expand ability-

@Override
protected void handleClickedChildItem(ExpandableListAdapter<ParentChild> expandableListAdapter, ParentChild value) {
        super.showToast();
}

mTempGroupNameItem contains all the Group item that are in expand state and mTempChildNameItem will contains child of such GroupItems. While collapsing the group, we will remove the group items from mTempGroupNameItem and their child items from mTempChildNameItem.

private void checkChild(int position) {
        ParentChild value = mGroupNameItem.get(position);
        String clickedItem = value.getChildItem();
        if (mTempGroupNameItem.contains(clickedItem)) {
            int actualItemPosition = mFinalGroupNameItem.indexOf(clickedItem);
            removeChildItems(actualItemPosition, position);
            mTempGroupNameItem.remove(clickedItem);
        } else {
            int actualItemPosition = mFinalGroupNameItem.indexOf(clickedItem);
            addChildItems(actualItemPosition, clickedItem, position);
            mTempGroupNameItem.add(clickedItem);
        }
}

If it is the Group item and it is not there in mTempGroupNameItem, that means we have to expand it and then add it to mTempGroupNameItem and their corresponding child items to mTempChildNameItem.

private void addChildItems(int index, String clickedItem, int itemPositionFromGroup) {
        String[] childItems = childItems().get(index);
        for (String item : childItems) {
            itemPositionFromGroup = itemPositionFromGroup + 1;
            mGroupNameItem.add(itemPositionFromGroup, new ParentChild(clickedItem, item));
            mTempChildNameItem.add(item);
            mGroupImageItem.add(itemPositionFromGroup, ResourceTable.Media_star);
        }
}

If the clickedItem is already there in mTempGroupNameItem, then we have to collapse it and then remove it from mTempGroupNameItem and the corresponding child items from mTempChildNameItem.

private void removeChildItems(int position, int itemPositionFromGroup) {
        String[] items = childItems().get(position);
        for (String name : items) {
            mGroupNameItem.remove(itemPositionFromGroup + 1);
            mGroupImageItem.remove(itemPositionFromGroup + 1);
            mTempChildNameItem.remove(name);
        }
}

FavouriteItems

We can maintain our favorite child items, i.e. favourite Artists, by adding them to an ArrayList mFavouriteItem.

protected void getFavouriteItems() {
        mFavouriteItem.add(ResUtil.getString(context, ResourceTable.String_item_child_Rock1));
        mFavouriteItem.add(ResUtil.getString(context, ResourceTable.String_item_child_Rock4));
        mFavouriteItem.add(ResUtil.getString(context, ResourceTable.String_item_child_Jazz1));
        mFavouriteItem.add(ResUtil.getString(context, ResourceTable.String_item_child_Jazz2));
        mFavouriteItem.add(ResUtil.getString(context, ResourceTable.String_item_child_Classic2));
        mFavouriteItem.add(ResUtil.getString(context, ResourceTable.String_item_child_Salsa1));
        mFavouriteItem.add(ResUtil.getString(context, ResourceTable.String_item_child_Bluegrass1));
}

CheckableChildItems

We are maintaining mSelectedChild, which is a HashMap, and will map the checked item to its parent item. We are updating this HashMap whenever any list item is getting clicked.

@Override
protected void handleClickedChildItem(ExpandableListAdapter<ParentChild> expandableListAdapter, ParentChild value) {
        String parentGroup = value.getParentItem();
        String clickedItem = value.getChildItem();
        if (mSelectedChild.containsKey(parentGroup)) {
            mSelectedChild.remove(parentGroup);
        }
        mSelectedChild.put(parentGroup, clickedItem);
        expandableListAdapter.setData(mGroupNameItem);
}

To handle the case where a child item can be under multiple groups, we have created a class ParentChild that will store both the parent and the child name for each of the list items. While rendering the view, we will check only those items whose parent child pair is present in mSelectedChild HashMap.

if (mSelectedChild.containsKey(text.getParentItem()) && mSelectedChild.get(text.getParentItem())
	    .equals(text.getChildItem())) {
	holder.setChecked(ResourceTable.Id_checkbtn);
} else {
	holder.setUnChecked(ResourceTable.Id_checkbtn);
}

Expand List

This will have a list of Group Items (i.e. Genre) and on clicking on it, the group will expand and their corresponding child items (i.e Artist) will be visible. we also have TOGGLE CLASSIC GROUP button that can automatically expand and collapse the Classic group item.

expand

MultiType List

In this, we will have our favourite child items. While rendering the view we can add the star image in front of each of those items that are present in mFavouriteItem.

multitype

SingleCheck List

This will allow the us to select one Artist among the other artists of a particulat Genre. It also provides us with the CLEAR SELECTIONS button that will clear all our previous choices.

singleCheck

MultiCheck List

This will allow the us to select multiple Artist of a particulat Genre. It also provides us with the PROGRAMMATICALLY CHECK BOSTON button that will automatically check the boston Artist of Rock Genre.

multicheck

License

ExpandableRecyclerView is Copyright (c) 2016 thoughtbot, inc. It is free software, and may be redistributed under the terms specified in the LICENSE file.

expandable-recycler-view's People

Contributors

anshulgoel72 avatar applibengineering avatar samuel-abhishek avatar

Stargazers

 avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.