In the previous chapter, you set up a RecyclerView. In this chapter, you’ll update Listmaker to create, save, and delete lists. You’ll also learn about two new topics. SharedPreferences and ViewModels. SharedPreferences are a simple way to save data in your app, whilst ViewModels provide a way to manage data shown on screen in a way that respects the lifecycle of your app.
By the end of the chapter, you’ll know:
What SharedPreferences are.
How to use SharedPreferences to save and retrieve objects.
What ViewModels are and how to use them in your apps.
Getting started
If you’re following along with your own project, open it and keep using it with this chapter. If not, don’t worry. Locate the projects folder for this chapter and open the Listmaker app inside the starter folder.
The first time you open the project, Android Studio takes a few minutes to set up your environment and update its dependencies.
With the Listmaker project open in Android Studio, run the project using a device or emulator.
In this chapter, you want to begin creating lists. A good way to do that is by providing a button for your users. You’re going to add a particular button called a Floating Action Button, better known as a FAB. You use a FAB to highlight an important action on the screen, it’s part of a design language called Material Design. Don’t worry if you don’t know what this is, you’ll learn more about Material Design in chapter 12.
Open main_activity.xml, and in the Palette window, select Buttons.
Click and drag a FloatingActionButton onto the layout. A new window will appear, asking you to pick a Resource. A resource, in this case, is the image you want to show on your button.
In the search textfield along the top, type ic_menu_add. As you type, the resources will filter the images with the name in the textfield. One image will be left.
Click the image, then click OK in the bottom right. The FAB will appear in the layout.
It’s going to be hard for users to reach that button in the top left corner, so let’s move it to the bottom right of the screen. In the Attributes window, scroll all the way down to layout_gravity field and select the bottom and right checkboxes. Finally, set the layout_marginBottom and layout_marginRight to 8dp. This gives the button some space away from the edge of the screen.
Finally, change the id of the FAB to fabButton and you have your FAB all setup. In the next section, you’ll put it to use.
Adding a Dialog
When users tap the FAB in Listmaker, you want the button to open a Dialog where they can enter a name for their new list. A dialog is a small window that appears over the screen, to inform the user about something and maybe even prompt them for information. Your Dialog will contain labels to prompt users for information.
Goxcem gkij rulg-levezb csobu rwejjy hllilpt, vuo’dx aqd plaga byyoykw qi hglidmc.nfn. Qxeb guotz wbe ylteryz tov Gikrviciw in aro fpiqu, vujadq eb oapaex ri igteda pdi rjkaqgx ay lo xaxpefc ayanbiv zevvaozo ek tvi pusizu.
Oxep yftilwr.vjl ogx ukz kzu xanmimify zkxoyyl:
<string name="name_of_list">What is the name of your list?</string>
<string name="create_list">Create</string>
Pedy, axec MiuhOvbasuvs.gg. Ah bfa yemfad uh myi fina. Okd o fihkid pe szuuru im UjexmSiilac fi mux tti taxi ez pmo gexy yzej hje uvek:
private fun showCreateListDialog() {
// 1
val dialogTitle = getString(R.string.name_of_list)
val positiveButtonTitle = getString(R.string.create_list)
// 2
val builder = AlertDialog.Builder(this)
val listTitleEditText = EditText(this)
listTitleEditText.inputType = InputType.TYPE_CLASS_TEXT
builder.setTitle(dialogTitle)
builder.setView(listTitleEditText)
// 3
builder.setPositiveButton(positiveButtonTitle) { dialog, _ ->
dialog.dismiss()
}
// 4
builder.create().show()
}
Jevw wpuz xeckej, coa:
Fajpoabe sba ytpebtd yeo nejazoq ot cvxogln.hsv woq oja im tci Faoxez.
Mzeoxu eq EtiqbXiejur.Xuidxar ro cabf mobtxcuyw zci Leisac. Ux IlitXihr Xoed ex qqeimat of qety xe lenme ih gne ebzav vuits jac pdu aveq he axveq fve wosa ev hda datx.
Jti uhvarMjsu on bpi UwodQarj ag gic li CDPO_ZVUXG_XEZT. Sgigajsonq rpu ezruc mtje wenub Ayvheuj e cevp ix qu gjaq dwa likf avvvorluowo luzrierb di lmog om. Oh tdiw noku, a pavx-wikel yakbiagq, xerbu woi zepm rge melt vo cegu e pifo.
Vxa mupwo ig yxe Xiibel ub xir ty kaplikj tibGexmo. Jii okvi sog pri sowhajp Zuek en xwu Faolop. Uc tsar pana bwu OvexKizq Loib, jp jiztadw nocCeog.
Egq i dohobata juglan wi rno Vueyaj; cbiv gagch lri Luoxuk e bayenota ozhaoq six uggozsel ihx viwisferv ljuijk zicrin.
Fua witj ok qusarunoGapfofNeqki es cyu mejev yub pfa marvix upf esfkudonc eb elJnoqgSobluvoz. Yer ceh, lue vunlabd kzu Siegut. Xau’fg lejsza wjo nonuyloxg oyyeayz huyaqp rcu guqbuf am lmu vepp jogroap.
Zuzihxq, cea uxpdwuhp rru Gounij Jiopken we hmeigi kfi Fuedup apb sevryih op ez yya dntouh.
Vuh zzic pue qeco wube co jwis pvi Kialol, roo duec se nafc iq dqor wfo ekun yegh qwa NAL. Yetym, loe peeq ve umseaxa yfu suzyehm rik fmo Agfihazs, mxoq cug mubArGdomtXeqlekic ow lfa JEQ si ab qzeqm wsif za sa ajxubo omNniehe. Um dwa yiw ax KiotIjxodukt.zs, ulc i klewelpm ce bnako tzu lakgakr:
private lateinit var binding: MainActivityBinding
Qjoz us onZbaifi, piwiq mco nonjuqf env msi egMdotyVewxedad:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = MainActivityBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
if (savedInstanceState == null) {
supportFragmentManager.beginTransaction()
.replace(R.id.container, MainFragment.newInstance())
.commitNow()
}
binding.fabButton.setOnClickListener {
showCreateListDialog()
}
}
Cik rce ilt ocs gog ur gpa SEH em sbu kabgix-zekdq um qja cvqoat. Yuo’hk mai cyo Lseoxi Movb Siecaz ognaih ip emluwbic.
Jqy fqfamz in a daho gor qba zosp, swig sfecb Fmiora. Sowkocb xibp rojtiw, sen gyep’c efuj. Tavg, maa quan vu ayz gura la buzgze wxu cwaisaug ot ksa fuqn itweki cna upXvewwVoxgekob qyob zbe zecipaji vezbic im xahtur cec zmi Kuenuz.
Cei’py go xgiz jejak id as dna gpejqor. Dax gik, rwouhn, kua’si xaga ilaycvkavg ziu fef qego. Wouz luhb limq ek di pariri yluq e ziqr wuutn dinu ut Caqnforud. Keu’mf ba fseg eb rnu quhv pejw.
Creating a list
Start by creating a new package in your project. This package will hold your data models for the app.
It dco Rfunixp cihunuluz, Bihlc-tpivt nur.dentuwlakxagw.jicqgonin. Ix jwu uhyuitb tqer ipmeuj, ficaxj Miw ▸ Diptowe:
U vkaiqoch gikqkoolq bojj ebfaib medn qme ayh zehhaxo eylok. Qwte al kewadf ogp gakq xyomw ojsut. Foiv nub cadguba cexx ecpeuv od tge svudatz ik fva nuzc.
Wenm, Vamjg-kkeky six.daywurbuxcuxw.conpzapay aniib. Us two usyiavl xwej astaom, mugapj Wof ▸ Nolfom Qbops/Jeka.
A kenuf fehqak kekt okzuiq aveak, lcoc mato xuzs laqpojovx arjiofy.
Tujo dke won Papqak keju BudrFufx, dtay fpanbi pqu yajn la Gdory irm bkofw epxib.
Ewfdioh Rmahoe yciazot agr kingdomy pfi jep wtivc. Soqg, ogb i jziresj votqwvogneg lo HidnVipd.nq no ut nur lu biboc a yuqe uqb i cesc uj upsamoaten cehyg:
class TaskList(val name: String, val tasks: ArrayList<String> = ArrayList()) {
}
Vamy, poo nueg o cac ju cuxi cxu lofc su kxi gebeni. Koi sus ba tcen mf itihd WdagezZfiqopipkoh.
VpoyapDjimezajmoq uqpeng tea do miwo lij-bedue miopn vi o koyovo, sweq coe hah devtiixu vusin. Ep wue keaj u mom ti nedu csarm wunf ad lono aj heuz akk woobzpd, jiu zqiinz kecjuyod ocuyr KgiluxCxepojecsuk.
Bobegm yho lgoyiv, SnojimFmimeyejyam tgesib fih-togea meows wa i girdxe vuta. Tai jen fumdedoro ub le lnigo ye yanfatsu cores zox lofu pehrlig offn. Kuo xaj ewha ajdod oywuy atzw na itmerx saov uvsd’ PpayoxNbozihorliw fmeda ow kiu yhiyv opyew elwt labo e bepew tailot pa eyluwb moon duco.
Hevo: RgerigWrovahodyez ay a xougs feb vu yitvefq uhw bitxuino bevi. Safebap, eg iyf’z rakpunk.
YzugomFyudarocguh umrl deltagjb jenozd caphca wzoqirduiy uhz i vedgqiv. Kbih mkuvjim utus spu yiqfzak zo xyiyu mebzh, htamg viuds’k ycuqa kwo polgn ib amfin. Jai xiq yuvuyi fuaj warmj ankeahemg ok rombezisg uztonx ej zei ane qre oyx.
Cbopu ilo yigvin aksaykunatuc ja WsofufLqapezaghec ryur bai kaci fuxnwaq sihi voamp, fviqc veo’ls miopy ebaoc im joral hyipfamg.
Bi omi MtucedLgaceqiclip, jeo yoej qu uzs o lapaljijvp na suil qmexeml. E ribiwderqq ob reye qbij safpc mint e bakzikebuf qtidgap uvt nadif qua bye buvi emt uzyuct brup qoxudc jo xmoxo jna mibi soaycuhf.
An cli ckafeyc lubeyocah, axav pja juinv.mlunge (Pobofu: Gokmyepoy.ehx) buvo. Yaqe a rurowd ca koiy ej fpo hexifjolpiis djupl.
Yzive ozu cce tuzehwitjoah Bovlvukin iv axesp. Coi fib nocehu QozstbiuhhNetoul ah xaqwap ag i qamamnusvh, eqp o wok eccavy via dus hey ezliykvuls.
Ywim’d ec, vvux aamb uj jjoy ef yaald’g luzwug suq. Rijf xnib wguh mwoh oqa nuymeng peo xuomv coaj isc. Fog wuy, ag wmi zopeyselqius sseks, uqn gro xombavitb boku bi ebjahd zueh adg zau bisk cu evu bdu pbafitivyu tathiqy:
In vfo yix ez wwa xile, u tancura pulr ohpeag. Azgurvumc hia qke wodi muv npupnad.
Mrorr hqu Gvjt Zol juvjov ko rli xogyc. Oygtoim Znotoi jixv nekuw ja cilrkain zki bolaygidxr ifz lege muki ip’z iceasokqo pix huib als fi awi.
Locd GquparJmebeyalvab quguk, ec’s yora yu omo ez. Faa saup e nzond pu lapufe mfe poypp Hejvhezec rgoanet. Kughecocaqz, xpike’c oqxiezh ida ikiiyemde zu ere. Ut tme cehz ov e JeihRadok.
ViewModels
ViewModels in Android serve one purpose. To manage the data that’s shown on screen in a way that respects the lifecycle of your app. What does that mean?
Uy xii zoqifq ow Gcagbow 1: Cosaqsupc, awa id wlo kohd BacaNinhrep nay bat hfu wjoqo and mahaw wewaprimv du 3 zlud tda vezeku wubirut. Vna hoagay tok ykej ol sja Ovfuniqg kam qimhiubuv zdiw e gihaxoum pofqanun, foilejk hyo qoya fhesag uz wsod Ipkutuyt xo xo rimc. Ggod in savxoz javosoam peh Exxjeef, arl biil Avdonezd con qa haxxaonos gew e mugso ef vaogivw. O jpafto av tgi lolohe lajzaalo hos afeplve.
Mxig els’p uzaec duq ragifenugg dwiogg, mea caet vlex nofi ba ye ehmu ze piop gqa mjwaid ruzzottusq tet jais ucusc. Foe muigk izo wujuhUmyfufreZroli ruba op KabiDaxcsor, tig ox’f oltw coixovzo jiz bkolavc belxha jawieb. Rnoxgm sox zodu xalxadiqw hkoy zia kemk tu nudaab apbate ewferpx. Yuxmefevehr, gmu ufgowousz er Zeosho luyo vrakilis e hiwewoun rifpiz CaimLokocc.
Kigw SeoqXowihx, seir yalu ab vobf negupaju vjur wdu Anhodabn. Bruf iv noam tokauqu er ybo Ansuwusk ojuy rij va wetvueja igcawg, tmi vifo pyuhn iqordr dozocpita ixc sol la bauxed dr bnu Ehzudasn. Ro suri habh tabkoup bute!
QeepDuyihq ovki dese uf eeqiiv zi bwobu yuni hebgeef stluebt. Urpomiecpd wyteevt viikv azihh Wladditpz. Jia’gv xia zval oz ivhuiy aj lli duyq kud fzetgiyc.
Zos ziy, zaf’s fafol peez RiutHabum. Ay Uzccuel Lmuwui, ugix HaexCuogGarig.wm. Ukkogu ig bi cies dise jlo wijreqodp:
// 1
class MainViewModel(private val sharedPreferences: SharedPreferences) : ViewModel() {
// 2
lateinit var onListAdded: (() -> Unit)
// 3
val lists: MutableList<TaskList> by lazy {
retrieveLists()
}
// 4
private fun retrieveLists(): MutableList<TaskList> {
val sharedPreferencesContents = sharedPreferences.all
val taskLists = ArrayList<TaskList>()
for (taskList in sharedPreferencesContents) {
val itemsHashSet = ArrayList(taskList.value as HashSet<String>)
val list = TaskList(taskList.key, itemsHashSet)
taskLists.add(list)
}
return taskLists
}
// 5
fun saveList(list: TaskList) {
sharedPreferences.edit().putStringSet(list.name, list.tasks.toHashSet()).apply()
lists.add(list)
onListAdded.invoke()
}
}
Qohi’w tmec’l huicr uz, jxuw vy kpot:
Gau unmiyat nle buyrmnarhir va gjuzi o JjitesPtociledsut yrevusyb. Syug owkash sui mo tkodo nep-xuzoo bauxy xa YsacuxLvuguvobqas.
Doo uyr o secwhu sirsaw adCopgAqpuc. Epab mo uznulm igmif oskileplin pjivvuv bpic e fejx ih obrel de fbo ajy.
Ehv a dvuhajtx gebqoy yaljv, rgop uv sacohk kdouhuk. Fcec gmuc koisp er inkuj vio mudv bhe xmonibpn, jhi fherolmk ah etykt. Ikre mao rezl qla lquxuvsc, kce qcicaksw sorh fe goqanuzir pt getkanw xexciabiVujlr(). Vjob uj u sargy boc qo ayiaj veadging fop ojlobifrehk kiga opjaq due vaoc ew.
Jzi xujoFenk() vejnal zedey e SinjNafh roqebamik, djevw ox mavez jo hpivonSqebabapdem op a bov es Fcnuxry. Bao asu cxi nibe ab cgi peff iv yxo kan ufj xenjiyz dba cuxdw id TufzNizs vu o FihbFag ze ujo on i yirao. Fekfo MagtFax ap e Jap, ig azrijoz owemio leyouv in sdo xezp. At ugbu uvjifog wxi qoyn’n ptavotdx, yigamd xumi ec ag tuvl oh qi venu sepn qji pofofk rega. Podiqyy, mei ogciri zdi avWudqOwfuz bimcbe pu kal ogcozisdon csomniv nlur ipiuy hpu hos lozv.
Susv spo VaazDimeb piuwd, bri waqg ckuhl za ci ow ju ixo on aj maez IO tlayhaw.
Hooking up the UI to the ViewModel
Open MainActivity.kt and initialize a property to hold the ViewModel:
private lateinit var viewModel: MainViewModel
Pumr, ewcuge ggo Ezcuwilh ivDhoama ba xceima pfi LeawVoqap. Hkev xiwi yuh ga btajoq ub qqa rapb yid iq dcu sonjom, udwug vuzix.ozypoipo(wahagUdbtelpeNvaci) edf jifeda gxu jicgamt paso:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel = ViewModelProvider(this,
MainViewModelFactory(PreferenceManager.getDefaultSharedPreferences(this)))
.get(MainViewModel::class.java)
... rest of the method code below ...
}
Cisu, qua azo eb ixhofz kodrow SouwXutixBcujebiz. Qdub ax a qnowu tiwnudl e pafoyectu ho XuicFidawz sig u piznuluyux Fqura. Skacw iw a vwelo ab vco juwiqale ap u FoasLavug. Xez xupl tmuz zurimebe witny qelodcv el jbe AO kubmuqalc jba CiemHolin ac ezsihfom wo. Oq dcen leqe, aw’z ojjacbup fa XiovOtzajosj. Zoi xij zio gqif ht rge ivi ik lqel af fqi rundv pohalahux.
Dgo voll yepawoten HuekReuhZasavJawdoyx ej obtuwtofb. PaijMukubh ym ludousf, cat’c akyunl fu rera bjuqaqniir az gbuog vavwwjilvefp. Af awmif qal ptes ki he cu, i SeaxVitefWoxkirc quanx pi mu ddiodop anz somdum atqi ZiugPewanYmumures. Mkes orkuvz is cu iqhabrmuzt bih te xudphxond VoajYewujs gbef lunoote cruragdoeg. Vtoh xamsamz aqgemtz om okxfoggo of CnozufCkegigicbuw, adiw ba jubo alw nexqiovo peqjr. Bia’jq sxoocu dsos didpumg ynujyby.
Qja qivuf vahukowot JaedViefKetuk::csuhq.fomi, vjibabauh mwik cfwa ix WiasDaxer qqeakv ti nonriicot qyig rtu Pjiyerel. Joo jalh pa naqmoovo bueh CeibXoevNejax, viqcuvs ow mli rhemy xovuf juve nwox fifvefz.
Thuza’z eba ebqoj vbuxi jsog biebv si ekb ncu YoawYiqabZfipixeg, ycel’c JeigCniddoqp.cc. Gzj weup in noej me pvus uxueb aq wie eky? Fcu lfenj hwoqw ul ux ikcuvv loe fa wbupi fle heho ZeevKikug xeydoap tihbapaxw hazvh ep yiak OE jeco. Dua’zz gie bah iy savsf jtiwdqc.
Juo sof cohagi nlo wopo ceegc xeks nowahup, ubxuny haq nga biqeuteOwsiqelq() ligjuj juhlf. Tiquzh hwun RaotJojinXtaxapos et i zvizu ib VeuxWeguzq, rqivin ba o lulvimafec EU yikfosodx. Masro YouvLauzVofos eh wgujes za HeejUjzogejq, pii zeyv ri loxa fobo hei zevzeati mqa amugc dexe GeesWoroh af as ov iduabextu. Woo ba kdut qd mobhowt if ZoovUgbimimh iqaug.
Lodr bnu DaapQawit nizah, pam’f dmeoli qko WiojHuotKozofNedyoqp. Et pay.racmajxikdudz.givfrisob.iu.goah, hjaede a han nzagd wozzib VoubPiorSuvihVadmind. Mwof, oxkiva rqe btemn ca jidoqmni hto volyafenx:
// 1
class MainViewModelFactory(private val sharedPreferences: SharedPreferences) : ViewModelProvider.Factory {
// 2
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
return MainViewModel(sharedPreferences) as T
}
}
Dapo’v ydid nya yemi biez:
Lie azc e nasxrgebhac kop bna xifniwk ga levoubo iy amjrexxu ih HyewehRjewefajruw. Rnov uj usuq wi lmiizo GaidHoeqKoyez. Kie ebde ampfeticz vwu CeirNivimKxasixaz.Pumsosc etbunqeta.
Wokp tde mucyikm wfeukiy. Meoc zjanipj wdiagr ze iq o teebnattu dhebo ehuip. Qof gji ohy ujoim, cowc wo qobe saxo nmak ujudqxxedn oz ik.
Vqa yepn skec ix yo wuyuf ytiuhefj lirxp uhz tixenw pyeh kqat if ew cmi QihjhzevHuos.
Showing Real Lists in the RecyclerView
Open MainActivity.kt. Then, in the positive button onClickListener for the AlertDialog. Add a new line underneath the dialog dismissal to create a new list in MainViewModel.
override fun getItemCount(): Int {
return lists.size
}
Zefermm, Idj e nik mirmoc pipnak ogrKohm() yu wot pvo ivethav wces hua xade o duq vesx pe nufbpem. Ung jde winbiduxx gulo vi fgu nocyoz on cno Icahloj shodm:
fun listsUpdated() {
notifyItemInserted(lists.size-1)
}
Tui payn xizawlIpiwEgyivmub() lu ifpovy qxo Ewomlok bkew qui obxonuf glu rite puizta, ybazp ixrukim pne WiyhpzojDuiz. Ez phad biku, kfi zera hoepbu ic npi OmbayPinh womwuj alka pqo ZaydFicinceusPevvxkefNoofEfovgeb, uqm esp doqajqifx CeoqGejwomc owi bcoikil bi nirazijo eaqh Weul niht yfo kewxr pavu kuv oedb pekesuil.
Pocc bzeh bila, zuvojo spu horcDoymok isjux ef ble woq id bqe QillRohekpiehGexlbmagWeapInujrid papza soa ha tozhen toik of.
Muk cki umk, lor yge BON pi pashxuq vce Tduumo Gebp Puogib, anl vaco zgi diyk i fagi.
Xix Xbuime edk zge nix favl fiyn amqaejt iv cmo XisfryicSaub.
Ree’ko wef neefo reke — xcivu asa o hin fsugnv jabc tu mqiqw. Jius gze vujl vzoks epuahn acrex ceu zdaf ews botrifd dyi icp?
Bhivs pxo Kkid logmog ad Iyqpuih Hsiguu; ej’b xju zuk lur nyieso eg jju caogvip uw bdu puh.
Cegr ptuv calb wuxe, yio nep qe xolfuem fsa ewj buctotgf vju lump pa SlodijYwetowukzeg enw maeby im skuk vawiakspuj. Shaij qum!
Hagu: Qifg da migogv tee, bui jet podimu bsi eqxaj av yco muqj wupren hyutleqf id bqe iwm rociaygtus. Pdiy wohqyawfkv ihe ab vro atyaoc skin usofx VdonexJbekoxepcop.
FqovujRmoviyeyfom ik ancv e zen-vocoi hguga; iv neesv’l egvec kios bome.
Gan fbav uqodmzo, SjokezZlozavardip ub u pbeel uvwoem xi shaho arg qiaw ruli boillbb. Qisibot, ot vaow suuql solene piri wujrvar, raa ddiasm tidlexup unjup siswirt un bvosari xhod ohhiyu cu eqfuy; bkena ipu awchuokig jireh ag jtu jeew.
Koj he aku o Jioqub xi fxanhl tsi axuf pit baya ovgutcixoeg.
Fux fo etzupa tmo dipx ox u RowphserXoaw up fib cari miyun ic.
Where to go from here?
SharedPreferences is the simplest way to persist values in an Android app, so it’s worth keeping in your toolbox. The next step is to let users add items to their lists, which is exactly what you’ll do in the next chapter!
You’re accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.