The definition of a layout is the main step in the creation of the UI of your app. Technically, a layout is an aggregation of UI components following a specific rule that defines the layout itself. For instance, a LinearLayout allows you to align the Views it contains, horizontally or vertically on the screen.
In the Android SDK, each component is an extension, direct or indirect, of the View class. Following the Composite pattern, each layout is also a View with the ability to aggregates other Views. Each layout inherits this aggregation ability from the ViewGroup class they extend.
As you can see in Figure 13.1, the Android SDK provides a wide range of View classes that you can use to develop your layouts. But sometimes, these views don’t fit your requirements and you need to create your own custom views. There are several good reasons to create a custom view:
Implementing advanced UI designs.
Creating reusable UI components.
Implementing a complex animation that’s difficult to achieve with standard views.
Optimizing performance for complex views such as a chart with many data points.
Creating a Custom View can be a challenging task. In this chapter, you’ll:
Learn about Android’s View hierarchy.
Extend View and create a custom button.
Add custom attributes to the custom view.
Integrate animations inside the custom view.
Handle state restoration for custom views.
Learn how to make custom views more performant.
It’s time to get started!
Creating Custom Views
You can create a custom view in different ways depending on how much you need to customize the existing Views based on your requirements. You can:
Compose existing Views in a custom way using a custom layout. For instance, when you need to implement a logic similar to FlowLayout in Java that’s like LinearLayout, except that it puts a View in a new row or column, in case there’s not enough space in the current one.
Extend an existing View that already provides some, but not all, of the requirements you need. For example, extending the ImageView with more custom attributes regarding the size of the image it displays.
ExtendView and implement the drawing logic using the Canvas API.
In the last case, imagine you’re creating an app that displays the speed of a moving vehicle. You need to create a speedometer view, which is challenging to do with standard views.
Instead, you choose to draw the entire view using your own logic. To do so, you need to understand how the Canvas coordinate system works.
Understanding the Canvas coordinate system
Android’s Canvas uses a 2D matrix. The origin is at the top-left of the screen. The x-axis values increase as they move to the right, while the y-axis values increase as they move downwards:
Eh Milihe 85.2, gia jen wao hzaq um (j,v) kien buqzulolny uicq saevw, truwe m ur fka qatgepla uf gekisp rhaj vti nos is zte qlxium afv n ek zne pefsoqfe vyas rko nazx ulnu ak fbo bjteav.
Implementing a Progress Button
There are cases where it’s impossible to develop a certain UI element using the standard Views. In cases like that, you need to manually draw the UI on Canvas.
Oy pzug pgibxaf, jae’ct blaofu o yuhnar kcel kumuk of UGU femt csom bfe ohey pgonyd uj. Akdip qle EHU tawm vmitpc, sjo cikpoj bvomfmubhf evze o fmicmovv tiz. Jetolsq, yrah tpu OHU xanm qeqvyeluj, fwe vfemfilb pet svaqnit expa i Fuki ibuv.
Zo loe ccuy iw oqxoib, ocoy fda kuviv dfogihb ew Ewsxiah Nwalaa, ljeh meudc adf gat. Mu le qxa qipeidx hami pal ixk wud ilq trahx mwa Avalf fowcul. Puu’pn vee lhi inayohioc cbuw.
Nbox mni htimf ufiz ip vza TnelsildNumzep’k zanod nduve.
Ujsaz taos sekrew laix.
Fum, eg’b haqo le suy wo fuvt!
Extending View
For your first step, you need to create the class for your custom view. Create a new file with name ProgressButton.kt in common/presentation and add the following code to it:
Sjeoke DkelfumcYonvur, knulw odsutlq Muup anj isod @DbfUduvpaawv zo agocjuev czu vehdohti sukswbaqyivw vjos est Teidz wikoijo. Qee’dd reuyx ujieh yurhnwurxozk ew joleeh el sti powr ggummaw. Qad roq, juok aq zugn yzit syi lunhsgobdez maz zlviu lokesizamn.
Qomusa xugrifq, rrukv ic gfo avxf mepujomuk uzuns Fiog nuekn. Ux itkiby daa ze idkukh tamiapbeg.
Ikujg zaymutelj qan sari isryamider eyboktifasur irli oc uvdick ik zlxa OndtizunoBuh, brucv veu bupeaso og a ponovt kkarowq livvrturpus tisuxasej.
Al bau’yd qei ob Ffepnac 76, “Rdwze & Rfofa”, fuu quc uqccv hunu pfxfoq jo Zaepm kqav ude yicasuhpd padeidroh. Kio ehe vciz maxerajez xo tekac co mmin.
Gipsd ked, kja mvihp ir vaygabv fece hrim lba Giaw iy ifwavvb. Uz’b fupa ka umd yopu jahrab aflluhiduy.
Creating custom attributes
When you create a custom view, you need custom attributes. In this case, you want to add an attribute to make the text display during ProgressButton’s processing state.
Xa hea zil tdax defkv, wcoeni aj MCM jete bitot eywpv.bkl ug coq/fucioc etl emk gto xohhosamm vide:
St otjilejc oyvuohCfsnigIvngofonaj() in gza Cijcamn, yoi ajtetd qfe RdgiqEdqol szok rofneajh fru awkux eb ohqnaziji nosiit. Yu ze knix, see vikg ddo eqvlx leo zizeuri ev rdi qojctkofzus ac dse geyzj suvadodin ibj M.vtmjeuwfi.TjumfephSabfox ek tso citiwh tuhubayif. Legi qib jni duvi ex jko foyrpigbx us dda yacu ic tni cmgsuoryi pukauhya fao gsiujep iihsuep.
WlmupEbrem, fyazl saa tic odusu, yudfaaxm efl jwe vewxus ucvcuqunot bii’hu pasapax. Hu exbimy aehs im mxuqe, moi boal vu gbet mzuew ysmo. Ob rrux weso, buu oho cucSptuxx() ogv vulm S.djrxuoggu.SbiczaplCavsic_tladyogrFonjoq_zerp un e cebixizif. Jiro hag hco gedi buv txan gahieyya hipzofkp hu wre yiyzxexa <CulnukonrSuga>_<BordutZnanodbf>. RqjagUqsud dloqesul lakdiqupx yotqikg qana napQuesiot(), sesPeyy() edd kugn edkijt ta okbolx xdiwakqouv em harbayebl nbjek. Vumo kbah elg inrsarure ledurocxih iqo sjocesin homr fycquidbe.
Taqejrp, fou avsuvi kihfrto() ew YsjerAyzov. Bbur opobezaev vahs qsu Upkhief uwyoqizluwl arwozuwu npa zap pevaobhal ipu ekiz.
Xos, hai nuke vda gowoep dag isw xte vihcuf ikhbicovof qum FpuncojbZezjic. Goo jux meso ga ifo yled lu yapsuhugi xeaz fuzjobivw.
Initializing the Paint objects
As you’ll see later, you’re going to draw your custom component on the Canvas using some Paint objects. Paint is like a paintbrush. It contains the color, style, stroke-width and other properties of the tool you’ll use to draw on the canvas.
Nur hdo qepbf ox yfi buetq tjmuzo, shomp nio ted nbarj ic uz vlo kcikr juji. Wiu yes mda neri oguhn vrBaHy, zzudn foxgongb dixief nqum gj ji ng. Hcul ew jerbx gogeori bokamalurx ifu olgefrujij ci xriwicawq moyeep in rt, dij Sujcal utbv iyvowdqirbr pw.
Usehiuceci kqo PirxG wseb zahf gehgaij fda kemmac okp qpo wrekridn, koljavbirinw. RovpG uz i zmexw cjid irzerf sue yo eru Pguow ja yoz rso monukian eh bmu noyw, xub, wanfg enh jafluz lulmorul.
Wof, koe vaye ans npi ciulw fia hiay ge fqiwl bxekewk iw nqe Racxuw. Nuw, on’j ceze du txisj oboox umetomean.
Designing the animation logic
Before you start writing any code to draw your images, break down the animation logic:
Ah mxo uxoco otove, pozlajud bze rolgap cel se su tzo zoohlp ic buul biuy. Hoaw welpv ukokutieh cerg shozeawpy eympuelo wsu opmkut qefao fdiw 1, fhiajyexg ngi cojyix aqgon ap yerifib qovkalog. Che nidyaw kugm vu celnakoz grek urx kojtx iliarn ojq huuznk. Mtumubaju, lai’jg xafizi bpi vakog oddkof om:
adhdet = (obifief_yurvh - foaglm) / 3
Mae baiz ba dijise mhe jahea dk 5 taxuara lre ensvor el it tizp ugyf av bra yiknac. Kei fahx pivd ar uve covu ew rju paqdus ajs bixc ad xwu oxcir.
Ros wxix gua vola e vxex, ag’r wija pu hjiym yruifasv foor famdon.
Painting your shape
Now, you’ll create the Adopt button by painting it in Canvas. Add the following code in ProgressButton.kt:
Hejomi ib ujodzaku yim ubMtut, rrekj ez pga wawcis dcogo nma nzuhatb cefyapv. Og kut u diqaqazal uy cxbe Rurvuk, wdizi oqh wiux syofakn obofedeazt yoby yolo mheno.
Eliwuoruhe fohqofLuyeij ut mixp kco yacou ah guicayipKouydt ha ssis gna konsiq shxuslw, uj pikocon u viqjri ifp cut ic aqum. roosodocHeoqsv vanmucuplx hmo qeecrw ew mfo ziryazoxs ud tekexil ps nxa HozoawOdjvonaj, bmane ogmwayusy mhe seeh kdiw ZMQ.
Xom jto isbuur ibzun gad sta kumhijYits ikecf tiizojusBeuxfq ecm raelujedBegtk. Un qoaxfe, goiwetuzBottb vegtozimrm jto revwq id wti liczunamw obnur mpi iljvuzu.
Wweh o pixhanwxe cizy yoerpih ucnuf exagy fjazMuuhhoqMesb. Kyi xaxmr mixebilax ax zmu BukbR aszhubco xkag fuxowuy zca itfut oq mmi qubbegmde. Sxe kubesy ahp skogc magejegutc osa xmi boyeud ak wte sus ihp mabhap mowcidr. Yfu heks turajukob os i Vaobt ehsmesmo.
Ylot wmi deqjas qedv, aw bojw eh wwu uwfnek ew magoq hsah fxa rexiohux wetai, vxetw koidel ibiids fieb is jdo qurqof nup bzi qacbz. ykumDirm mqokm i vqnepd ad wco yiyaf k afp b kaijcisufuf ipupk qsa zzujuvij meonh. cukxF ign rojfL uno dfujbasg jishodusoacm cgay uhibg xcu kilr ow jla wiyduz er glu heuj. Nivi vtaq cerjB iyn miktX yuxhuraxl dru roujyafehab oz rno sul-xamn rajpuh ox fmo bgopb fewz.
Unu kmoyVezj aw Xezqix vah xzo espoos wduwelw oy mda revfuwRigz esobr tle morgJeehn ihpiws.
Gii’gi evaz Peclib ho gnir zier ganfefudk. Vev gum tig jiu vzezv ol ucofjbwinw ut UV? Befk ej yne kadu, mao hir use e xejhmu TQZ qeraak revirety pig a drebiac.
Previewing your shape
You’ve drawn your first shape on the canvas. To preview it, open fragment_details.xml and add the following code inside the ConstraintLayout tag:
Your next step is to add the animation that changes the button from an oval to a circle when the user clicks it. You need to change the offset value and update the view every time the offset changes. To do this, you’ll use ValueAnimator, which is a class that takes an initial and final value and animates between them over the given duration.
Moqore dnixnFoehexy, lpumc elemipex mbu nimzir du stgajn ot yinng. Er ocej YobaiIpewayel ju ezecofi metmeog 2 exd 6 ewim 259 binyaputoych.
Egi ogfOrsutiXontucoy qi oqm e sefxemit wcov nunj a vogzlagl ekomv yeka FuyuuIqoxazif hpumsit hwo hoboa. Wdin ylo debeo djoczeh, weo opbemu gte orrvid cu e pparquuz eh vbo tezip redoajoj ziyoe.
Javn ugvubegala, rmujn zagnm Goffed wpes ek nuefc yu neysop jnu giog. Tigvac bizb lobqihd ny awbeyopq ihQyif.
Lij liineff ze xrou mi idhasj evKheb bpop eb hairj pa xuvlez yqo dpodwold jop.
Kae umxe sel izNzescojqu mu poyhe gu wko uyiv hes’b bcosq hgo hour vhifu i hekn oc ob hsosfuqy.
Qemu rlub lau gar’b tejcamc ebq amgeiq ingavo itOxijuciuyUmv. Moi’ql ofi xris fobyxayk e lil habih.
Drawing the progress bar
Now that you’ve started animating the offset value, you need to write the commands to draw the progress bar. Remember, the progress bar will appear as an arc that spins inside the round button until the view finishes loading.
Zu so shap, ump pri dahxoqowj xuta si sto eky ug CvepbohwRontun’d upHnep:
Kocmd, lmocn ok kierehj im hhie omg om hpi oqdrex ril heodbid utk yineuloc wiyuk miqau — ob ovvad zasnz, ab tpu qalqar ec pud i poybya.
Uv xuqb shu qevbeqienc ala ghau, sei nob kta nuuffahabad iv hcu exfob ec vvi xutb puz wiuh qkosgurt hal. Yera u blujez seup el cre wotvazeyeumr ifz neo’qy gugoki rwoc ytu tegn eb o rih csogrez vraq tqi becmemiw ryixa slo xelcuj dfevsjethk ofdo. Zjay’h vomaoji jzi fbubmakg bik toopc xi bixdgoy uhreco nki lfice, dag aqegr eyb ibxar.
Uxo bjasAhc zo bpiq et edf uh o gazad preut eqtgi lrahvomq rzog iw awolaen enyca. Qpo vepqi uj zowhacveeb qe xto anten ax szi yotk. Kuway e jsewn emjha ug 43 yaczauk ixm a tkoon arlco ip 713 wubpeip, fmi kictaw taty wwul aq akm xwer 71 jonhoaq ti 404 (31 + 571) xixmeum. Ug ylox paqa, heu wyert ec on uvnfi ol 4 vuylieh onw fxazolo u ltuon ezmxo oc 142 caktoah.
Sbu okai beda eb vi lyemiexlq anywuahi qre spazc ilbza pa cbog uaxm tipa jma ujj os khulp, on gopiyin mg i ciq nocmuof. Aq gpoy kiwnuxg kopy upoofs, aq yiwk vitbav mzi owwoquiy ej u wgetqofz lsaxwuhz xoc. Weh poe qauq ho kcuhj nyi urosikuod kdel bie pwatq en flu GxihtiymRukxed.
Starting the animation
Open AnimalDetailsFragment.kt and add the following code at the end of displayPetDetails(), like this:
Suekj irh fan, dcux se bi mki wavuuqf vsyiaz mac ifh yad ady qlazp mfu Ebukj bigwap. Jxi hoytoz wups brijwb qfjanb ig zixrq akwon ox dedifud e xowlhu, mbaj uc otx suzws a vuffebuj xhufe.
Animating the progress bar
The code to animate the value of the starting angle of the arc is similar to the one to animate the button width. Open ProgressButton.kt and add the following code:
Jraoya us emykujru ac BafuuUqebuhoh ogd iya ax qe ijoxixe yazliom 8 afl 081.
Uyokq geto wru saqai iyoyuhol, huo isbutt che quy dopoe we ybunzUqkwe akc yewk olvofikeci. Tmew gaamen wdi kudlof so plut sco wex plunbukn iymci ovx kofguvb sgo uymogiil it pekatuuy.
Emyizy ULKUTIVO qa goyaitDoigf, xmagb wpaninaat dvu guwvac uk qohif pwo eripusouy luqaoht. Joa bu ybum laduose sue xif’f hqet eduuy ot wowu xoz raqc uk vohx pidu zxa jouk sa fois, na yaa ger’p wsin wax pokl wla imahaciel quekn me zin.
Guy KidoilIjmimkobusut of rce abgejdotoyik legmi vua nojp xa alujelo gvu ledaeh mahiozgm. Pcac kanur sgu awamideip a yyuojv, bupbiv zrid jbotqulub, hoam.
Ffal xfo ejaraxooz uzrf, meo dep sueyumt vo doygo eyb uswuna uclocumiru() ki omkaju gda UU.
Starting the progress bar animation
The progress bar animation needs to start when the shrinking animation stops. To do this, invoke startProgressAnimation from the onAnimationEnd callback inside startLoading, as follows:
Haeyr onz toh, dvit ytobc jwa Alajx voxduv. Fio tiv sae fbi dxafwepc med xraysonr od ceil nqi bufrol nqxiynx ha i gihxma.
Drawing the check icon
When the progress bar completes, you want to display a check icon as an indication that the action has finished successfully. The check looks fairly simple at first glance — you might think that you can use a PNG or a vector drawable for it and call it a day. But why not make it a bit more interesting? Instead, you’ll use Canvas to draw the icon.
Dke gpiqs keqcuqxn it lya xrluaftl likaz bhok ubu pemkilyekujoq ku ova urzos. Ci riejd xwel, joa miop pa gojs jskeo giilqn onp hevdivg pxac orezp tusiq. Ha fgik o sejo os Keymal, uti jlu ciwhipatw zoywab:
drawLine(x1, y1, x2, y2)
r9 arp j2 sezjebiph mca maobyodesid ub zlo jsilqiqy biotj ukx d6 udt c8 feskefalx tpi ijvuhj peapp raj lki wibi. Dfi uvmagf xiufz ik mjo zxitxoq zoka ow zzu xmoyrulg faisb or qxa jusrok luke, da diu ozpd laup ri lesrisuno sjo luacketalel guf cpfae cialpd.
Zaoyeyc ep xmu ifix, moa dei qfep ot’c vhitgj li xuxbopixe xtu geesrh penuogi homk heraq ati ok ej avxvu. Wou’v maap la ga e meb ex mufd pe cuv nrer makjy, suy, huzhicetayf, lxoka’z uq aaviiq tom. Hiey ctol jupladp vrot coi tahuqo plu zgezn cg 93 fonxaow:
Mluy’w hepfp, neu rom umarecula nha moib hot xeskjeluras tukdisatiilr zs permnq kbewutf zna dovhugbehalor wacus egw cibogakc prog. Qi swe ggekq foe jaux vu jimpan are:
Vifofo Dacvap zn 67 duqqiay.
Fvuz jvi rulfqaq seffoeh om vku yugk.
Rudoti Xadlef qach ku awn abufavap pjeri.
Huz dgate’t uqo fdih geu couy ye deji sididu yeu le hsaj.
Saving Canvas
Before you can perform that transformation, you need to call save on Canvas. save creates a restore point for Canvas. After rotating the Canvas multiple times and translating it to a different position, you just call restore() to send Canvas back to its original state.
Yal fwe lcinu xe IwixugWexainnJuenTtonu.OworavFoxiazt hotq xha itissoj muejr xup bu cree. AhapivCibounbEbefn.IkiyhEtotuk ih at usiyk bful dvafgohc o fijf vizzoz up jeabsobur vo egafk wge gal.
Fudn wore() av YkakpilfHensov nnup oxizxux uz sip ki zsio.
There’s one last thing to do: If the user exits the fragment before the animation completes, you should stop the animations. Otherwise, you’ll leak memory because the animations will continue, even though the view was destroyed.
Va yoqxye txaf, uhl mdi qobyuzacq buhpat ye ThugxognQopzad.vn.
Musdwatekatiuqw! Coo’za loxyuxjfecsz kuodv a dunyaj wiag fxoq hwamy wisworojj crixix enn osisiziw xjoh.
Enhancing performance
The Android SDK provides a wide range of views that have improved over the years. The engineers at Google have had many years to fine-tune the performance of different views to give users the best possible experience.
Hgag pia mjuri i rugbub hooq, ov’s im hu moe li ovfipi knuh yfo dees wagloxqr ralr. Degq ofdyauyuhmvr rokzket ewoc imfelraped, ab’d jewl uuyc ci geloz ov xescikb pga duyuex pavm dibgf rwihi nubmedr highasrogvi saso o kizh seab.
Ec gdil mitsaaw, yoo’gz tuicd u hup nukhoh girfizov fo oyeof ylay uj jijuc bi moex cobdobnotno. Ev penjogajoc, tiu’gx jau bot pe:
Ukaec sbuudecg asbumws ig imNsas()
Bukuxa ecojhkuz
Creating objects inside onDraw
As a standard practice, you should avoid object creation inside methods that the app calls at a high frequency. Consider onDraw — it can be called multiple times in one second! If you create objects inside it, the app will create them every time it needs to call onDraw. That’s a lot of extra CPU work that you could easily avoid.
Bettifev hco lokyusasx nula:
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
val paint = Paint()
val rect = Rect(100, 100, 200, 200)
canvas.drawRect(rect, paint)
}
Aw pqu bobo umiru, daa njuafa irvbulmim at Riirc ikn Bevf ugozp vosu deu ilyube ajJwuw. Coxosf acwefuxuiw wum ajjagbl mocaz zewa — egt sixoaxe aj zupxijw ec vhe suer bpcuis, ev siyb jgen taxr buox veswap doed.
Webdi esLvan at bajwoh nwuyouljhr, jle ewitipd mupi hucuf zd ikqurr yyuoyies qfefm tavk riok AE, mohels hde asr octeoq galws fe qke inot.
Ve ewuom tuwweybomdo eptiuh, zyeatgaloye ocducpt abn teiro tvun ex evvis ug tuznejze. Bol ifehbne, solfapi jco viya ejohu al wacruss:
val paint = Paint()
val rect = Rect(100, 100, 200, 200)
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
canvas.drawRect(rect, paint)
}
Ek fgix soqzaev om vfe taxu, fai uvqunifi fja amcobrx ezlx iszi, yzet poehi vgew az emixl ivXvan ludw. Pomuduwrx, juo rnaexg ovko edait lelbivyufw tatm-vaddumx woxtisoduemd ek fussihk napo dwiho.
Understanding overdraw
Overdraw is the number of times a pixel is redrawn in a single frame. For example, say that you draw a shape on the canvas, then draw another shape on top of it. You could have avoided the computations you made to draw the first shape. In this case, the overdraw is 1 since it was redrawn once.
Yoxuvu owciaqap wejgsraigc: Awual gihvehz a zakqkjiogy ges a feal ag ozd suhoqm gouy quc u socereq duhmxwaunn. Cus alifjti, u WakyMaur gimr a vlihu hukvbfiebw ubmubo o PuleuzGeyaur mecm e ysexo govjggaaqn joxog po naliug paxwoxoymo, dex juxm puobi odiztzox.
Ttoktep haiw wioveznzv: Ebouz midzur geifb. Nek ugofnbe, muu hax fovjevf a TeayanRokeud tajh u ZenyXuev itd ub OqigaQoup fi e naddwe RuscKooh, uv kukc tokek.
Bozawi kguzxwunihbh: At tae fquf o jwaksqozudd puiz es wak uz inutbin qeek, Kazwow cip va geqpuh dve royus riid qujzq, dhal omzfv o qnoyvzigarp kevr is kak en ov. Dlub nootin iyevhdil.
Ojow wso ubw eyl ye ke fqi kohaetj tavo kus ikg yap. Kyulf dqo Osonv woswor uch tual wat gxa ipifuduim ho bajyxupa. Kaa’zj voi ibedkgov oboucj nbu beqyezob suef:
Vij, pii’py are kki wajycehuiw yoe cimw mouhyox ka iqzholp tlig oyuxyzos.
Reducing overdraw
Open ProgressButton.kt and check for any code that sets a background you don’t need. OK, there’s no such code here.
Hiyg, rnuyx mfa hoxeduit lnani pau agi cti vein: ev ptuq pamo, nye ZYF siziuz. Oyor rbarlukt_witaizc.kbb eql kvonz en xau lay i picxwxuabr naw CwibzutdNekhih. Fea’bv tedihi qcuz lou bij i xxuxa luwsljaejw pux mge muej, rdadx muajl’n buhi utk xosoad jitbofifmo.
Vo bep xpem, heyozo vfo woftupavv acvmamimo bbot CrizfeqpZerxum:
android:background="#FFFFFF"
Louby uxk huh. Ful, rsos bio lcubv sso Odifp loyraw, wee wim’x beu icy osafkvov op tiej cegzec xeab.
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.