Creating a comment section for your Android App

In this article we will be implementing a comment section for an android application using the following technologies:

This article is a part of a series on how to make a reddit app, which specifically focuses on how to show the comments of a post in an application. All the source code are available on this Github repo.

Final product:

final product of this article

We know that reddit comments are enormous and have a lot of parent-child dependencies. I wanted to find a way to group them without high complexity in the adapter or in my view. This could be very confusing as each comment can have 20 children, and each of them could have another 20 children.

That’s how I found groupie:

https://github.com/lisawray/groupie

“It makes it easy to handle asynchronous content updates and insertions and user-driven content changes. At the item level, it abstracts the boilerplate of item view types, item layouts, viewholders, and span sizes.”

Just by running the example app by the author I was very impressed by the variety of implementations. It also supports data binding and kotlin, which I was eager to try.

Working on a prototype

For this prototype, I decided to follow Chiu-Ki Chan’s advice where she recommends to work with a library in a prototype before integrating with the entire project. So this is what I decided to go for. I started building the comment section within the library’s example.

Comment prototype using groupie library

Groupie is really helpful for very complex RecyclerView layouts. The example app itself shows a very complex UI with a lot of different Groups in a single GroupAdapter. A Group replaces the ViewHolder(You can still use yours, if you already have some working) in an adapter, which later on you add it to a GroupAdapter that replacing the Adapter inRecyclerView.

Among all the examples in Groupie, Expanding Group showcases how to have a group with children and expanding them on a click, which particularly fits in the needs for a comment section.

The example uses an ExpandableGroup and adds all children using the add method. Also note that we need a header item for the constructor of an Expandable Group, which implements the interface ExpandableItem.

Expanding group view

So we have two layouts now, one for the header and the other for the children.

In our case we need the child to be able to have children recursively. In other words, the child needs to be a group itself that can have more groups as children.

In order to do so, we need a class that extends ExpandableGroup. Meanwhile, this class requires an ExpandableItem in the constructor.

With kotlin we can call the super constructor when extending superclass so we just instantiate our item ExpandableCommentItem

class ExpandableCommentGroup 
constructor(comment : Comment) : ExpandableGroup(ExpandableCommentItem(comment))

This group will be in charge of adding the children comments of a comment to itself.

Now let’s add an ExpandableCommentGroup to each child of the current ExpandableCommentGroup to achieve recursive parent-child views. We can do this in the init function of the class:

init {
   for (comm in comment.childs) {
add(ExpandableCommentGroup(comm)
}
}

We will also need a way to know the current depth of the comments, so we can show the separator in the views according to the depth. This is why we add a depth parameter to the constructor with a default value of 0. We increment this parameter by 1 each time a child group is created from a group.

Now we completed the expandable group that holds the comments, we will we need to have the item that will be displaying our comments. We will start by extending the Item class. We will be using the ViewHolder implementation that the library have so we will extend as follows.

open class ExpandableCommentItem constructor(
    private val comment : Comment,
    private val depth : Int) : Item<ViewHolder>(), ExpandableItem

Don’t forget that for this item to work with the ExpandableGroup we need to implement the ExpandableItem interface. When we use this interface we need to override the function setExpandableGroup() :

void setExpandableGroup(@NonNull ExpandableGroup onToggleListener);

We will need a lateinit variable to store the ExpandableGroup when it’s assigned through this method. This method will give us the current ExpandableGroup which will allow us to toggle the group.

override fun setExpandableGroup(onToggleListener: ExpandableGroup) {
   this.expandableGroup = onToggleListener
}

Our new variable expandableGroup will allow the library to update the adapter with the new items inserted or removed. We just simply need to call expandableGroup.onToggleExpanded() and the items will be added/removed accordingly.

We will also need to override the getLayout() method to return the int resource of the layout that our item will be displayed

override fun getLayout(): Int {
    return R.layout.item_expandable_comment
}

You can find a version of this xml view for this item here

To wrap up, we need to override the bind method to display our data, as a regular ViewHolder, and add the click listener to toggle the group.

I also created a function that will create separator views to show the current depth of the comment.

So far we have worked on an adapter for a RecyclerView that is capable of showing nested comments. The next part of the article will talk about how to add this new items to our reddit application.

Reddit API

Let’s talk a little about our data models from the Reddit API. I won’t explain deeply but I will talk the most important points about this.

Our app is making a GET request to https://www.reddit.com/r/NintendoSwitch/comments/6tm3yi/a_demo_of_rayman_legends_definitive_edition_is/.json

The types of content we are interested in this response have the type t1. The complex thing here is that the API have different data structure for it’s different kind of objects. So if an object have a JSON structure for a post, it’s different for a comment. The data structure wasn’t always the same, so If a JsonObject was returned in one node, a children node would return an empty string.

In order to get over this, I created a RedditContentDataParser that could be registered to the GsonBuilder as a Type Adapter. It also had to recursively use the same TypeAdapter for the children, so I registered the adapter to the gson instance used in the parser.

My application receives the data as a RedditContentData class. I needed to convert it to a Comment POJO that can be used in my views.

That’s the reason why I created a couple of Functions to use in RxJava. RedditDataParser that will take the reddit response and return the necessary data and a CommentParser that will convert the data and it’s children into a Comment POJO.

Adding our prototype in our application

Now that we have our Comment objects, we need to add them to our recycler view.

We add the GroupAdapter to our recycler view

rv_comments.apply {
    layoutManager = groupLayoutManager
    adapter = groupAdapter
}

We init the call to our API calling presenter.loadInfo(permalink) and then the view will receive each comment in the method showComment

override fun showComment(comment: Comment) {
    groupAdapter.add(ExpandableCommentGroup(comment))
}

Our presenter calls the reddit API and also uses the mentioned parsers. Once we have all ready we call view.showComment(comment) in our subscription

override fun loadInfo(permalink: String) {
    compositeSubscription.add(
        postStore.getPost(permalink)
            .subscribeOn(Schedulers.io())
            .flatMapIterable({ redditResponses -> redditResponses })
            .flatMap(RedditDataParser())
            .filter({ redditContent -> redditContent.kind == “t1” &&    redditContent.redditContentData != null})
            .map(CommentParser())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(object : Observer<Comment> {
                override fun onCompleted() {
                     Log.e(“tag”, “completed”)
                }
                override fun onError(e: Throwable) {
                     Log.e(“tag”, “e “ + e.printStackTrace())
                }
                override fun onNext(comment: Comment) {
                     view.showComment(comment)
                }
    }))
}

With this step, we have finished implementing the comment section in our reddit app. If you would like to see the branch related to this article you can find it here:

https://github.com/PedroCarrillo/RedditApp/tree/feature/comments

Don’t forget to leave any questions or suggestions you may have.

Thanks to Xinyu Jin for proofreading this article.