Sunday, September 4, 2016

Android Compound View Tutorial: Building a Comment Box

Custom controls are great, but sometimes you just need to reuse groupings of views. For these scenarios, the Android framework supports compound views. Like a typical custom view, compound views are allow for functionality that is not possible with just the framework's standalone views. Instead of subclassing a view, these inherit from View Groups allowing you to reuse combinations of controls, while keeping any UI logic in its own subclass.

When completing the Popular Movies app, I needed a reusable way to display trailer thumbnails and reviews, but didn't need to develop a custom view. In both cases, a compound view was suitable. Using what I learned from making the view for movie reviews, I'll explain how to make a simple compound view for another common scenario: an expandable view that displays comments.

A sample project with the completed comment view will be linked at the end of the article. For now, let's take a look at each of the components.

We start by creating a new layout file. For a comment box, there are 2 components we need: a single-line text view to display the username and a multi-line text view to display the text. Because some comments are long, we're only going to show a preview at first. The view will contain an expand button to view the entire comment.

The layout is straightforward: just a vertical linear layout with three views.

Since the point of a compound view is to make it reusable in code, we also need to create an accompanying Java class. We'll call this CommentView and make it a subclass of LinearLayout. We then add the three default initializers. In all my projects, I use ButterKnife to avoid excessive findViewById() calls, but if you're doing it without, you can assign the views in the setupViews() method instead.

At this point, the view should be working and we can populate it's data through code. However, there's no way to restrict the number of lines, and the "show more" button doesn't do anything. There's more work to be done.

The expand button's visibility is dependent on the amount of text in the comment view. Consequently, we need to create a separate setter to monitor when text changes. There's an issue with TextView's getLineCount() where it only works properly once the view has been rendered. To work around this, we post a new Runnable to check the line count each time the text is changed, as suggested in this StackOverflow answer. This occurs each time the text is assigned, making it necessary to define a custom setter.

Now that we know when the "expand" button should be displayed, it's time to actually implement it. To do this we add a new property to the class to hold the value of "expanded". This value is used to either expand or collapse the view in the OnClick handler.

The view should now be working. In the sample project, I've added it to the screen in onCreate(). We're almost done, but we still need to handle state restoration when the device is rotated. In order for onSaveInstanceState() and onRestoreInstanceState() to be called, each instance of CommentView must have a unique ID. Because they'll be dynamically added, I called setId() after initialization, with a custom int value. Saving and restoring the instance is a little tricky, but I got it to work using the approach in this discussion. The following is added as an inner class of CompoundView.

Finally, we just implement the methods for saving and restoring the instance state, making sure to pass the value of "expanded" into the Parcel.

And we're done. The compound view can now be used in our code and it properly restores its instance state when the screen orientation changes. The sample project shows it in action and can be downloaded here.

Our comment view in action.
To make this more like the view I used in Popular Movies to display reviews, the expand button can be changed to an ImageButton with its background property set to @null. I got the chevron icons from Icons8.

That's it for setting up the comment box. If you enjoyed this post or want similar tutorials in the future, feel free to leave a comment (pun intended) below. Until next time, happy coding!

No comments:

Post a Comment