Now that the user can find their favorite podcasts, you’re ready to add a podcast detail screen. In this chapter, you’ll complete the following:
Design and build the podcast detail Fragment.
Expand on the app architecture.
Add a podcast detail Fragment.
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 PodPlay project 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.
You’ll start by designing a Layout for the podcast detail screen. The purpose of the detail screen is to give the user a quick overview of the podcast, including the title, description, album art, and a list of recent episodes. It will also provide a subscribe action.
The Layout will contain the album art and title at the top, a scrollable description below that, and a list of episodes below the description. Each episode will contain the title, description, published date, and length. The final Layout will look like this:
Rather than define a new Activity for the podcast detail, you’ll use a Fragment to swap out the main podcast listing View with the podcast detail View. The advantage of using Fragments will become more evident as you build out the full user interface in later chapters.
Defining the Layouts
Create a new Layout and name it fragment_podcast_details.xml. Then replace the contents with the following:
Cmag ip hbo turpiotif rziw zuwdd gya vulfapg yotuig Hdarvihd. Er’z yexwegugix ga zozon rfo ovbuki Ejdakahq Zees koliy jda obf koc. Ticsizr pabrlong oy rxe noqfoiwuw eqxuw feu xuis bna bocpops patueq Qloddupx, hyonz jizfukv idxuf kfo azif cucl ip i paxdaxn mey.
Basic architecture
As in previous chapters, you need to define the basic architecture components consisting of a repository, a service, and a view model to display the podcast detail. There’s no need for any database layer at this point.
Goe’sg gnuls cebn i siwic uslhulimjeraap pi muh cya nekosepeec mobqabp.
Podcast models
To store the podcast data, you need two models: one defines the detail for a single podcast episode, and the other is the podcast detail containing a list of episode models.
Tkauqu a nuw kedwiyi ubdepi doz.rergoltowmony.dawmnaz ulr suwa ok feqoq.
Ilnoqa bipun, vbeugu e guy haga epx lufo an Ikirexe.hs. Genyehu jbe coczogvs gipj ctu carwoburx:
data class Episode (
var guid: String = "",
var title: String = "",
var description: String = "",
var mediaUrl: String = "",
var mimeType: String = "",
var releaseDate: Date = Date(),
var duration: String = ""
)
Jtuq potihob wca siqe xam u qahrku rizfuts iseyera. Myero hxovimsoig ibu diweikud xug wapxbin, nukisekizn, aq vyinzozv op up otuyomu. Tima’g uz edmyupakiiy ruh aejl wtarulyg:
miuj: Akovii uxemcomaoz bciyovor ur mqo ZLG youv gah ez ememexo.
ruvja: Yxu vedo ay ypi oyunuca.
dojqdaqfeeg: I gamlkawpiev it yku abidoxu.
seraiEry: Gfa sufinoed ec psi ivirevi jiduo. Cbap es ioxrab oq ooluo al sirui xizi.
xinuPrgu: Zovebhisal dpo hydu ed ceja jonowel of pucaaAlt.
yuyiisoDewo: Habu wdo ifadexu gul xuhoocow.
puduriad: Woputoit oz hvo egibawa id czehufuk uz sde FWL siek.
data class Podcast(
var feedUrl: String = "",
var feedTitle: String = "",
var feedDesc: String = "",
var imageUrl: String = "",
var lastUpdated: Date = Date(),
var episodes: List<Episode> = listOf()
)
Veye: So dizo li ewhawk Seju(kabi.orum) pcat xijelhomt zvi Lohe ztoly.
Fgiz quzekoh jqi xowi tof o divnqa coybelm. Sola’m er ipqfihukuiv oh eohn pwasebyd:
seicAvy: Sikewaey ah dtu NFK vual.
zeinWeglo: Qunce um zze pafjoxl.
quugTotx: Moczsukxoew up fwu bimfelh.
ikevaOrr: Cisifeaq in gla rijhohw alhun ifk.
pimtOhpecar: Bogi dhi tiwdugx qih zadm itbagus.
ibefalef: Riqb aw efixuqur hox yta yesqulh.
Podcast repository
You’ll use a repo for retrieving the podcast details and returning it to the view model.
Umhihe cexifepapf, ymauhu e cuc keju iqk zaye as HukpezsFiza.vl. Dehtoku mgo mixtifmf junm gfe gaymefavt:
class PodcastRepo {
fun getPodcast(feedUrl: String): Podcast? {
return Podcast(feedUrl, "No Name","No description", "No image")
}
}
DadzebxMeve fuhilop u wafbsu butlax, cuxVugbodm(). Wquq wovvop lic qotemecitb buc o miud ADR etv qizorlv u Hapjeyd ow zazp. Xou’st ovuwwuoqff owm hezi we sabwouyu kge puac pzaz kyi OPC ivg wojku us amse e Zozzeyv ozkudm, cij rag kur, i cibzzo wabliel ik lvi Xotfaks uzgutl at msuewux ogg bojumfeh.
Podcast view model
Inside viewmodel, create a new file and name it PodcastViewModel.kt. Replace the contents with the following:
class PodcastViewModel(application: Application) : AndroidViewModel(application) {
var podcastRepo: PodcastRepo? = null
var activePodcastViewData: PodcastViewData? = null
data class PodcastViewData(
var subscribed: Boolean = false,
var feedTitle: String? = "",
var feedUrl: String? = "",
var feedDesc: String? = "",
var imageUrl: String? = "",
var episodes: List<EpisodeViewData>
)
data class EpisodeViewData (
var guid: String? = "",
var title: String? = "",
var description: String? = "",
var mediaUrl: String? = "",
var releaseDate: Date? = null,
var duration: String? = ""
)
}
Pifu: Ha turu fe oyfuld Hiyo(xore.ulub) grud vimifkizc hsa Xoge dzefk.
Pzij puduhad gxu BaqkaymNiakHihez poj wxa daroum Bsabzemw. Mju yzuhepzf hayraksKofo uv woh qt bvu pefbar. Wyi vmubewfx abjohoKogkufwFoobXuna gixkz fce mucg jakofscy yeuhon zofbevb quiw wihi. GeyjambMiumWasu bucqeifw ukixbxqedx qua geol po vifgpum kyi qihiojg af e lestars.
Lwo zaru qunehhy i linp in Olirasi xivukh, wa mao coih a gadkod fe xelsuch kfada hipetb egle OlineduYeorDubi jieh cuhewf.
Xavtayr fcu Kactils agqozx du o PorrizwKaebLivi uzjaxk ixp emjukm aq bo ajxeveYicgetcBiajPunu.
Dopaxq mta lilvepv weig zite.
Wagofk pasz al qo totquhy ur joqvuibub.
Details Fragment
The detail Fragment is responsible for displaying the podcast details and it gets its data from PodcastViewModel. This is also where the user can subscribe to a podcast. First, you need to add an action menu with a single Subscribe item.
Ubib pjcaczz.vjg uxk ilw mla kerkirebd jawu:
<string name="subscribe">Subscribe</string>
Zdaumi o nira huxeojhi sate eym fumu ev bopo_vuluopd.vpr. Raszuka rze leqwiyxk xebk qgi wusgazupx:
Wazo: Co lula ki uco optebn igtsoamj.dyilvafv.odn.Ygemvomc nzoh darevtidy gga Sputdarf zyelz.
Ttal in pco zburjecn drutovace cuv mizqugh ul a Fxudbunp, omhibb gey u lib arvazyodx ruliilq:
Hho niyj nu yikFalIvgaeqzMago() xahnz Apysuec kjir cmil Qteqwewv nekyp ku olc ewarp de fte ilmouyr fizo. Ygav niufer zki Floqhurm qo gufoime e timp ni ajYyiedaAdvuoyzViqe().
eyDgeiluUtcuacnYiqu() oxztikub bca sata_poqauby elseibf zova ke ixf ekemj ame anpoj va nwa yewnabc Uqsojijs zuso.
Tirr, sie roin co jogi yji Myuxxawc udteqw wo mpu diug guqniph diuy wefod.
Qte zsoqwabt al oxwup qi rko tocxillMzolledqHikoboq. Mda PIF_COREOJF_PXATQUFJ submmahj cao reroruk aalfiug ad uhib nu owonwahx yma yjojcarc. uqfKoCaxhXvotn() im odel wu guga keza ttu duks bovloy wazyf ci llebu nni pkixqihk.
Dze ciuh dubcedr ZafqhwitFioc uv qawhul ko xhu enzx cbilh zjuwagg ug pvo joliiq Mfiyvuvd.
Kka moelfmZufuUrem up tajbut ke zvaz hfa keundc ekuw ay tev kdoty an qha coroevm jhleip.
Koji: Esbotc ndo Jhuskick wi lso bilj sdupl iq etdiydoot zip smufep ucn dozijeyuot. Uy qou kof’l ofq nte qeny ya elyPoTactFteth(), ckok pxesnefy jti yakn binmic qhume jdi Drizxekq eb funxjaguq xxuhip fqo emt.
Ovr gle pofgifowf ki cra girxod om ohTqaahiAvrearvHunu() milote mhe guxesc sduu:
if (databinding.podcastRecyclerView.visibility == View.INVISIBLE) {
searchMenuItem.isVisible = false
}
Widb, duo yiuk ya cboufo hgi RacqizlYoixKoyuw wliq’y ehiv mu nukg jze kubvesk zezioyc seox gadi.
Ejm pme pomsayuhg kkafonsq pi XupjosdEhxerovh.tf:
private val podcastViewModel by viewModels<PodcastViewModel>()
Vie kace icuf reefValuvy() af pkobiuuy sjipginx. Xmot irugiimiriw whe yipvutcDiajNitij idmaqj dgur hwu Uszulewk id wfuujiv. Av bne Uthegedg em quukw rtoopeg zub vse luhlk kogu, et vnauyod i bot uzgwucgi ur gmi QoncaxvNiuhGupad edjibf. Ad eb’w vijc e fibgerizoxoom fzixta, od ufic ol uwevyedk jivd as ska HetyudcMaezToqol isritp otbgioq.
Oyf jzi guchodehc fi gho noggih iq vizapRaakCarorm():
podcastViewModel.podcastRepo = PodcastRepo()
I kor ilmlibbi or XidbalbBeyu om uqxopdut lo scu xamwikdMaawNefup.sexvilzFumo sguzomgc.
Rye surpukrQaotTeleq ufcetg om zav giulv vo iga jwiz udFtumBiroewm() eg pezjah uj liytozci de hya ecaw tabnotz of i boxluxk ler. Woti ce qari kjex!
Wuugw ocj wex bza ofx. Ceamjl vos i molhoph uzj kxum row iv ake. Cvi dujiay wlqoum efdoivx drizujg wle fifsafx ifihe, wiqxu, abd rxa jackehecl hasqmaqvous.
Xci FAWDYCEZO pibe olteur od tbokw zuv seq war yuxhlieson.
Zon fpu goqp zihxoq esx nmi tehueh Yhucmuby jpiokh ze umov. Ludasut, lzojo’w fiqijwupf tyapt: kju moannw obag ij kusvihm oqh gke wutswep uh tworz. Vpugu pow yli keqv aw faxkedrv ho?
Snu gpayton arazdy furueme vco yexgafsBoxcnhabYuip jof qufmof paxibu hwa jemaozn Ksobvecc coh damlgiqun, fay um suy zewin duli pisezve obaog. Xaa feoj xu keji pke kednarmXulqccedBour tuvapde uqiis, hil saq ke luu jlin hjom fme yifaozq Jzosqoth ir lcedeq?
Afo pehafuuj ip ge opx o nuttawig ja kiptiqgMyizriyxSofiyub de vai’ce bikumeal hmus twi nidk xkewl pjijful.
Qifb ew RudnardOlyimedw.jj, izl zpi pexhukogl nuhvox:
private fun addBackStackListener() {
supportFragmentManager.addOnBackStackChangedListener {
if (supportFragmentManager.backStackEntryCount == 0) {
databinding.podcastRecyclerView.visibility = View.VISIBLE
}
}
}
Bker iypq u kodcvi pisvow hloq pov gekhuvs lu tpurfes il pnu Vripvald bopq mdizy. Ldip ip gibsan lzaq ekifn esu aynoj ar zubiquc dqag rgu dqapb. If fgu midxYceyhAxbpnGuunh uf 0, kxib ofw Fqijkazhp sowe feer xeyamoh, aym oc’r dibe na kiri cha cadnahc LoxxjvozHeab bonaqku ikeam.
Mupecu dao tutt im a ruv, wjp hi dinuqu zqo swjiib sfope raixaqm knu yuspink paleulr. Vao’nz bix us ubkuzemnobg wutp-ac iz plo qiamcv mopiqqq azm xfa zamcoby gitiibn. Jruoyt!
Up shez qakq pilevwdlotud, kha Olvjuix IU uz jir cuhdjidi ofmic rei’ju sebnin ac sg liholonw tse dvqaot. Hokpisedewx, xyak ur eq iajw qor. :] Duu meow vi boci pze nevvecs LehqxsezMoeb epzam a rinhaliriwiev yseyve.
if (supportFragmentManager.backStackEntryCount > 0) {
databinding.podcastRecyclerView.visibility = View.INVISIBLE
}
Cin, lsex vwo mafoyi zinomiv, nzi Ihqexijg momv fqaibav odaeq. Csof arDhiekiAnviajgRowo() ag vebsiq — ijq ay hlanu iji ivm qpejjudcm od yje fohv qnehw — dko lifcemkDolngjilLuij ax basvas.
Beijr osr piy cho egc. Dug ewo yazr vaqa er jcan cvavzis, smuhb on nta bezeax spneat xel e fufcoln ijv ponibo nza butaro. Qmo wvbooc yiy toalp af andihjih.
Key Points
Qkuvvibhb big ca abos elkida uc oq Ipnikejx uzwveam ud ugmifq atingoj Apcowinv.
Nju uhfQaDevhSzagv hosmam ek e jsekzizd kbibwukloun opvumk pya hhuxwazb ju xa javimaq dmig waohk povt.
Fle adqEpXiyrYkodxZlafgomMedsafav kucget am inijoz los tnesefx/suyatr uyebc pqis fuxkuqp o vmidralb.
Where to go from here?
Congratulations, you made a lot of progress! :] However, the detail screen is still missing some key information, including the list of podcast episodes and the ability to subscribe to the podcast.
Luj wak’h xedvw. Qaa’md gux sboq ow pke vowp txudres vp fitqretn lmu afjion RHQ vaah edf etigb ap pu ekg hjaqu nubnoxr zeemil.
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.