Published March 27, 2020
In my previous post, the Container
components are removed since they will be deprecated soon. The vector assets are also drawn using the Icon
and IconButton
components. In this post, some of the action buttons are set up to modify the state when clicked.
The comment button will increment the comment count since a user can leave multiple comments on a single Tweet. The like and retweet buttons will act as a toggle. These also need a selected state so users know if they have liked or retweeted that Tweet.
A few things are needed to handle a click on the comment action item. The click event lambda needs to handle updating the comment count, and the Tweet
class needs to be updated so our composable functions can react to changes to its properties.
Modifying the click lambda is first. Incrementing the comment count requires moving the declaration of the lambda to a higher-order composable function. It is currently initialized in the ActionRow
function, but there is no reference to the Tweet
class there. Passing the tweet down to the ActionRow
would pollute it with unnecessary information. Instead, the event listener will be lifted up to the TweetView
function.
First, the ActionRow
is updated to accept an event listener for a click on the comment view. This listener is passed to the Comment
function.
Next, the TweetView
function implements the event listener and passes it to the ActionRow
function.
With the listener hooked up, the next step is to modify the Tweet
class. The commentCount
property needs to be changed from val
to var
so the listener can write to it.
Then the value can be changed in the TweetView
.
Running the app now surfaces a problem. Clicking on comment does not change the value shown on the screen. Adding a log statement in the listener and running the app again verifies that it is reacting to the click event, but it is not changing the UI.
The reason this happens is there is nothing telling the TweetView
to redraw itself when the contents of the Tweet
changes. Compose provides an annotation for this functionality. Adding the @Model annotation to the Tweet
class tells the TweetView
to listen for changes to the tweet properties. If one of the properties changes, then the TweetView
will redraw itself to display the new data.
The class comment on the annotation provides details on exactly what this annotation does.
* [Model] can be applied to a class which represents your application's data model, and will cause
* instances of the class to become observable, such that a read of a property of an instance of
* this class during the invocation of a composable function will cause that component to be
* "subscribed" to mutations of that instance. Composable functions which directly or indirectly
* read properties of the model class, the composables will be recomposed whenever any properties
* of the the model are written to.
Since TweetView
reads the commentCount
property from the Tweet
, it implicitly subscribes to changes to the value. When the commentClick
listener modifies that value, the TweetView
is redrawn to the screen.
With that, the comment functionality is finished. Next, the like and retweet actions are setup.
The like and retweet actions are slightly different than comment. Instead of incrementing the count for each click, they need to toggle on and off since a user can only like or retweet once. While this could be implemented using the Clickable
function, there is a better way.
The dev05
release of Compose introduced the Toggleable
function which is exactly what the like and retweet actions need. This function accepts a value
parameter indicating the current state of the toggle. It also accepts an onValueChange
listener that receives the new value as a boolean flag when clicked. Like
is updated first.
The Clickable
function is changed to Toggleable
. This function requires a value parameter and a value change listener. A boolean flag, liked
, is passed to the Like
function which is forwarded to Toggleable
. The onClick
listener parameter is updated to onLikeChanged
which requires a Boolean
property for the lambda. This listener is passed as the onValueChange
property to Toggleable
.
Similar changes are made to Retweet
.
In fact, these are the exact same changes made to Like
. Since this is the final form of these functions, this duplication can be extracted to a new composable function.
Like
and Retweet
are simplified by calling through to this new function.
With the low-level functions finished, the ActionRow
function needs updates to pass the new parameters. The new toggle listeners will be passed from TweetView
just like the commentClick
function so ActionRow
can continue to have know knowledge of the Tweet
class itself.
The TweetView
now needs to provide all these parameters to the ActionRow
. There is one thing to fix first. The Tweet
class needs properties for the liked and retweeted state. These are added as var
properties so they can change. The retweetCount
and likeCount
properties are also changed to var
s so they can be updated as well.
The onCreate()
in MainActivity
and the preview function need to add these properties to the Tweet
so the project can build.
Now the TweetView
can provide the necessary data to the ActionRow
.
The toggle functions set the current state on the tweet, then modify the count based on which way the toggle goes. If the user likes the tweet the count is incremented, and if they unlike it then it is decremented.
To wrap up, a little color added to the toggles helps the users figure out their current state at a glance. The light gray color is used for the off state but a different color would be nice for the on state.
To implement this, a selected color is added as a property of the ToggleImage
function. This color is then set as the tintColor
fo the DrawVector
function as well as the color
parameter of the TextStyle
for the count text.
The color green is used for Retweet
and red is used for Like
.
With this in place, the app now styles the like and retweet actions based on their state.
In this post the action row is setup to modify the state of the view based on click events. The comment count increments each time it is clicked while the like and retweet counts toggle back and forth.
In the next post the state management will be further improved by making the Tweet
class read only. Instead of having var
properties, everything will be declared as a val
to have an effectively immutable data class. This will provide some data protection because the individual model objects cannot be updated.
Thanks for reading and stay tuned for more!
Photo by Hal Gatewood on Unsplash