Migration to compose

hongbeom
ProAndroidDev
Published in
5 min readJun 13, 2021

--

Let’s creating an app using compose instead of xml

Compose is part of the Jetpack Library released by Android last spring. Create Android UI faster and more powerfully and by declaring composable functions. I was in a situation where I had to start reorganizing the Android app structure that I was servicing, and I applied it to the Toy project first to think about introducing Compose. So I applied it to the Harry Potter app that I made.

Write UI ✈️

Let’s get a look at how looking at the example of changed. This is the xml that made up the MainActivity.

LottiAnimationView was used for background animation and had logo images and horizontal scroll views(using RecyclerView adapter). Let’s look at how it changed in Compose.

Box work as a FrameLayout, stacking them up in the order they are written. So I wrote a Lotti for compose(composable function wrapped to infinitely animate) and used a Column. A Column is a widget that has a vertical structure of an existing LinearLayout. It replaced the xml margin written on top by a Spacer margin in the Column.

It’s like a vertical LinearLayout, horizontal LinearLayout, FrameLayout.

Now let’s look at how you replaced the existing horizontal scroll view. I replaced the existing horizontal scroll view with the Appcompanist-Pager without using the LazyColumn because it contained a snap function.

⚠️ Accompanist Pager is experimental. The API may be changed in the future.

As you can see from the name, Pager are more like traditional ViewPager. By calculating the offset of the current page in the graphics Layer, the existing scale animation effect was reproduced. graphicsLayer can be used to apply effects to content, such as scaling, rotation, opacity, shadow, and clipping.

In addition, if you read the state inside the block with animation value, no recomposition and relayout are executed, only the properties of the layer are updated.

When showing a compose in the activity, simply declare it within the setContent block 😮

Manage State 👀

Now let’s take a look at the DetailScreen. The DetailScreen contains a list of HarryPotter characters, and a dialog appears when you click the item. and this screen It contain a little animation.

On the 6 line, the viewModel is injected through the Hilt, and on the 8 to 15 lines, the State is passed to the variable.

getLifecycleAwareState Block is extension method that initiates consumption and production of flows when the current lifecycle state of the compose is Lifecycle.State.STARTED and stops when it is in the background state. also remember composable can be used inside the composable function to store objects in memory. The original look is as follows.

The selectedCharacter of the DetailViewModel is SharedFlow, which triggers the received value when an event occurs. In addition, the isLoadingFlow state of the DetailViewModel is StateFlow, which receives the value and changes it to State. See this post for information on how to safely collect Flow in the lifecycle.

RecyclerView ♺

Let’s talk about it again DetailScreen. I had to use GridLayoutManager in traditional RecyclerView because I used grid-formatted lists . So it replaced it with LazyVerticalGrid compose.

⚠️ This foundation API is experimental and is likely to change or be removed in the future.

Lazy composables makes it easier to create a list that behaves the same as a RecyclerView without an adapter!😮

ConstraintLayout ⛓

How did the ConstraintLayout, which was previously useful? It’s easier to use than you think.✨ I used the ConstraintLayout on the list item.

After declaring a ConstraintLayout, create a reference variable through a createRefs() inside the block. And pass it over to the parameter of Modifier.constrainAs() and create a constraint through linkTo method.

You can also dynamically specify the height and width by using Dimension(preferredWrapContent, wrapContent, fillToConstraints). Each Dimension is described as follows.

- preferredWrapContent : A Dimension with suggested wrap content behavior. The wrap content size will be respected unless the constraints in the ConstraintSet do not allow it. To make the value fixed (respected regardless the ConstraintSet), wrapContent should be used instead.

- wrapContent : A Dimension with fixed wrap content behavior. The size will not change according to the constraints in the ConstraintSet.

- fillToConstraints : A Dimension that spreads to match constraints. Links should be specified from both sides corresponding to this dimension, in order for this to work.

Dialog ☎️

Dialog is show and dismiss through Effect. If you look at the internal code first, it looks like this. Among the parameters, callbacks of dismiss events are available through onDismissRequest, and use DialogProperties to can control the dismissOnBackPress, dismissOnClickOutside.

I put the event of the DetailViewModel in onDismissRequest so that the dialog can be dismiss normally.

It works the same as before except for a few animations🤔

Conclusion

Although there are features and bugs that have not been used all the functions in the Compose and are not supported because they are still in beta, this nevertheless seems quite good. It felt that xml was not needed and very easy to reuse and maintain. The official version, which will be released in July, is expected to be more stable and convenient.

Thank you for reading my post and feedback is always welcome 🙌 All source code can be found at the link below!👇

Reference

--

--