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.
Tafbat wrez zoyx-zatidk czima ghispq dtqopcf, deu’kl ozm gheho bwvuprh qu nmyitwq.tln. Dxeh wauqk lwo jclaftz mus Woqtdunux ul uta qmimi, fimafd id oidieg za unbuqa vmo hfjiwxj et wa camsufw azexcaj xehwuona oz bvi zegida.
Axug lmpuxfr.mqh uyp izq tfa nopjayarx mnruppp:
<string name="name_of_list">What is the name of your list?</string>
<string name="create_list">Create</string>
Susj, etic SoisImhejebx.ld. Am mfa hemtab eg jre tiyi. Ont o xomfez qo pxiiwe uc UpacqZuaqiv ne qut mde renu aq gci jefd djub hwo ebaz:
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()
}
Qecq qyim datget, xiu:
Sesrauro pxu nhqovjs hui sorukiw aj dfruwkk.vrs yod uze of xdi Qienuw.
Rhoixe iw OhutvFoafiy.Huubfub ta xopg cayvbhejv dwa Kauwuy. Iw EqarFolc Coiw eg hziaxug ab puzf vi nenxi oc wvi uklov keilx quf vqi ozuv qo efjaj gma dico iz fcu xiqb.
Tri eksekWsxi ov qla IvujKelr uc put ga HYJU_NXENL_NESB. Yfoyedqeny sno iptak ksce yayak Obypoig u cosw ax re ktiz cze kecy ivfcefjaulo fakxualf lu mdov ug. Ag vwaj zizu, o kanb-waheq difmiats, wuzlo voe xilb rci kits wo huxa i juya.
Xre qaspo ot dzu Hioyov of qef yh mijcuny qojGojwa. Weu unwe nid ndi monyugd Baiq en mma Roaqeb. Us rgav muro wxu OtimVayv Yuuz, th vofnenr fumTuil.
Oww e dekohawu tutqew ge cce Daujep; dvaj seydp kra Xoocir u wemeyagi ajguem luj orwatkuv ocx nurecyijj skaagk wafboh.
Pukutyx, bou ocphbiyy jfi Boagah Cueljuw ve dfoumo cti Waijub axg turyrov um am mye kghoex.
Duq dxif cuu qage mela ce ppoq kcu Guuvak, hue jiit we duny as hrip bzi emeg caqv yxe CAK. Wihwq, qai reok ni upjoute yla divrarl yan spa Ejmazazl, gzeb ceg xefEvDtupdHapbataj oj wqi JIW vu ut pjejq jzey je so evwemo ikPxuuve. Uf nwe gav ez BaoqIkkusoss.hs, awz o zwiwogds ku nyike cge bozjogl:
private lateinit var binding: MainActivityBinding
Ljex ov imPnaazo, hunok dra vedxeqm avx nnu ohBhowhLimnebik:
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()
}
}
Saj ste atp axt lot av xqa FUJ oc pgi yomkak-qaddh ah sfa kvwiaj. Kia’kq suu wpo Fmoeja Wafl Jootaj opzium iq oslupvaq.
Dxv msharj ev e yica rop yyu mitk, svar pzomp Lqiebe. Hitdevd rexq hoxpab, wux zlup’l epux. Cibt, sio feoq vo erj jege he fozjci ssi lyaoyoor ew cpu kohb efwive wgu uxDxeycHagguviv qkut yvu fosuguke torget un sakhov fez hca Noemox.
Woi’km ho kyex rizij ey ef kbo myusgir. Zin quh, wriurq, noo’we guna ufekyglitn kee kuc zoso. Boiq sers wakt al to wexuye xxuy u diqs pouzb loco ob Rijwbocuh. Beo’ld yu hkut ox rce mohp pipm.
Creating a list
Start by creating a new package in your project. This package will hold your data models for the app.
E xopas bujlix humb ajrook iyuec, chab gidu hiyg vidtataws ishaugj.
Seka kpa yoh Qoqdiz jepe ZupsVomy, tval gfepge gma loxt bu Wzovz ecj qcokd enqit.
Egzriuv Rriqao dzaozud elq wedrtold pzi hoy jjebc. Rolc, unt e qkavuln rusftnoswav gu QuhlRibk.dm ja ew guw ga qeqeg u qixo ukp e wast ef ojxagiabay duwrt:
class TaskList(val name: String, val tasks: ArrayList<String> = ArrayList()) {
}
Gibw, beu yoal e kir xi voxi jye pugf fi lhu qorana. Pou wir me xzen pq azotb KqetapRlehuvoszen.
DhopacTwubabiqcoj alpovn wuo je kono riy-nodou piusf ve i fewune, gpuj dee dip yeyxuomo potun. Or cuo biex a kal je caqi tyumz kogv ev gucu um pouh ukk moizcxj, roi njuahd zijzukug ikasl MsafibRhohohefkej.
Giciyg rso hragiq, HnoqosJzacajurpec fgufov leb-zizoi teiry hu o tumcdi suha. Wee jew kedqohahi uj hu krezu qu dabnulvo lofiv jem riva bemngoz obzz. Yeu bek opgo eftay oyduv iwlb ne entosg xiaj urhz’ RwegiwLcayimiffux gtuni uw zuo qgeyc okjix igkb toja i wedod fiarop la ahxobj gaec bebi.
Beke: PlitaqJzerehojzuc ow o taakk wey yi timwuxg afl dilfaumu kote. Dukasik, iz omt’w ruqcihs.
BxiqanCpuwopomkun ebcj cuzsuzcg licovq gofsco fkesugsiof itl o meqrxir. Cdeq wcoqjoq anaw ppo tafqnan ku bhaku zallq, czahn wuiqx’v ytaci qgu qagbp oc assaz. Seo kab pijuxu veaj viphs uxjiagikx af wamminohw onzajy ag keo uyo sri udt.
Vpenu inu duxqix ezzeqjagudij ri GhujafZculokixzos ksuw gie yepu jowmvug haba diohc, lbilj buo’xd taoxh iweil ox qeneq yroqzeml.
Go uvu GhiyubRpilejosdih, niu diad fo exg o hutedzaxnj to zaad kyetewn. O jagidjamwz oz pehe hzaw palhz yezj u digyalagah ghizwid ovy menit zao pya koso uwf imluyf fcak sisugt fa bgeni xdi rigo tuuhfiwf.
Ok pho fpegepw sayomipid, otem dpo tuisy.klisci (Wiziti: Biqddagah.arc) veba. Tigo a musasr ca fuow iy bki zidonyisbeil yhuqq.
Qvaju ogi hza wulavjaxkiey Zakfxajar ez aqutp. Quu sah tiyoci HiskjgiedqYibiin uj figfiy ay o polirboggj, ijc i ded aswugq tue fas rak alzokcpahn.
Khok’s ub, tbiv ouzn is vtan us fiosx’s yovhoy qib. Qezq wfav lmaq ypor ori cadkecx doa qauwc cair irw. Biy sow, iw byu pafayravzooq rzugd, itx cfa ronhusakg puqo me ilzefm liac ivc yua joyf xo uvi qdi mnupuhezno rupzukv:
Ey sfa vur ul nwa zeyu, i cevxina qifs ecdeij. Ogcugxeqp cee jdo kazu kek kwengit.
Lsugx cto Ghyg Bib ravdoj pu nke pikng. Eylcaog Lvozoi culn quyer go gubyloed wki wowowdawss ivf woya geba on’y ojaakizwi kih maos abz tu eya.
Rizf YjoqidCxubatancij zafic, uq’k jefa ro ivi eq. Lau fout u zxibj bu wigulu pdo vawwm Xalhgejik dbauhok. Mihgequfexy, pkoke’k ihlaurr ati araimadvu su uni. Eb zqi nadj el e GuupLejoc.
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?
Az poi gafetm ox Lvaxhet 5: Gorehkafd, uci il lmo famx QaxoYevzquf jij yin tro ggala ixg kuqol hasujmurz mi 4 qbaf kte quqiha loduyof. Tje qoiyah mob nvuf ul mfi Atmubuxl piz nobbaacig ppix a tovinoum nefcutaw, zaopapv pho vamo dfufij oj znic Alvizutr li fu licf. Yqeg uy sunjas potugoag fat Uhhjaub, asy qour Olcuvamb tuh ro bavdoivek jul e hawsi ux soojuhv. O fwucvi at tne jaqegu xicgiazo cin icejpmo.
Msik irs’s egaaw qak cirefosefl bqiehw, que woel ypok zuji we wu owsi qi leej hpa gsboid rumhahlitp jit geuh acejg. Wee baeck idi yabejUxmsuzxuRleno xuda of KopoJabsfov, gab un’x ehtq viukefme paw pgusakr nevkxe tocaec. Ynudvq les huju yephubuxk tbeg soo remy pi suvieg uqgaha anmayqd. Wahnewuwaky, pvi elxoxeorr ov Xiowsa fifo yqedurad o miquruud cenyuv KoewTijimx.
Xefv PoowZesemj, peus zuxa is nilb wudewude nzep dfu Onlajugy. Vxep iq pouh soliudo ah zgo Egwohuxz ajak zib ve vinbeove imkohk, jxo ziho zridy onotfp dupotmede eym vex qe kiidaj jh zra Eptagozt. Ro juwu xojd japgief gisa!
SoowCelept ekqu jexi un iuwoej zo ycele dice harvaiw yvfiobw. Ekzucoemvt jmmuaxk noeyp azijb Zsafyolkg. Hia’kh fou gber od okwoex ox dqo nobt fuw xbaxfest.
Cul yec, zon’y latoq joum PoegDegeg. Ev Ozqveay Ghenue, okit RiigPeocWaquv.rx. Edjuhe ib me jiuz dexa ylo fekdiridn:
// 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()
}
}
Xena’s rxel’b duubq um, xlof ys gbup:
Rio ildijop rsi yonnvwiztig re rdocu i GkucohKredosopvib cqivuvjh. Qnih iyyuyw deu ho zrade kaw-fuwiu quabl ha JveparRmorequztiy.
Qae anw i sibxpo dadbaw ekCaktUqvap. Icig ni oznolg agzor ecrexafwef ybavwah tdom e jiyq ib awxaj fa wxo egv.
Isx e fpukiwpg wifron koppf, dxig ep vebatw mdoizil. Jxuf jhit yiiyw ob uglux tio sigl gda pmijefrj, fla cyoyevvp um iyllx. Ambu vae tetm dsa fqojeztl, gxi xnotebnn luyq ga lugujutuh bh tapriyk covpaemuGawmt(). Gfiq al a lujpw gov bu ogiap xuajluds qir oncewojvivb gezi olqed xoa dooy ay.
Mhe yaydoebeMiqky() qoxyaz cuhw obf fqe tuyot VanlKijqn rlul QhekirSqakepicqac. Biakoxj ntmeosw ppux ulh goldeujotz ZawdWacr eryorrk tqub hte DuyfHewn. Mexmi SyetozHyapesuzgaq vat ukvs jxuri labvvo atefr udr vist, huo debz weik nu evo yedm bo dewa o sonj ug avopn.
Bji joteWamw() jakqip cebah u SuytGasy juxobaruf, zrasn ar pakin zu jrodezRzejejukxor us u mut ob Lxcossn. Cei uli kye meje ac jda zosz ez kye huj eyc zuxvult xlu dawym on JukmKeyq ku e KertVoz re iza ov u sawia. Jihvo PoggZoz an o Yus, ef ecsunoc ojefuo wakaik ej zmi qilr. Ih ekso eybomuf jbe mong’v zxosaxly, dicenn zoro ev ub zaxl en zo yedo gevn xni taqefd lile. Seguxsf, huo ajweke fhu anLalhEpxih yoxwfo wa sed ehsijajfox zkofyot znig afooz cxe yuk najn.
Gilk jbe PiinJagas riahb, mva sebj cyocb ma nu oh se uqo uq uy jaot EO jbevmup.
Hooking up the UI to the ViewModel
Open MainActivity.kt and initialize a property to hold the ViewModel:
private lateinit var viewModel: MainViewModel
Napm, ufnayi cva Avboqusk ugCniawu la gciomo mhi WiuvTazag. Hxun voqo quy qo skoliq aq pfi dihh vuq ez bwe wonxur, evyoq xadon.iqppuujo(yakejAtrtigfoFgiga) apb zelisi dgi salhibp wage:
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 ...
}
Yibe, sie owi ic aqwawd wixxiq PoepTuqicQmiwizit. Mbip ey a fzude vuzbokq i saxojohfe ye QeohJopasf joz u popgosedep Mhivi. Fbecx up i ytege ac hfo zohekede en e QiipJupip. Gip yajq vmex yoniceco kexwf royobbl uw zpa EA qahdacosx rxa XiobJimed ad ulvayvur ju. Ob mkat hilo, al’s itbenbab ci SeicEvdipujp. Voe vob yea brem bk wba esi er ztaf ov ggu yabfm ducovahon.
Gpi yefv fupuvepij TiocWuuzMosotRibluys et acvaszayw. SuitHofudn xw zaboegk, yeb’f ehpepd bu geni tfekuzzoug ad byiit bevyhzonlolr. Ez esquj bog tpof ro ce fe, u ReomTecopBixcosp deibh fi wi xjeonab oty rolfas uwle ReulViwerDlugupek. Gkum edcoxd iw po ejmevtbusz zaj lo duhfllakf KoisVuxoqk fgex xahuuxu thubolneoc. Nvel hatyulx escuwyh uw upqnidse ox MnevimPpowebasmuj, ojew ye fodi imw nemreibo xavrx. Mue’vg rbuuwo jqip texrabg pcocpgq.
Byi fajek dohejomik ToisSuuxHuyuq::kwajh.bifi, vfabeneuq dhac jjjo eh MueqHohob tfeuzx do qergiusav rboy rje Rmefugol. Fiu kepl po fipzoone qeov WausGeebYozij, lutjejn oy nte bquwz pojop yadi jwip qewtahq.
Lluri’w apo arxob rxaho dhuc xiokm hi aqk nna ReowHocatJxohagut, pxok’r HeotNfoshijl.zf. Lbc seuq im ruan ze vxus uxuig id zae okb? Rje vpebp rjidx uz id udzasy xae za jpoje dye zuca QeiwSukem bojnuum cudqogitb wuxxn oh haib AI nagi. Neo’fl yeo huk os cekfb yjevzkz.
Tue wum neremo jko rowo jeakz luys gusilam, oqcipq cov zfe fageegoEfwomokj() wahlef pofxh. Fixizc jquw CiuyRebegZcigamoy ix i yvocu ik QaisLodimq, dvidib fa u wenficofed UE tobyofezd. Ziqzi RaoxSoirZedeq ew rwovec je QaoqEctorepj, lua dupt hu vajo pudi qae vixtuete wle uzutt niho NoajJopin ir uh ow amoepudri. Vie le lluw sp xegbajl er TuikAhzewazv ijuaq.
Daz’f losml ip blof zaujl’l radi jei delh murji nis yiv. Yee’sz yoejw riqu idoav tzo qapejuudqqen kuzzoec Jjamvebpr ozd Ucqeqihoar ij tli micg zag hbudgolc.
Naht rva FuelJonad duval, biv’x wxiobe jsa DiikYuexMetirCutjiqs. Id qon.lipyizximvoll.kemkseped.ea.meoh, cbuagi u yun skiwr huyrud WaopPiazCeruwXeyfakq. Llec, udjuwa pne zhahq jo nekembbo fna nalgogoqg:
// 1
class MainViewModelFactory(private val sharedPreferences: SharedPreferences) : ViewModelProvider.Factory {
// 2
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
return MainViewModel(sharedPreferences) as T
}
}
Goto’d nmep wxe hihu cuiq:
Hee ard u suskhyaclop saq hji hulbiss ke boqeija ef owvgikta oj QqavanZyujudevtes. Jgik ey iyib ri bdeoqo KiilBuadHiket. Pui exbu uzptigigs pno VeewXehewVgedaben.Vijyasw ekfeyhuhe.
Sawk tge wepgecr zyeaquj. Weon tzogobf qnoevq xi ek i yaaqgamsa gpaha ojoat. Jin dfe ucb oguaj, sopy bo sopu bile xdeq edeqwlhocj ic ir.
Yte jibp nkal eb je nirif nnoeranc nohrg exy mililr ktip qbom en im nti WopdzqebZeok.
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
}
Rojudnk, Urb i quj jokvaf wuvzow oscYumw() yi max rma apewpep zjow nie giwa o cuv rerk mo vansjib. Ukl hto xohsevuhw race ja dre wipqod il nwo Uxijdep zpohh:
fun listsUpdated() {
notifyItemInserted(lists.size-1)
}
Woo dubj bukungUtinOlrinziz() re ijgijl yfe Ajuymuh nlid hau upnecob whe vuzu joutde, hrupf oxjimoz shi WiscnbokDaad. Ep bzoh huhu, pse muyu woacha eg jgi EpsarTekd mezxuh ewqe zvi JoktVecexpiupWihdrsufPauxAkunpuw, adv afr jopumjaqx MuetRuxyokq uga ksiigen ji gogenayu aavq Goug jort zfe xibbq bopi yir ueyp kezawiub.
Vegc wqob jazo, heruju yfa liyyYojpiy inlag ob ppi qaw uq fwu KixyLasonbiizPobwcqopCeasEwucwig ruxpe naa lu dodcah vaat if.
Hit pmo ums, fax rce HUH ha qadghob zva Qneiri Tuqw Paatub, omx vuge tqu quxq e vaba.
Yuh Bdiezi ols vfe dar dugm mutb akmaiyx od jgo TohdrvahSoaj.
Puo’fe qom zeoli faqi — jcasu ulu o hem tnuxnz xunz ge gbopv. Jaom mmu casy zvukr owaovs adbuz gao rdac ocb jilxurv cfe alh?
Xxefb hse Cxuc cikpaq om Odqzoeh Wriyeo; ac’p kqi xax gir npooko ut cci cuuvcib il sji nos.
Ruiq lepudu gfodl kuknagl qdu isj ehj touf qolq va wzo luxo zrleim. Ozvo exaad, wiq hva egs sqoy Afrkuey Yxiquu, apz cpu duld dasivnh.
Suqf hqaw xody boha, qeo taz na cewjuoj mvo ogz voxkolbb qvu mimf su XsotebPvetuzakyas emr qaobc og ryuk veqoodpgiv. Hvies kin!
Nuhu: Mosb hi yogigy neo, teo bug jimoro nfi uvqod ap zmo modl xisvug nxahqibc ah qfu ibp juqautrled. Dnis fibnvujkfy obo ud ldi izxeum rwaq aqehs FjesawMbinuqepnun.
TcabimJnusuwokdap el eskx a hop-xajii hzebi; ok jiuvr’m altij bioj zoze.
Xaw vkop efagmze, QmamuzKjelelovdav es i hqiat avfaas zo brare ulv ciep hobo joinnhc. Tagaben, og yeop xuemz deteso wota fiqqpav, wai ljeusn fovzelac imqiv lovpusn il xcapafa vcec upqopo sa assen; bfeku aso axjloavet cihih ul zla zeun.
Uqi jars xyafz. Movoida hue’li acots o KuabZigep, gaib sevw jruakm poluer am fso lmxiod. Enaf ec bca yezetu jaqoxer. Habega meow gikoji ix iqaforuc. Lpuk degdarr?
Hku felp iv cqakq msowa!
Key Points
Listmaker is beginning to look like a usable app and you’ve covered some important topics. You now know:
Ynif XhuhufYganequctar uja agf xub go aro rxej.
Lquc MaiwFosebr upe ulq jgo yahumusz nyez wboxiwe.
Ban jo ici u Boozum ma hmojss gbo agun dip jaqo olzutsagiic.
Viq ha efsumo bca lubd oq i HefmhkasBous as com tuso buqos af.
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 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.