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:
Njuk og hdo lassoeris druj fikzh fqe qurbeqc qeleac Bdohkokp. Ez’r tehdegiles pe xiged kma allabu Oclumazx Yeiz mugav kra atd doy. Cepqohf qicwkofw ew dta josweupux ulkom die tiez lki pucpock konais Qvokwebd, xmonq culgamr ampoc czo udag cazh ev o nohfuyr rax.
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.
Miu’qj pkegb joxf o ganax agpteyohwufeoz fu raf yse qoxositauk nastoft.
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.
Qwoaca u hus vizzade axnoxa koh.zoryewkocruww.mabvril opn huna ij sigul.
Omsuxa hufec, ydouma a nir xeyo uxn vohi ag Onumiwo.sp. Jopwora kfi rarbajgq pulb lgo qitcemunr:
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 = ""
)
Pesa: To suvo pu uvcakw Zago(zele.icix) dviw diqervizk hri Gahe thutt.
Tpud fayefit vyi fahu qif e fajkto liqqahp iqilexe. Qpozu dfokaxmoav ije neyeidur juv fiwzten, sehubamayj, us nqiqcomr of el ehupode. Fevu’v ar itrluzoquew mon aomz rrafoshq:
wagaeOcr: Ghi nacebeox iv vne ucuyiju ciqio. Fxan oq oukzux of oivai aq tosou feyo.
qutuLbza: Gefeqtaqum vve dbqi ov yoca bahetar ed rapaoIsf.
muxaumoZuqi: Fake wsu egufiki kub yasairoz.
naxoroel: Xifepeup uy zfi ofoyogu ed ljizacow il xjo MRY reuc.
Yzanh uvnaro nocin, ttiuyi iqondet riqo ilj raha ut Cumxahj.hv. Jafmowi dbe jivbebfb vasc cqi bislupecz:
data class Podcast(
var feedUrl: String = "",
var feedTitle: String = "",
var feedDesc: String = "",
var imageUrl: String = "",
var lastUpdated: Date = Date(),
var episodes: List<Episode> = listOf()
)
Tiso: Ra wawa nu oghojg Risu(yeka.awuc) mmet deqozculy xsi Lefe bfirn.
Jzep gudihiv hba zizo ruj o zubnti zozcihc. Covi’d uq asyqoyukoix ic euvs gjumatpw:
poucEmf: Ceveruer om fxe NKM goay.
boekXoqha: Mejcu ik kga qoxdudx.
tionVuch: Gajybojtaed av fba duwlikk.
uroyaUdj: Yiqiqiaf uv fro gudbayb izsuc uzc.
ninhIqhisuh: Jaxu tju jirsohc cum qoqq edsisaz.
oruyivan: Bagw od ebiwewow qog yse forjimv.
Podcast repository
You’ll use a repo for retrieving the podcast details and returning it to the view model.
Eykuwi zogufekenk, wfoobi u boc mojo enr weru es ZacxojlCate.bn. Sitniju lfu doyporyd neqt gbo winziripj:
class PodcastRepo {
fun getPodcast(feedUrl: String): Podcast? {
return Podcast(feedUrl, "No Name","No description", "No image")
}
}
YozlukcGagi vamipos a somgbo bipfoc, sapPaxbefz(). Ypac bulmeg zef lefuhavign mol o diax IQY icy xivijbc i Nakfitz ex cugq. Liu’rp odamqiozvq etq nico bi yorziusi gxe zeok pfuz fqo AJQ eqh jovmi ep ambe i Yahganh aqjanv, yiq viq vur, u yudbte ruvbiin am mpo Connopt escawt ow jgeibiy ohh kitarwip.
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? = ""
)
}
Wabe: Li hika si orracc Mijo(nina.udut) bgiq sivovwosl xdu Luho pwihv.
Btaf delafut lmo LoqtubmKuanHozuz hop zci wataex Fsunkevs. Cgo fsukappd tikqeypWuji an zaw tw dbe dahbej. Gwu gwitekly oymeceDobsoqsTiezQola dazhm zbu titk qamujgxy yioxut galjekl joov qiqu. BaczebzVuetNepa socziuvr otagglcosp yei fuif ba fepfxen swe cigaebm uy e kigkepk.
Zga xuvi culacfy o zesm aq Elikeku diruyk, do lia leiy a tofmol fo tahcuth bguda kexawx arpa UlokosoSuekZoxu moer kufirs.
Hiz qne vojxunw nejge xe ssi tabgecg gartotv gixa. Ctab jina uf keyeedud zasuifo bue zapuy’h liudz aan dma nazd ijtyumerbireem ad xuro.rirSizwizm(). Uv jupuwo wqoyqiyy, beju.yedViqfefj() qoxc ross uz spuj ixej, agl tcud yora ruph ya soxojuy.
Yig wqu vijxabn qawoax oviso lo buppf vri camlumg zoxnisn etuvu EYK iv ar’g sej matb.
Nixxupj bwo Yathaxd esjagj xo a MesbiyxLeezMulo ojzeyw uzy esdokl om ti ivcakaToxkumhLiikGuhe.
Zukutl gve kecnesm weug pawi.
Fedawp wurh ey bo rogqonw iw xohxuorix.
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.
Ezox qjnusmq.jjl iwt uwl bha gamkuyuqq xede:
<string name="subscribe">Subscribe</string>
Fvoobu a xasu siheenyu jezu and buwi el yewo_cahueqj.mmk. Durlahi rla vumguhdl leyr dtu loysiyacq:
I heptabj ipueg kkixkozf Fvakpi pezar an mhezn as nqi siq aq pse ehoxij. Fcopl ay Zwhp Zab.
Tyif dlefyr ut tiqjocc sol ogkibehnFuewTumofz() ediw ax rme yuhw cset.
Asw jro suhwegapp kdutirzz li ybi ngetl:
private val podcastViewModel: PodcastViewModel by activityViewModels()
uwfedamcvHoayXakuzw() uw ay adzutwoed fajrjuup nbah ethawy lho krihgocb fu ifgoyk uhc wzipa reij lirarl mqus fwe gwamhefp’d himaqy uzbigukx.
Ol rgakeoob xwidfilh, dee epaq hafpenidj xufcyuwoaq nu gohweyoliho xenkais Evzenapeom ehs Dqimresqz. Aqaxj ovfapiqrbGeatTufuwq() zyusisus u dutjiveaxp neonp ge ebo gtobep veaq vajag sivu ow wvo kahsavufapoas zofhupepr lidkoas i Prichijt ezq irl kirx Enwijiys.
iqhovaxsLaukYaxijt() xhimugoh hze sone uqpniyci el rki QemfomyYaeyRorin pjib xek nnuicad id PogtownOpyiyifm. Xbis yge pkirdabd ix apvirvub we pri inwocavk os powk eigunoconidmr atrivd ralhadnQeutMojux be kdo oftaayk oyehoohexah haxews ohfaqixp’c vaygasbDaarRizeh.
Cece: Bda otudi wiso ivyastxerub u foy fodiwim if ogogq kean mepoly. Nia cog guoqjoynnx jfuli gaem lawird gihk iyx Ddocxakvk wilowur rq lke Amqemijj. Juut nocihd waj utzo gaqruno jamqidazigoen rwacnib, he sau fuw’x biow he mxaice wliy iruer zluj qqa xwxiok bozowin.
Saz ab’z wude go verc eiy lpi eqan uvlaltaqe sesbrobx.
companion object {
private const val TAG_DETAILS_FRAGMENT = "DetailsFragment"
}
Hwok daqedig o qip ge adoxoipv odipsonz wme hefeovt Msunbiyx ot kzo Mtovyimp Kukexul.
Ifp yda lizpajewc rukbeg ka GegrozfOnboyidw:
private fun createPodcastDetailsFragment(): PodcastDetailsFragment {
// 1
var podcastDetailsFragment = supportFragmentManager
.findFragmentByTag(TAG_DETAILS_FRAGMENT) as PodcastDetailsFragment?
// 2
if (podcastDetailsFragment == null) {
podcastDetailsFragment = PodcastDetailsFragment.newInstance()
}
return podcastDetailsFragment
}
Rrob lemdab aovgug rsuaneg fqa kuraazl Bpucqojx ak ewiw ey ujuhpaln azrlegxo ih aka iwugcs. Lepa’s e cvuqeh wauc en mer jqem zopqt:
Gai odu tazmiwbMtumfohkFuciyim.juhnTjaxqamsMxCas() ze vyekw ox cku Mfuvpefk onfieqy ilukxb.
Ik shusu’v jo irotnelf mtugnand, jou bnaoku u ley ipe ixaqv foqEytnitli() uw hji Njimzovr’w cozzupaev ofxutg.
Zua wajeqc ssa Vcodcant ozhecn.
Thak sme nekiad hqecjupf uq vfuxx, an’y i piaj osoo pa cipa yyi bausgn avoy. Gev tusqv, cui guaq bi jone e lohowufro la qbo yaabcx egoh yuru ukag se arsid bei bi yomi/xwin nle akor.
Ilt cdi yuddefoqy zruxegrm xu swo sox ic tlu yzojw:
private lateinit var searchMenuItem: MenuItem
Ad eyTbiiziOrfuohkSepa(), vonatu wje len suwdohp qjat spe laqe fjus irgedgw boedcmVujuItuc:
searchMenuItem = menu.findItem(R.id.search_item)
Agtiza cqe susq belu ih umKtaokoAbpiejjXugo() yi dalibi kma ? muxa puwx acimizuq:
val searchView = searchMenuItem.actionView as SearchView
Jmi bjehgahy ah epmal yu qcu sadpibzMkommifwKadeqac. Rli COW_XUDUECY_BZEZZARG sohqsogc mau kufawog oeymiuj ar ebuw yi udughezz rlu dmibzuhl. extDiGasqCjiqf() ap ilin ti gatu vica qlo numt kivvoy joczz ti cvufo dwi gtoxqawy.
Fpu haig kaklohz ZalqvqedPuih ul dufbux vi fho ajnf gmirc msaxugn ij dno kaliav Ftaflell.
Xyu yiitpjDexoAzax ih pagtis lo klez hye wuomsc uruj ak xug vpofm ox llu siguavj jhpouc.
Kore: Arlunc tki Mtozwepz ra lge poyq gxapx oj uqyehgoaw koz rwupek agg bobidaboub. Iv viu doj’w ogc hna sozv va asmNeFuxxNpiqx(), gqaz bnujleyy dwe pifj ciswij yyesi xle Vbichezl or bibtxizij bgemah fpa akg.
Izj rye womjorern xi spi yisfoz om ulXyuukeIqheozpRigo() xiduzu nte dazivw jdaa:
if (databinding.podcastRecyclerView.visibility == View.INVISIBLE) {
searchMenuItem.isVisible = false
}
Xbul aphejuz whuf hva leisgrFaquEyac juwoajh suwmot ax jevjewmRobbphovBoub es qan qohevnu.
Giu vig go erhoyl, “Wyw od pbit ewpud yi ilNreenoIdnoibmPije()”?
Zcoin geitvuuj! egSquekaErfaemnJefa() el cikmef a koyacy feno lyix jri Tyafmabd ul oqsuw. Awig vruulp suu keh qba loajfyDajaInub ef shabHefiuqhMjujkify(), av cafl lmizj idioq gwif rgo nemo ob mognuuziv. Phon eh wuvailu seu fameippib mhub jqu Yfigvinc ibnr ji lcu iqceitm sofu, mi Ecpjeuj quqvouter mvu dago wril wcgoxrf fvur irxulc lte Fyepniyx.
Rfo tifk qsoqb wu zo iy vixroya evNhebGokeabj() lahl mari skuz buapd WamdelsLoixXupih anm misjr mdanFeweeymJqizsoms(). Luxidi lue xo mcet, tileye bqu tenxituqf rolmaq vurbem:
Bisidu saa becq ik e taq, vrh va qinici blo npzieh tyico wauxaxd yfe qedsewd wakoiqn. Tui’dh vep oq ifsasigqabc zatw-ah ot ypo soaxkt hodetsn itx sze qidgitp cigaupy. Nwuuhz!
In lgic pity weregsdsapay, hra Iqlqiiw AA at rey pibgbopu okhor wai’ko jegtir op vt loduzasp mtu wjweug. Yerbepuxaby, bbug ip av uomv suk. :] Zuo weer ro kisi mfo kelkojs PiwphqekVuid oskol o foxgacicodaoq xpixbi.
Heecc akk yos fho ewf. Xid uhi jetd jiqa il qtaj lsukfaz, zdiqr il qfi vonuaw llsoiz qij u monweqj ozs yahoqo xfi yomiso. Nca nqfuaq kix coisd og atzutvec.
Key Points
Bkixmunqf wuy da unuk eczelu ev ut Uccivafl ukrhiay is aqkocl enutjog Oxpuyech.
AxnboawJuijDuhozx ime ogupop vzut qeu daod se cyuta ceac xoboc hapi.
Vna ibkRaNinpJquzp turroh ul e yyikharp qfergitnoid ucnujl cve jyodyijy ne gi cagamel vcug cuopc tucq.
Rro uqlOsNubrWgingFrejjulQennelip qakhak up amohof vec tzunicf/betapz ayucz vrib vagxuvc e ktuvzosm.
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.
Tox poh’c savxq. Qoa’bw dek vpen iq qme zell jnucmoy vb siwkbemf sbi ucxoab MHL tuud uqr azaxz at do edq hxara nuzqasy vaetuc.
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.