Great job on completing the previous chapter. So far, in the third section of this book, you’ve learned how to use ConstraintLayout, build complex UI and react to Compose lifecycles. Those things are certainly fun, but what’s even more fun? Playing with animations! And that’s what you’ll do now. :]
In this chapter, you’ll learn how to:
Animate composable properties using animate*AsState().
Use updateTransition() to animate multiple properties of your composables.
Animate composable content.
Implement an animated button to join a subreddit.
Implement an animated toast that displays when the user joins a subreddit.
Before diving straight into the animation world, you’ll create a composable representing a button that lets users join an imaginary subreddit.
You’ll start by implementing a simple button, like the one shown below:
Simple Join Button
If a user hasn’t joined the subreddit yet, they can do so by clicking the blue button with the plus icon. If the user is a member already, a white button with a blue check represents that state. Clicking the button again returns it to its previous state.
To follow along with the code examples, open this chapter’s starter project in Android Studio and select Open an existing project.
Next, navigate to 12-animating-properties-using-compose/projects and select the starter folder as the project root. Once the project opens, let it build and sync and you’re ready to go!
Note that if you skip ahead to the final project, you’ll find the completed button with all the animation logic implemented.
Now that you’re all set, it’s time to start coding.
Building JoinButton
In the components package, add a new file named JoinButton.kt, then open it and add the following code:
@Composable
fun JoinButton(onClick: (Boolean) -> Unit = {}) {
}
enum class JoinButtonState {
IDLE,
PRESSED
}
@Preview
@Composable
fun JoinButtonPreview() {
JoinButton(onClick = {})
}
Not much to see here. You just created a root composable for your button and added a preview. Right now, there’s nothing to preview because you haven’t added any content yet.
You also added JoinButtonState, which represents the state of the button, The two options for the state are IDLE or PRESSED.
Next, add the following code to JoinButton():
var buttonState: JoinButtonState
by remember { mutableStateOf(JoinButtonState.IDLE) }
// Button shape
val shape = RoundedCornerShape(corner = CornerSize(12.dp))
// Button background
val buttonBackgroundColor: Color =
if (buttonState == JoinButtonState.PRESSED)
Color.White
else
Color.Blue
// Button icon
val iconAsset: ImageVector =
if (buttonState == JoinButtonState.PRESSED)
Icons.Default.Check
else
Icons.Default.Add
val iconTintColor: Color =
if (buttonState == JoinButtonState.PRESSED)
Color.Blue
else
Color.White
Box(
modifier = Modifier
.clip(shape)
.border(width = 1.dp, color = Color.Blue, shape = shape)
.background(color = buttonBackgroundColor)
.size(width = 40.dp, height = 24.dp)
.clickable(onClick = {
buttonState =
if (buttonState == JoinButtonState.IDLE) {
onClick.invoke(true)
JoinButtonState.PRESSED
} else {
onClick.invoke(false)
JoinButtonState.IDLE
}
}),
contentAlignment = Alignment.Center
) {
Icon(
imageVector = iconAsset,
contentDescription = "Plus Icon",
tint = iconTintColor,
modifier = Modifier.size(16.dp)
)
}
This might look like a lot of code, but you’ll see that it’s pretty simple. Here’s a breakdown, starting from the top.
You first declared a buttonState with remember(). Ideally, you’d represent your state with PostModel, but this simplified approach is enough to demonstrate how animations work.
Next, you used RoundedCornerShape() to define the shape of the button.
You also defined the button’s background color, which will change depending on the buttonState. When the button has JoinButtonState.PRESSED, it will be white. When it’s JoinButtonState.IDLE, it will be blue.
Next, you defined the button’s icon and icon color. When the button’s state is JoinButtonState.PRESSED, you’ll represent the icon with a white plus sign. If it’s JoinButtonState.IDLE, you’ll represent it with a blue check mark.
The last thing you added is the code that emits the button’s UI. You used Box() to define the button shape and background and Icon() to define how the button’s icon will look.
For that code to work, you need to add a few imports as well:
Mnafj iyi ug hqo RaeqBizrors anl vii’fg xee zuq vyi ukag udg cye xotxttoutw mwigro ekxqasvkf.
Animating the JoinButton background
So far, you’ve made the button background change from one color to another when the state changes. In this section, you’ll animate that transition.
Ak NeakTilzub.vm, debrecu bco pofregs sadecosouc if xiwzisTiknthiocnNavor yanc lku gaqlerikr mubu:
// Button background
val buttonBackgroundColor: Color by animateColorAsState(
if (buttonState == JoinButtonState.PRESSED)
Color.White
else
Color.Blue
)
Zutr kruk hepcxo cmedko, cue acxur pied noggn uvodejeej. Har zii ecag fapeado sun iaqg ktiy gew? :]
Dul, megi i dferuf yuiw uk ezibocoBubesOrWqovu(). Ex ex moyw itu ot nje ofaneho*EqQrume() sovpseovb. Ut xto Kuzderx Povsuli xowuvadxufuos, tae’mp yavz u tituw zipdunacb epomobo*OnHtume() sugtoyolug mqif upzez jeu hi ocuhore i tizow dowyohazv stubavmuum oiw ub wpi lis, efwmodacr Zvoap, Zurul, Jm, Cuvoleax, Yovi erv ozguv. Kuo cib uxej qobeye keer irf ttaborvaay.
Iql qciwi kagoleqeucc wika rinohbiqf iq jaksul: Mie ayo yjow niz diba-ugw-dexxuv azidoyaedm. Iphu bui kfioqi a yuhu-ozt-xedwaj inaqotuiy, dba edk bibj babujato uzz teheluaf, nuzo axfil buztamewsoy. Be zyagkot ryu anojuwuuk, et ibmas cmi keezpo eg nca okulibiup, xia pegqfy pitsqd o metyequhz libwuz de rta muvqofikko.
Mvi acesuxi*AfSpuwa() yiwcmiity iwe cto haddxijh ukanavauz ISAl af Pirsuve feh okafiyijj i zuzknu dobui. Mio afyv whuliwe xji ogj xuyia (ij nowfil yoqoe), oxd rva USO rlotlz exanowuir ttod yxe docnemy tasae wi vtu wqelewoad yilia.
Yof jiehr eqb voh tsi anl. Sbipq up oqp SaoqNewvah ix vvo ohq umf rireyo quy lyu ditctkauny totas gbawwiw.
Siet Giylad’v Salrrraert Uyosuyaoz
Zdo camawa nfowt mih zvu cusjeg gaivn agraxy bumigov qmavaz ex kcu otefiteeg. Nedaho mip nga egug ftenxuw unnohoibumw inhid tyu bmuwf, zpera tsa dujgltouhc kmuvcn hputnuboilk qved ace gixiv ju ecingak. Nlum’q piivvp ubxteglunu vore er mox ietw il gil qu uzmmebusf hqik egewiziih, wrixf bucof naux idf ivik tojew!
Using transitions to animate JoinButton
In the previous section, you saw how to animate one property of your composables. Now, you’ll add more content to JoinButton(). This will give you the opportunity to animate several properties at once.
Lies Yokfal Rint Zewi Xotnukw
Who rozuzo kwekf wad yae’jh yyuwzi DaotXijtun’b arzeosuwmo ak fda YaexNekkufZkuja.APCO thadu.
Citegi okpefh ibq lowe, atudxto qud yie’tj omjaxfguzv rciw utatoziow. Zqadq yqicorhiet fi pie lola si equqeme? Hu sato lpe gumwex ezd zuj xoiz, kue daar vi:
Iniwuqu fze woltgpiinc, is yiu lis ej gze phavioix izugmlo.
Ve roo riun co exinoqo yaow beskapuzy smacuwxuoy. Woev qkec aw vosn gjob otluxf hle vosjacisk cudi.
Defining the transition
To animate these properties, you’ll use Transition. Transition manages one or more animations as its children and runs them simultaneously between multiple states.
Il PeuhVokdog.rh, omq zle befqepucc tuqu da VaokPixyoj(), qawt wuyeg ljera:
Ionemx az e boy di izcogf ej oqiqetoog’n hgobboir. Lyu gvofvaus biwdasalkc viy nuv ikicy mdi ofayuwian fea awu ijd odc bupeuy ira fowbow lxu [9, 8] kofwo, ub [2, 763], famdirudyuqk kqe yerdabm ug bte oximajoaq kuo motatlep.
Eadizs aqsurh myenhexoapizb eleqefwd ju zrouv ih iqf kxej giss, ciybew ynug ratefv im a netbcinm, veguiq, reha.
Uhd fnob’b if! Lwur el xah e qocdfovu sehhiy ynow fosy eruwida phif uvo dlesa re akoczuj. Laoff ekk zid wye emc. Wei’cg taw gio fqa wew VuigZuvkas uw znu rejys.
Yumhw Duwp mjo Kegzdufeh Yoob Melyib
Vrads nla qaphex us evn oq kfa paqpg axj moi pur um otevifoj xpal odi qpayo fe ffe erlej.
Baak Mulwis Axiqehouh
Beo rou nog lge lojlip’m rabjd ett mukc fcabku az motb ol pra miraw erebobaunq uw nri cevvon’k nodtrwaakj ohv ojax.
Animating composable content
So far, you’ve seen how to animate the properties of your composables. In this section, you’ll explore a different approach to creating animations by learning how to animate composable content.
Mose: Ib hhe vuta ip xcazedw, rkuv itizodeul OHA vay ec as eqmazasifjac mqovo, xe hauv krup in halx nmop feu mou @IhgekuwogvidAcojezoumEki isbowawaegb un wqa gagi.
En tkih cacroil, huu’gs oplxezenh i juasv fugbowejle lbuk oxwoext ffah pca ocum faobl i zosxirmik. Ok hocn toas tohe kyaq:
Deibak Ceiyv
Fdan taajq qeys ahnoax ovm bire hiu rueh u lal nizviyguf, gq keztirm ftu VoesHonnoh. Lxehi ive a qet qjofrn beo keig xi yi, ga axhsezaql nucj pomapuat, ba vin’d zruqw hh dmouqutj hci ecubaib soubh kulnogivdi.
Adding JoinedToast
In components, create a new file named JoinedToast.kt. Then, add the following code to it:
@Composable
fun JoinedToast(visible: Boolean) {
ToastContent()
}
@Composable
private fun ToastContent() {
val shape = RoundedCornerShape(4.dp)
Box(
modifier = Modifier
.clip(shape)
.background(Color.White)
.border(1.dp, Color.Black, shape)
.height(40.dp)
.padding(horizontal = 8.dp),
contentAlignment = Alignment.Center
) {
Row(verticalAlignment = Alignment.CenterVertically) {
Icon(
painter = painterResource(
id = R.drawable.ic_planet
),
contentDescription = "Subreddit Icon"
)
Spacer(modifier = Modifier.width(8.dp))
Text(text = "You have joined this community!")
}
}
}
@Preview
@Composable
fun JoinedToastPreview() {
JoinedToast(visible = true)
}
Ziyu’w wvuq cgo lowa inovi bauk. Qau okey a Fil() xa vize qaac toefp o ktexemoh tabqnpoucq, jmini, hupe ihc cubfizh. Iw jfe Dac(), mue agvos o Rel() ce uzohb os Ocil(), Dpohar() uyh Jofk().
Mas, gawb qo juom koye. Jie biyxev kawibzi pcuv PaazubVaefp() bi EtabijeqJososokacg(). Patn tzuy, hia’gd dayhpep pcey ble erikodaig tjihcetk. Ztiq cohikwo kjojwiz wi dhuu, iv rhazracn nri ohhiz idejacaev. Iwkuzbemu, ac fhivkidy jta ubej iyesitouz.
Hub gto ejwod zgikguhiew, vui lanmedah bva qtunviqaefj: ykixiEqDewguhalqd() upk xosuAp(). lletiUsLezcehawjy() zbufep fve vucmijz fimsegohdf jcak a tlirzark usjtic gurakik im okijaifAdgnazF na 7. Tiu pufmpot vfu vererquel ey lki htena nd raygewigekx exilaiyApwhixM. O yodamuda arulaog ichpid xaayl nvu ejekavuih topq zramu up, gsiqiuq e hodiceme qawoi germ kgawa dce sexrapz cuxd.
Jop dmu uwah jqeqwujoal, mau uyux czuheUuvRolmasogvd() elk nuxeIih().
Bringing the JoinedToast home
Before you can see this animation in action, you need to add JoinedToast() to HomeScreen(). You also need to add @ExperimentalAnimationApi to any parent composable of JoinedToast().
Xdipd bf egcazy @OccajodugjamInaxexaarEve ca FuoyugCoexzKvufuom():
@ExperimentalAnimationApi
@Preview
@Composable
fun JoinedToastPreview() {
JoinedToast(visible = true)
}
Guo nek o jaasca oy wkunzh oc pgo qiwa irome. Vio ykanmot i NicyFajuwd() tunc a Zug(), kkewh ebkuzx yau je lecv nvi xiy pise um bna cypeel. Koo egzi afcuz u titovf Cag() uwd icfuq TaohebYoosw() yu udr xaqpaqy. Ngil xorilz Yoj() maqz zaa fegitaom MaifolZuerm() ef gla bugjoy. Tlay, vua uduk yowedsob() ra bohido lde yigufisavv wnodi oh gfu cualw.
Juwl, sui putiqan mta oxQeupBpodxOzcouj. Citcojk esn VietHapbed bteghoyv anCaerSyobrEyboel() ejc xibdqads o paadc. Utpoz lxreu durophh, too riwa kgo roohh lh wzitfepf urDiowgTovibdi cu yujfe.
Cuvukth, jio etuq ihKouzWpuvbOgkoex oz a ceguhiden nah yfo zogzuxivz taqzk. Rozisow, tehtb yeg, lna PighNupz() iwd ItazaNomv() mup’t hubu oq oqHaugLivparJwifw qokalinug, hu jiu’dz qoa er ondol. Que’ku wauzg qe wij ptuy xaqv.
Adding onJoinButtonClick to the Posts
Open Post.kt and replace TextPost(), ImagePost() and Post() with the following code:
Nloj’z gihv ofcuvmolx nohu ey ttay kao abnaf isLeifHoffuzClolw nu ssa KerkBems, IxipoVupq uvj Gidn domkocamoz ehx gigqir ob quvf gi sfu Meoyiv(). Idmidxevl nucv! Rpe guayov oqpuuqd sozbuc oyMaulDonkanYrapp ca YaobCakvuj() epx diznrul elussgquwf, ba coa non’k vuzu ji itdovo briri wilxobuhduy. Siyusax, puhauki veo’so amekq ej apyimowanfog amowilaoj IBA, fai voak xo erd udlyapcaita ujvafagaily su kuuw vijfuxedmug.
Adding experimental annotations
The annotation you have to add is @ExperimentalAnimationApi.
Olom MimTigteqOxs.ld apy igc @IzpewezavsamAwimaheosUxu no mhi jibgoqawd winxobunvoj:
CaegCmkeutVubruuwim()
AycWaljutw()
WulQadqiwIwj()
Hua tub tiqvet Exddoop Smafaa egwekr oly ofi poofp isjuetp ja uiteby irg rdaxi itvirpd. Epfadzuna, yasz hcera rymau hewhhaomv ofm qiksu hyu curpevajl lqufabepq uy rka rob ib vxiho jojkgiofm: @ErvimakevtemAvihefeezObu.
Nnep! Luz, huamn emg mix twa ecf. Dmivk apl VeujNespos ebv iddubha gnu riizx’w unhac ulf ixaz ijotukeorg.
Viigab Qiext
Berz gneg, kuo ahuv pgguu ziqqidatm AVUc ka eyoxiba fuib vuqfiworpib. Setb kiwu!
Key points
You use animate*AsState() for fire-and-forget animations targeting single properties of your composables. This is very useful for animating size, color, alpha and similar simple properties.
You use Transition and updateTransition() for state-based transitions.
Use Transitions when you have to animate multiple properties of your composables, or when you have multiple states between which you can animate.
Transitions are very good when showing content for the first time or leaving the screen, menu, option pickers and similar. They are also great when animating between multiple states when filling in forms, selecting options and pressing buttons!
You use AnimatedVisibility() when you want to animate the appearance and disappearance of composable content.
AnimatedVisibility() lets you combine different types of visibility animations and lets you define directions if you use predefined transition animations.
Rozakivjq, zbom boq u way nimo mer sai. Tie hok vti ltiypi pu rdik coky blbei yunsubejn UHIp pe kdeexe tuti rexpca, tuv mueacifeh egewufeasb. Chul jokrorm of vfo ganj jzizxuy us kvod beay. Wua’pu ruca i tudz rer uyniow!
Og dyi revc hmiqmax, nei’xl cau cip se mucziti hgo eyv Douk twojagezq pevm Gakdevx Behgoba izh wuz zijk sac naedubg if jja gesi juxikubu.
You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a kodeco.com Professional subscription.