This is the third post in series of exploring Litho. In the first post - Android GIF search engine with Litho, we explored some key aspects of Litho - LayoutSpec, MountSpec, Use Glide to load images, Update RecyclerView data, etc. In the second post - Managing State in Litho, we dabbled around with state in Litho components. If you’re not familiar with Litho, I would suggest you to go through the previous posts.
In this brief post, we will see how we can easily navigate using Litho. In our main component - HomeComponent, we display a grid of GIFs. Now, we will create another component - FullScreenComponent to display the GIF in full screen mode. For navigation, we’ll use Litho components instead of different activities or fragments.
As you would have seen the first post, we create a view -
LithoView and set it as the content of our MainActivity. LithoView is a special
ViewGroup tailored to handle
ComponentTree. We can easily switch components of this view and we are going to use this property for navigation. When the user clicks on a GIF, we will create a new FullScreenComponent and set it in our root (LithoView) and when the user pressed back, we will catch it in
onBackPressed and update root with the HomeComponent.
We would like to display the GIF in its proper aspect ratio and make it acquire as much screen space as required. We would also have a like button to show whether the user has liked the GIF or not. We’ll also introduce a state so that user can like/dislike the GIF from FullScreenComponent also.
The component needs to show GIF so we will reuse
GifImageView component with a bit of modification in LayoutSpec. We also need to update
GifItem model to include width and height of the image in order to properly measure its layout.
code - GifItem
We now have small and normal image. We will show small image in the recyclerview and original image in FullScreenComponent. Also, since we have square grids in recyclerview, for layout measurement we will use aspect ratio as 1 but for FullScreenComponent we will use aspect ratio as width/height.
code - GifImageViewSpec
We have updated GifImageView component in order to use in FullScreenComponent as well as GifItemView component. We have introduced an optinal
@Prop boolean isFullScreen. Since it’s optional we do not need to provide it in GifItemView. We can just pass
.isFullScreen(true) in FullScreenComponent. Based on this prop, it decides the aspect ratio for layout measurement and whether to load small image or original image.
code - FullScreenComponentSpec
Creating layouts with Yoga is a bit difficult as you can’t see the changes until you run the app. I would suggest you to play around with it.
ClickEvent is same as what we did in the previous post.
To handle click event and navigation, we will need to update
GifItemView component and
MainActivity. We need to update the callback in order for MainActivity to know that user has clicked on the GIF. We also need to update MainActivity to include FullScreenComponent and add navigation logic as well as backstack logic.
code - GifItemViewSpec
We created another event handler
@OnEvent(ClickEvent.class) onViewClicked to handle the events when the user clicks on the GIF. To listen to the clicks we added
.clickHandler(GifItemView.onViewClicked(context)) to the Column. When the user clicks on the GIF, callback will be received by MainActivity and we need to create FullScreenComponent and navigate to it.
code - MainActivity
LithoView.setComponent() methods updates the component in the view and creates a new component tree. This is similar to FragmentTransactions to replace one fragment with another but much simpler. When the user presses back, we check if
isFullScreen true or not. If it’s true, we set HomeComponent to the root and change
isFullScreen to false.
If you noticed, we are setting
.key(gif.getId()) to FullScreenComponent. This is very important. Litho differentiates within same types of components based on this key. If we do not provide different keys for different GIF items, it will reuse the same component and
@OnCreateInitialState is never invoked and the user will see the GIF is liked even though the user did not like the GIF but opened a GIF (the first GIF that was opened in the session) which they had liked.
Now, if you run the app and click on this funny batman GIF, you’ll find this!
And if you press back, you’re back on the main screen which shows a grid of GIFs.
This looks great but if you open a GIF and change
isLiked state and come back to the recyclerview, you’ll notice that
isLiked state for that GIF is not updated. We need to make sure that when we come back the state of GifItemView component is also correctly updated. We will cover this in upcoming posts.
You can find current code here - LithoGifDemo - v4-navigation
You can find the latest code (keeps updating) here - LithoGifDemo
In the next post, we will explore
Events with Litho
P.S. Litho docs are very short, informative but vague and you tend to miss some things. I have read the docs a lot of times and every time I read a particular section again, I learn some new things.
- Android GIF search engine with Litho
- Managing State in Litho
- Navigation with Litho
- Events with Litho
- Synchronizing state between different components
Using Events in Litho
Use events to send component updates in Litho instead of callbacks