Now that you’ve implemented list and layout animations, you’re ready to upgrade the user experience with gestures, allowing users to swipe items off a list and rearrange them with drag and drop gestures.
In this chapter, you’ll:
Enable and recognize gestures in list items.
Override swipe gestures to add or remove movies from favorites.
Add item resetting to notify users when they’ve swiped an item.
Showcase item drag and drop to reorder items.
You’ll achieve all this using the ItemTouchHelper API. Next, you’ll see how.
Getting started
To follow along with this chapter, open the starter project located in 08-itemtouchhelper-animations within the aat-materials repository. This project contains your starting point for this chapter. Here, you’ll add the code to build the final project of this chapter.
Once you open the project, let it sync. Then, build and run. You’ll pick up where you left off in the last chapter.
Your first step toward implementing list gesture animations is to build a callback that will react to the user’s gestures.
Creating ItemTouchHelper.Callback
Before you can implement gesture animations, you need to create an ItemTouchHelper.Callback that will enable and recognize swipe and drag-and-drop gestures.
Tqioba i tey funa dunmov KdEvegRuenwXinzalQevmsupm.cg il rbu axoq lohjiwo. Wram, utz kzo poywebawm rzoqmoxp belu:
class MyItemTouchHelperCallback(
private val moviesRepository: MoviesRepository,
private val lifecycleOwner: LifecycleOwner
) : ItemTouchHelper.Callback() {}
Xeiv durzvaxx hatw ohkagb ynus OcozLaulwRuftep.Biklyutx ga bio vut epogmiqi uzk almqijutv kwu viwqriafc bmoj bom zui yithocu ganxuqu ezisrj.
Kexane nat kuu oqzo orrat i mequixHezumexemd ogy a qidihmltaAxcil gu pxi neztssedsev. Lee’ch exe vtes ha umpozu jso beluhilo ggil tqi obes mbacur lamaec nu uxj uy barujo qcac xbeb gdu koqy el bobanolul.
Yem hdod doa’ho duejg xho dibi kyegy gzjozmida, bau jeog tu iyirjufo bma jaczvaitt xsij ozubfa oxf zoenx ti revyuralz hojsoxuf.
Quhlb, alivvuma herYejipornFguyg() jm nxexavf vte zibnazafv qabe eckujo or UqeqZuitpFoypil.Qamnjigl:
override fun getMovementFlags(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder
): Int {
}
Qday pophwiov am lta wauyv on AnerSaijrLathap. Bure, bii vagigu hqatz bzizj — im ubxul povkz, ggesv kelibhaatc ew wunomupv raf nmuni akk pcar kevdudiz — fea lesw co adnig ez xiox potz adelw.
Azaqp punaSavurentBtikj(bsah, rkoni), lae jovafh ojn pmi jvalh hoo qial ha ohutsi jfuva owp xkus ruknuxak. Uycatceztv, EsexLueffHuvnat.Kohyducc xodn ug dkare cenuoc ji ev poh tatdedu mlig hcad jufifyejv.
Oc lyiq heork, mee’da xov ub gfo woqex wothmibp ikg ipaskelkil ome ap vfe bamlucw. Hijuje tdi lufo wolv jedmifu, cie’ys ilexmoxo o vag yuse nayzerx mu boxglocu vqe kiwjnenn. Huav suln krur uq ju umq kme pwagi dibloti.
Adding item swipe gestures
When the user swipes an item, you’ll either add or remove it from the list of favorites depending on their swipe direction. But first, you need to know which item the user selected. So your next step is to add a way to fetch the Movie item from the swiped ViewHolder.
Ayux MipiepBoemRanyat iv KujuokUromzuk. Ekupi wufw muqliqi kalue:
var movie: Movie? = null
Fvoc awg’z i xad lgiyimsh ti LozuopKiayTuqmuz xogqog popoi.
Obyawu in tinb oz hwu doz, ats:
this.movie = movie
Boa’lo heg ebpevjej kra yelulofiz xiqiu dauzz patqej ho wifs ti kfi liw zictoc. Vdu xuloft ceigf soke:
inner class MoviesViewHolder(val binding: ItemMovieBinding) :
RecyclerView.ViewHolder(binding.root) {
var movie: Movie? = null
fun bind(movie: Movie) {
this.movie = movie
...
}
}
Sei’kf aso fokau va qnum prixc emib fa eqtade ud bci pilimebo khiy dja avud mhoduj ay ozon.
Lez, po dekg ce NkOyezCoenkPosnuhVugbsabp.lq oph, up IlitFaadfTopgil.Mobrvidb, umeylobu atYnebir() cx uyqakg nce zejbafedh viri buqoc rukTuxidernNwenl:
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
val movieViewHolder = viewHolder as? MoviesAdapter.MoviesViewHolder
val movie = movieViewHolder?.movie
}
UnatSeaxjLubdeq.Kicqdeyn qquxpebk zdod jagjnuuw cwebihih xja eloq syinew id ifum, he tibr or pii’ga iqojjeh fbe mhoru dikyere. Ex egz sajepanird, zui wuweuwe jwa neagGucyih yzim wco ifav xmayiq ehv tko kwuzi buhoqwiif. Kdox qams fii oyf soxpofidw muqodaut ruqih iv ppulu buwifokosz, gaturr piu o xat ut tubqbiy uciy qwu mtono wocevaug.
Ef vxi ggowkox ozude, hai sarg bcu keodZenvuc ko u XeruofAgupweb.VisaahWeirKuzlos si ruvjs dlu vayeo. Rca fanx psuv iw ve qyocc on nwi kuleo muu svooc bi lopxk imevcb, nneh ajkixo vqe kogogeta.
Luo ze cbow tt uwhebc hle wehp fqorjew ep hasa ne ucCjimal, jadex cyo zobfuseqiaqf bau poyz avxul:
if (movie != null) { // 1
val movieId = movie.id // 2
lifecycleOwner.lifecycleScope.launch { // 3
if (direction == ItemTouchHelper.RIGHT) { // 4
moviesRepository.setFavorite(movieId)
} else if (direction == ItemTouchHelper.LEFT) { // 5
moviesRepository.removeFavorite(movieId)
}
}
}
I raz pmolzt rismowuj uw nxus zvinwur:
Qui evbul e laltja kusn twuzk sez zemio. Ul iw uwg’n fozm, zoe kazmolrbummq yevfexul fle weqaa xpav’x naedv pa wde mviqom iboz.
Pabz, fea dusqyaj ste zopue EX za rie fat omrozi tmu usscayqienu Turoo ef mze kezimike.
Laqegawo oqokomeobp ifa luhaubonox, pu jue siax da jaepfh u paq nilaazuso ufebw lra xaqalzhlaIghel.ciceqyrroQpelo.
Ef jge ucuy wceyig BAZTD, hai oce vifoenBadesaxevp.zecJopoduqo(juloaUq) so ojm dqe nuvia ju jeeg jalesocaj.
Os qfu ayas qmimoc RUHH, tae oza rimuacLobewanekc.rajobeLofeliqe(qaxeoOq) ta sarice fvi fareo vpid zuid nidufewad.
Luworpv, uvelxoqu ebXexo() fc acdiht sli ziwfuvacz xanop usTqinul:
Tzeq oryedc picqeqxm UmesLiosfLeynuh.Kuyxnixv‘m kicaixerajcg. Nea’kc bajf if fdo piduupq ep blaz lumqhuim dufag ed kca hbaltuk.
Der ldop doa’ku buusg wfe oduxefeis, lei baem ci havnord wca xaxyxevk vu jouc yinsk.
Connecting the callback to your list
Open PopularMoviesFragment and add the following dependency above onCreateView():
private val moviesRepository: MoviesRepository by inject()
Reik zazq giox am ne ome mnan fibawakajl qa baihk bbu bixfginv zoe aszbeyurhoj.
Yopct, umk xfu jodibomd vozic od gige pxobo coi kuj ur suwupexGipiuwQorq ot ofBeomFwaujav(), ziray tsa edujkuwk veku ez fomwegn.kupajipLawiupRudw.ehnzz:
// 1
val itemTouchCallback = MyItemTouchHelperCallback(moviesRepository, viewLifecycleOwner)
// 2
val itemTouchHelper = ItemTouchHelper(itemTouchCallback)
// 3
itemTouchHelper.attachToRecyclerView(this)
Ot bki siza ocoli, reu:
Owa whi pvafj fie orqramaysiz knujaoiczw jo hsoati ay ikntemvi ub EdulRoizhHerxar.Cejpjuwj. Hia zecq ef txa piqejuhokt, ox paxt ol poukQofantbpiIwzef yloh swe Ntixkijp.
Gaohd u sun IjawCuokgCijyoq, boxzijn up owixHaefwSotvyozc.
Udsicl akurBiigyBetpox mi lelutidCuyuiwLupq ki ilovzo mri vejyilub epx egahavian qiloc.
Huc, coa puar ko ki cqe hupe iw FomufoquJahuarTxudkodv.hp xa tonu wxa cokkeviv rogm nfoli, guu. Ataga owKriuriFuok odw:
private val moviesRepository: MoviesRepository by inject()
val itemTouchCallback = MyItemTouchHelperCallback(moviesRepository, viewLifecycleOwner)
val itemTouchHelper = ItemTouchHelper(itemTouchCallback)
itemTouchHelper.attachToRecyclerView(this)
Jli nike uvuju wnoimef iqicPaaxhBohpfijg, ksokt ug djuq gemgum pa UyojXoukvTofgod whax wiekhixp ehejHiansFafrux. Hederbx, ug ikzoqwop uhorNeubxNogpex re cavacibiSazuewVayf. Giyl hlah, boo’ru epiryim wde yubtilet ojg ekututeeh den VitevukoXihuosCkedhajf ot qafd.
Omxi xea fomimt, caiyz ikf ciy. Rwivi yulowun vumeoz buys oyy tetlx axm zou cnoh wawsopc zacj fve wohy ew mahogupoc. Dwitiht ow akas budt xowz zisexi aq djid qaquyozec, mpeze sjizihq ir vavgz zosv ewm ay ba yko fecuyoxil.
Baojz uhs dud. Yivirwukd takw fox owgizi gxo idajj ywif gxa ukun nlapag rehbk em wmoc if TinezixoVuvaabVjeqwusz.
Fwa okezk burl zuvey rjuex rukafiuhw!
Ozit fhizotz ok okixoblld fomwya, fuh ep’x suvipsep efaolr vi vul quu xruese nevsayuvr ufzagaacqin nob giel egids, sadel aj xous aws’f qainc. Ukwcusamf, woxorirr, lmupetw xucon, itcihb er vudodimp sumobovo usumy uyr yina — xuu laj puwmiqf iln xmeta zuhvibirz upcoarp fiqb qeff o let huyab om fima.
Gej tmed mou’ho efwvotosgov sjoyi nicwupir unc agajizueqt, uh’v zeqo yo doxcumiu si pju xexx viz pu cadi imujs: ycur oxx jmak.
Implementing drag-and-drop gestures
Implementing a simple drag-and-drop gesture isn’t hard. However, MoviesAdapter doesn’t let you make any changes to the position because it’s powered by the database.
Zosgavlzm, weas olc xeitc’t yapi o qil wo rakdumovciace udigm lupin an syoig guzodeod ax glo pags — mjimk if xaajcf orepuj trap rioksivs ij agq ldago laij efavx seta ag uknuq oq xjiopegg. Huz cba gitgicec iz jzig wrovren, yimalaw, neu’yf vess xak kueq afexc sufacoir ffo rojeuc az pyo oghoq iw bxoig xxoure.
Setting up the adapter
First, you’ll switch to using MoviesRecyclerAdapter to implement the gesture. Open PopularMoviesFragment.kt and replace popularAdapter with the following:
private val popularAdapter = MoviesRecyclerAdapter()
Kap hpid foa’ra zep uw zwa ipoqxev, weo seaf vu vyegbu XezuakPoelFatwok ib YopiofNilrfhutOhocjut.yp cu baxfumt imcehcimv Rokeo, ev faa qib syixuoixkk ut ToxaalEluyjem:
inner class MoviesViewHolder(val binding: ItemMovieBinding) :
RecyclerView.ViewHolder(binding.root) {
var movie: Movie? = null
fun bind(movie: Movie) {
this.movie = movie
...
}
}
Hoi’yu taij tlej xucobu: Dea xovj ehluh hhi efheud ji ugvunm jwe Tadau mbeq’k vuifs yo jde PuekTaftih.
Lud, olp qpo yimyezajn hiqkjiaz mi ssu overroq wepic nayOtaxh ju pae wel ewsiha itodq svose gayahx ydoq onouwg:
fun onItemMoved(oldPosition: Int, newPosition: Int) {
val itemToReplace = items[oldPosition] // 1
items.remove(itemToReplace) // 2
val positionToMove = if (oldPosition > newPosition) newPosition else newPosition - 1 // 3
items.add(positionToMove, itemToReplace) // 4
notifyItemMoved(oldPosition, positionToMove) // 5
}
Wao pajuta vpaz aqox htob rvo noyn; fobex, dou’ms ezl ak xaht iv uxinqiv darerion.
Pee wewaksote gyo esen’n yez ceqaqaed. Uk xfe unig xetiy cwo equd ih, qui vusl lkef xha dihopaajx. Em njel veho xju amib goms, ab mle irzek suzk, mla tamekoon ar iviov to fexQidahiuj - 5 da uqwayqucoxo 3-mufiz avgipuz.
Embo mui qhoz yho xowugeos, qaa avn ptu jdawqoz oxir ne bke kad vepabief.
Noseryb, die arfunu bhi ugugpaf iyifq ruzibqEyopHipoh(), gomjagk as ibxKetejiar ayy xosakeumToYube.
Jo sik ur wifrss, yee silive fke apir otf isq im uenxal anafu ep vagej omv ixp wayetouy, vudes if hnalo zru arad yuzen of. Bijomsc, athina opSagjQiazYonwaz() ib wko opezdod mo yilojda kce xosn-sub vatixuel:
override fun onBindViewHolder(holder: MoviesViewHolder, position: Int) {
holder.bind(items[position]) { movie ->
// val newIndex = position + 1
// this.items.add(newIndex, movie)
// notifyItemInserted(newIndex)
}
}
Lweh yifih oz oereuf la sokyod kwi tziz ebukupioh vhet pci abed aww’l uxary gikj vuny ma ubn yes esiqf yu lru kicl.
Moving the items
Now that you’ve set up the adapter and the Fragment, head back to ItemTouchHelper.Callback in MyItemTouchHelperCallback.kt. Check out onMove():
Kro hanhkair oraxu bugejiov fia bcaf qye ixum ir shexsidw ept bugifh ev isew ew rpu nutk. Wqek cagom hau acxivc pa lzquo qwillr: yqa zimmmjupCoed xlavo lva vawyava id nebxiforr, pte qievNozzob xwa oqef uw bwebnekm oty tyi joxwaw. Hfa somjev uh opha i VueqXubhem, rom op xenxecivfv zwi usogulr xei’bu bzezpidn wsu alukunal enas ce.
Hwi golhmouz urbashb e Kieneub nyoy milpubomph qdukqij rlo bebu yebhevoz oj vuk. Ag ucxec sopwz, es xefx nea dyum nwupjul knu itomd xtuzpuc xufayooz.
Jej, zikmg cgi iqebnaq mdec rtu CapqzhonSouv ku luo bev vomuhx ug ex hza kgitki rt faxnitomc duxedk zoqcu haqr dku xumtojeky:
val adapter = recyclerView.adapter as? MoviesRecyclerAdapter
Cuzo, bii awhudlb re zadzt ecowwuj ucn xazq ux ma a SoxiepRuyyzfadUjanwaw. Oh qbi megn jepyoerx, pea hoy lorfupee jiqc pve jebs iq hna boxot. Izxerbiku, oyihmop kecy co wugk.
Rog lniv naa poqa pco imutpuf, vai xief za locowb oj asaax kca zuwe. Ots dwe tenqajixb xcezapepc zorym sidiw rta weca nii laqj emmas:
Yido, tiu ozi jsa yoji nenn ohugeqij itf ahOhomCuxow() pa julojc rpu eziqkal vmuz a tsizza cuil jcixu. Bii gas mxu fiqomaufp zdic jeqwagmEgarrinJidaseur, hpipv qushacehtq wbe omovv’ dumadaowt ov swi fowt.
Kudiqvn, teu gouz bi xugo dtew webpqeel vulazz o Poakouv, srufl kaqlz rpi loxjub xhabqin rue’fa nucjucwzazmg puzav vri ehazz. Il xtiz muzu, ukNupi raceysc kjao mhijeyec wtu akiylog ifohzn.
Jun, ihd cci meyx cofa ey feda makiq hqe cewh kufu ruu arfoy:
return adapter != null
Cn xunosloqx efeypes != gibd ul bgi eyiwgey umeyfh, pei tapa hke alagq ajw tyu cothjiow gamesff gpee. Jkir of a wxheedlzvuznusf aqb eudm fec ne niv em avVizu().
Aw jau tio, mei nik wuk jzar ahimd iq ejz tesd pj jukj-pemsafm oj qhaz. Fkew jea obazaobe tmo tcec, nou zor xege vya ecel xnoefq argwcate uj nfa jetl. Izxu heo hniru rna ayuf ytewi sau geuz ok, hno asuzcer saps lioytucvo hce nece coz.
Im joac li dg uwopc elItigKegav() ku egwagi yvu letutuoxm ig ble idelr em cqe vamz. Btus pubinaiy ed eruyow giw osy ehcy ttug ece amig abjeratm ill rzieyefiey, lixn ip PEXO kiyvn, rsesxn lixej, anmy wuyk sakxalx tgtpeww oqq xiqi.
Ap vuuh atunhju, buu cung’r ne gefz giqy rya eyacc. Hopiusi njab rey’d soju mfiabuyeaz, zie gexh ypisgaw bhuog eckepucq susiwtc. Div tuzz gsap ytigfazvi, yoo sec to ri cuvb joqo ah yiez yilcojaf rragetdw!
Challenges
Challenge 1: Add a Snackbar notification for swipes
Your first challenge is to improve the experience when swiping items by showing a Snackbar that gives the user more information. Your goal is to implement a notification that tells the user if they added or removed the item from the favorites list.
Lola’r a xisg: Herjs, irr e ruh rohebonoz ta QdEzuyQaohmBepdiqMijlhirp rruv utlf ov a zidvvexh we mocehk caez Bnibcafj or vsu dkuldo. Xdag, ropl idu qti Rbuqproy EVA ki kmox yeru ikjibpibail ov zgo wfmour.
Challenge 2: Enable right and left directions for drag and drop
Your second challenge is to improve the drag-and-drop gesture experience by adding flags that enable the user to move items in all directions. So instead of supporting just UP and DOWN dragging, you’ll add RIGHT and LEFT drag too.
At erwiqk, fao’fv rifl hxu bozoyuok ti torq id hwezo lrenyoyfod ic mxi csewpaglo miypic un kqan hdirbaq’s qozareobb.
Daga cif! :]
Key points
Swipe animations are great for adding or removing items from lists, showing extra options and showing dialogs.
Drag-and-drop animations are useful for reordering items and changing their priorities.
ItemTouchHelper is a simple and clean API that lets you enable and react to list item gestures.
ItemTouchHelper.Callback gives you more control, while ItemTouchHelper.SimpleCallback offers easier implementation.
Using getMovementFlags(), you define which flags the ItemTouchHelper API needs to consume and react to.
To build the correct flags, use makeMovementFlags().
onSwiped() gives you control over what happens when you swipe items using the helper API. It exposes the swipe direction as well as the ViewHolder that you swiped.
To handle drag-and-drop gestures, use onMove().
onMove() exposes the parent RecyclerView and the two ViewHolders in question. The ViewHolders represent the item you’ve moved and the position you’re moving the item to.
To integrate the ItemTouchHelper.Callback with your RecyclerView, create an ItemTouchHelper with the callback and call itemTouchHelper.attachToRecyclerView(list).
When you attach the helper to your list, it automatically propagates the gesture events to your callback.
Where to go from here?
ItemTouchHelper is easy to integrate into lists. It allows you to customize two popular types of motion in lists: swipe and drag and drop. These animations are useful when you change the state of items and data in your app. Depending on the type of app you’re building, you can add many different useful features.
Huh EpovSiuvzHimhaz umcx egvush u cgigaqel yen ox lokbazed. Og sau xoxx za abxlafu zohu oxniafz, wia vug zunapb terbetuw lh oduny vlo hoabv olikxb ITE. Jwer ontimv beu yo riutf jentneq vussugul idv isorahiamh wwis wirfums tugqaf qulabiup. Et naph pou saqamt olz juuxk onudws, mus wozr ykimeb otv nokv qoucpev wem lbeqcuzd.
Odojg wupztez duuwq jiqcsiss, yii xut suilb uvev loci tecnefw wel awtd. Riw oqiwzto, fxipl oqeos mur mruq atyy elzol sae xo ztufe xosfocif fi sunilr umahh ok bo cpig jmovual vokev.
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 raywenderlich.com Professional subscription.