Group Group Group Group Group Group Group Group Group

Android Kotlin app - One Activity, Three Fragments, Retrofit2, Picasso/Glide, RecyclerView, and Reactive UI = ViewModel + LiveData + Data Binding (View Binding)

I successfully develop an iTunes Music Search Android app using Kotlin with one activity and three fragments (search, list, and detail) using Retrofit2 to interact with iTunes Search API, Picasso to get the art image from the server into my app, RecyclerView for the list screen, and the ViewModel to allow the fragments to exchange data.

Now I am upgrading this app to also use LiveData and Data Binding but I am having issues loading the music_item.xml (the ViewGroup or row) that will be loaded into the RecyclerView → music_list_fragment.xml and I don’t think I am using the combination of the ViewModel + LiveData + Data Binding properly.

The Ask: I need help figuring out why the music_item.xml is not getting loaded into the music_list_fragment.xml by the RecyclerView and help learning to properly use the combination of the ViewModel + LiveData + Data Binding so that when the device is rotated the data is not loss regardless of the fragment I have loaded in the activity.

In my MacBook Prod, I am using:
Android Studio 4.2.1
Kotlin plugin version 202-1.5.10-release-894-AS8194.7
Java version 1.8

build.gradle (:app)

plugins {
    id 'com.android.application'
    id 'kotlin-android'
    id 'kotlin-parcelize'
    id 'kotlin-kapt'
}

android {
    compileSdkVersion 30

    defaultConfig {
        applicationId "com.dj.musicsearch"
        minSdkVersion 23
        targetSdkVersion 30
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }

    kotlinOptions {
        jvmTarget = '1.8'
    }

    buildFeatures {
        viewBinding = true
    }

    dataBinding {
        enabled = true
    }
}

dependencies {
    implementation 'androidx.appcompat:appcompat:1.3.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
    implementation 'androidx.core:core-ktx:1.3.1'
    implementation 'androidx.fragment:fragment-ktx:1.3.4'
    implementation 'androidx.legacy:legacy-support-v4:1.0.0'

    //ViewModel + LiveData
    implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'

    //Data (View) Binding
    kapt 'com.android.databinding:compiler:3.1.4'

    implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.3.1'
    implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1'
    implementation 'androidx.recyclerview:recyclerview:1.2.0'

    implementation 'com.github.bumptech.glide:glide:4.11.0'
    implementation 'com.google.android.material:material:1.3.0'
    implementation 'com.jakewharton.picasso:picasso2-okhttp3-downloader:1.1.0'

    implementation 'com.squareup.picasso:picasso:2.5.2'
    implementation 'com.squareup.retrofit2:retrofit:2.9.0'
    implementation 'com.squareup.retrofit2:converter-gson:2.9.0'

    implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"

    testImplementation 'junit:junit:4.13.2'
    androidTestImplementation 'androidx.test.ext:junit:1.1.2'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
}

When I run ‘Sync Project with Gradle Files’ I get
enter image description here

MusicSearchActivity.kt


package com.dj.music.controller

import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import com.dj.music.R
import com.dj.music.databinding.MusicSearchActivityBinding
import com.dj.music.fragments.MusicSearchFragment

class MusicSearchActivity: AppCompatActivity() {
    private val TAG = MusicSearchActivity::class.qualifiedName

    private lateinit var binding: MusicSearchActivityBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = MusicSearchActivityBinding.inflate(layoutInflater)
        val view = binding.root
        setContentView(view)
        Log.i(TAG, "Music Search Activity created")

        val musicSearchFragment = MusicSearchFragment()
        supportFragmentManager.beginTransaction().apply {
            Log.i(TAG, "Using FragmentManager display the MusicSearchFragment within the MusicSearchActivity layout container")
            replace(R.id.fragment_container, musicSearchFragment)
            addToBackStack("MusicSearchFragment")
            commit()
        }
    }
}

music_search_activity.xml


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:tools="http://schemas.android.com/tools"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:orientation="horizontal"
  android:background="#9E9E9E"
  android:id="@+id/fragment_container"
  tools:context=".controller.MusicSearchActivity">

</LinearLayout>

MusicSearchFragment.kt


    package com.dj.music.fragments

import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.EditText
import android.widget.Toast
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import com.dj.music.R
import com.dj.music.databinding.MusicSearchFragmentBinding
import com.dj.music.model.MusicItemData
import com.dj.music.model.RecyclerList
import com.dj.music.model.MusicSharedViewModel
import com.dj.music.service.ITuneSearchApi
import com.dj.music.utils.ServiceHelper
import retrofit2.Call
import retrofit2.Response

/**
 * The {@link MusicSearchFragment} is the sender (source) fragment.
 * It sends the list of MusicItemData to the (target fragment) MusicListFragment
 */
class MusicSearchFragment: Fragment() {
    private val TAG = MusicSearchFragment::class.qualifiedName

    private val musicSharedViewModel: MusicSharedViewModel by activityViewModels()

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        val binding = MusicSearchFragmentBinding.inflate(layoutInflater)
        val view = binding.root
        Log.i(TAG, "Music Search Fragment created")

        initMusicSearchFragment(view)

        return view
    }

    private fun initMusicSearchFragment(view: View) {
        Log.i(TAG, "Bind the Music Search Fragment edit text and button")

        val searchEditText = view.findViewById<EditText>(R.id.search_text)
        val searchButton = view.findViewById<Button>(R.id.search_button)

        searchButton.setOnClickListener {
            val searchQuery = searchEditText.text.toString().replace(" ", "+")

            val musicListFragment = MusicListFragment()
            Log.i(TAG, "Put the music search in the shared ViewModel so that it be used by the MusicListFragment to search for it and then to display the list of music found")
            musicSharedViewModel.setMutableSearchTerm(searchQuery)

            parentFragmentManager.beginTransaction().apply {
                Log.i(TAG, "Using FragmentManager display the MusicListFragment within the MusicSearchActivity layout container")
                replace(R.id.fragment_container, musicListFragment)
                addToBackStack("MusicListFragment")
                commit()
            }
        }
    }
}

music_search_fragment.xml


<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:orientation="vertical">

  <ImageView
    android:id="@+id/music_search_image_background"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:scaleType="centerCrop"
    android:src="@drawable/music_search_background2"/>

  <TextView
    android:id="@+id/app_name"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_centerHorizontal="true"
    android:layout_marginTop="220dp"
    android:text="@string/app_name_textview"
    android:textColor="@android:color/white"
    android:textStyle="bold"
    android:textSize="40sp"/>

  <EditText
    android:id="@+id/search_text"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginTop="24dp"
    android:layout_marginBottom="8dp"
    android:hint="@string/search"
    android:gravity="center"
    android:inputType="text"
    android:layout_below="@id/app_name"
    android:layout_centerHorizontal="true"
    android:layout_margin="10dp"
    android:textColorHint="@android:color/darker_gray"
    android:background="@android:color/white"/>

  <Button
    android:id="@+id/search_button"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_below="@id/search_text"
    android:layout_centerHorizontal="true"
    android:text="@string/search_button" />

</RelativeLayout>

RecyclerList.kt


package com.dj.music.model

import com.google.gson.annotations.SerializedName

data class RecyclerList(
    val results: ArrayList<MusicItemData>
)

data class MusicItemData(
    @SerializedName("artworkUrl100")
    val imageUrl: String?,

    val artistName: String?,

    @SerializedName("collectionName")
    val album: String?,

    val trackName: String?,

    val trackPrice: Float?,

    @SerializedName("primaryGenreName")
    val genre: String?,

    val longDescription: String?,

    val previewUrl: String?
)

MusicSharedViewModel.kt


package com.dj.music.model

import android.util.Log
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.dj.music.adapter.MusicRecyclerViewAdapter
import com.dj.music.service.ITuneSearchApi
import com.dj.music.utils.ServiceHelper
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response

class MusicSharedViewModel: ViewModel() {
    private val TAG = MusicSharedViewModel::class.qualifiedName
    var recyclerListData: MutableLiveData<RecyclerList> = MutableLiveData<RecyclerList>()
    var musicRecyclerViewAdapter: MusicRecyclerViewAdapter = MusicRecyclerViewAdapter()

    fun getAdapter(): MusicRecyclerViewAdapter {
        return musicRecyclerViewAdapter
    }

    fun setAdapterData(data: ArrayList<MusicItemData>) {
        musicRecyclerViewAdapter.setDataList(data)
        musicRecyclerViewAdapter.notifyDataSetChanged()
    }

    fun getRecyclerListDataObserver(): MutableLiveData<RecyclerList> {
        return recyclerListData
    }

    fun makeAPICall(searchTerm: String) {
        Log.i(TAG, "Constructing a fully-qualified URL to submit an HTTP GET Request to iTunes Search API")
        val iTuneSearchApi = ServiceHelper.getRetrofit().create(ITuneSearchApi::class.java)
        val call = iTuneSearchApi.getMusicData(searchTerm)
        call.enqueue(object: Callback<RecyclerList> {
            override fun onFailure(call: Call<RecyclerList>, t: Throwable) {
                Log.i(TAG, "Error searching and/or getting music data from iTunes Search API:  ${t.message}")
                t.printStackTrace()
                recyclerListData.postValue(null)
            }

            override fun onResponse(call: Call<RecyclerList>, response: Response<RecyclerList>) {
                if(response.isSuccessful) {
                    recyclerListData.postValue(response.body())
                } else {
                    Log.i(TAG, "Error searching and/or getting music data from iTunes Search API:  ${response.code()}")
                    recyclerListData.postValue(null)
                }

                //Toast.makeText(this@MusicSharedViewModel.context, "Fetching results...", Toast.LENGTH_LONG).show()
                /*Log.i(TAG, "Successfully receive music data returned from the iTunes Search API in JSON format:  ${response.body().toString()}")
                val musicItemDataList: ArrayList<MusicItemData>? = response.body()?.results

                if (musicItemDataList != null) {
                    val musicListFragment = MusicListFragment()
                    Log.i(TAG, "Put the MusicItemData list in the shared ViewModel so that it be used by the MusicListFragment to display it")
                    musicSharedViewModel.setMutableMusicItemDataList(musicItemDataList)

                    parentFragmentManager.beginTransaction().apply {
                        Log.i(TAG, "Using FragmentManager display the MusicListFragment within the MusicSearchActivity layout container")
                        replace(R.id.fragment_container, musicListFragment)
                        addToBackStack("MusicListFragment")
                        commit()
                    }
                }*/
            }
        })
    }


    private val mutableSearchTerm = MutableLiveData<String>()
    private val mutableMusicItemDataList = MutableLiveData<ArrayList<MusicItemData>>()
    private val mutableMusicItemData = MutableLiveData<MusicItemData>()
    private val mutablePreviewUrl = MutableLiveData<String>()

    // Getter
    fun getMutableSearchTerm(): String? {
        return mutableSearchTerm.value
    }

    // Setter
    fun setMutableSearchTerm(searchTerm: String) {
        mutableSearchTerm.value = searchTerm
    }

    // Getter
    fun getMutableMusicItemDataList(): ArrayList<MusicItemData>? {
        return mutableMusicItemDataList.value
    }

    // Setter
    fun setMutableMusicItemDataList(musicItemDataList: ArrayList<MusicItemData>) {
        mutableMusicItemDataList.value = musicItemDataList
    }

    // Getter
    fun getMutableMusicItemDataClick(): MusicItemData? {
        return mutableMusicItemData.value
    }

    // Setter
    fun setMutableMusicItemDataClick(musicItemData: MusicItemData) {
        mutableMusicItemData.value = musicItemData
    }

    // Getter
    fun getMutablePreviewUrl(): String? {
        return mutablePreviewUrl.value
    }

    // Setter
    fun setMutablePreviewUrl(previewUrl: String) {
        mutablePreviewUrl.value = previewUrl
    }
}

  ******************** This is where I am stuck ********************

MusicListFragment.kt


package com.dj.music.fragments

import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.View.GONE
import android.view.ViewGroup
import android.widget.ProgressBar
import android.widget.Toast
import androidx.databinding.DataBindingUtil
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProviders
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.StaggeredGridLayoutManager.VERTICAL
import com.dj.music.BR
import com.dj.music.R
import com.dj.music.databinding.MusicListFragmentBinding
import com.dj.music.model.MusicItemData
import com.dj.music.model.MusicSharedViewModel
import com.dj.music.model.RecyclerList
import com.dj.music.utils.ServiceHelper

class MusicListFragment: Fragment() {
    private val TAG = MusicListFragment::class.qualifiedName

    private val musicSharedViewModel: MusicSharedViewModel by activityViewModels()

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        val binding = MusicListFragmentBinding.inflate(layoutInflater)
        val view = binding.root
        Log.i(TAG, "Music List Fragment created")

        val recyclerView = view.findViewById<RecyclerView>(R.id.music_recycler_view)
        val progressBar = view.findViewById<ProgressBar>(R.id.progress_bar)
        val searchQuery = musicSharedViewModel.getMutableSearchTerm()
        val viewModel = makeApiCall(searchQuery.toString(), progressBar)

        setupBinding(viewModel, recyclerView)

        return view
    }

    private fun makeApiCall(searchTerm: String, progressBar: ProgressBar): MusicSharedViewModel {
        val viewModel = ViewModelProviders.of(requireActivity()).get(MusicSharedViewModel::class.java)
        viewModel.getRecyclerListDataObserver().observe(requireActivity(), Observer<RecyclerList> {
            progressBar.visibility = GONE

            if(it != null) {
                //update the adapter
                viewModel.setAdapterData(it.results)

            } else {
                Toast.makeText(this@MusicListFragment.context, "Error in fetching data", Toast.LENGTH_LONG).show()
            }
        })

        viewModel.makeAPICall(searchTerm)

        return viewModel
    }

    fun setupBinding(viewModel: MusicSharedViewModel, recyclerView: RecyclerView) {
        //music_recycler_view   I am not able to use data binding here
        val musicListFragmentBinding = DataBindingUtil.setContentView<MusicListFragmentBinding>(requireActivity(), R.layout.music_list_fragment)
        //val musicListFragmentBinding = DataBindingUtil.setContentView<MusicListFragmentBinding>(this@MusicListFragment.context as Activity, R.layout.music_list_fragment)
        musicListFragmentBinding.setVariable(BR.viewModel, viewModel)
        musicListFragmentBinding.executePendingBindings()

        recyclerView.apply {
            layoutManager = LinearLayoutManager(this@MusicListFragment.context)
            val decoration = DividerItemDecoration(this@MusicListFragment.context, VERTICAL)
            addItemDecoration(decoration)
        }
    }
}


MusicRecyclerViewAdapter.kt


package com.dj.music.adapter

import android.util.Log
import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.ImageView
import androidx.databinding.BindingAdapter
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.dj.music.R
import com.dj.music.databinding.MusicItemBinding
import com.dj.music.model.MusicItemData

class MusicRecyclerViewAdapter: RecyclerView.Adapter<MusicRecyclerViewAdapter.MusicItemViewHolder>() {
    private val TAG = MusicRecyclerViewAdapter::class.qualifiedName

    private var musicItemDataList = ArrayList<MusicItemData>()

    override fun getItemCount() = musicItemDataList.size

    fun setDataList(data: ArrayList<MusicItemData>) {
        this.musicItemDataList = data
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MusicItemViewHolder {
        Log.i(TAG, "MusicItemViewHolder created - Load music_item.xml layout into the music_list_fragment.xml using the RecyclerView.")
        val layoutInflater = LayoutInflater.from(parent.context)
        val binding = MusicItemBinding.inflate(layoutInflater)

        return MusicItemViewHolder(binding)
    }

    override fun onBindViewHolder(musicViewHolder: MusicItemViewHolder, position: Int) {
        Log.i(TAG, "Bind the MusicItemData to the MusicItemViewHolder for those items that display on the device's screen")
        musicViewHolder.bind(musicItemDataList[position])

        musicViewHolder.itemView.setOnClickListener {
            Log.i(TAG, "Put the MusicItemData for the Music Item clicked in the shared ViewModel so that it be used by the MusicDetailFragment to display it")
            //musicSharedViewModel.setMutableMusicItemDataClick(musicItemDataList[position])
            //from the Music List Fragment I need to go to the Music Detail Fragment
        }
    }

    class MusicItemViewHolder(val binding: MusicItemBinding): RecyclerView.ViewHolder(binding.root) {
        fun bind(data: MusicItemData) {
            binding.musicItemData = data
            binding.executePendingBindings()
        }
    }

    companion object {
        @BindingAdapter("loadImage")
        fun loadImage(music_item_image: ImageView, imageUrl: String) {
            Glide.with(music_item_image)
                .load(imageUrl)
                .circleCrop()
                .placeholder(R.drawable.poster_image_xhdpi)
                .error(R.drawable.poster_image_xhdpi)
                .fallback(R.drawable.poster_image_xhdpi)
                .into(music_item_image)
        }
    }
}

music_list_fragment.xml


<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:app="http://schemas.android.com/apk/res-auto">

  <data>
    <variable
      name="viewModel"
      type="com.dj.music.model.MusicSharedViewModel" />
  </data>

<RelativeLayout
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:orientation="vertical">

  <ImageView
    android:id="@+id/music_search_image_background"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:scaleType="centerCrop"
    android:src="@drawable/music_search_background1"/>

  <!-- Header aligned to top -->
  <RelativeLayout
    android:id="@+id/list_header"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_alignParentTop="true"
    android:background="@color/purple_500"
    android:gravity="center" >

    <TextView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_margin="16dp"
      android:text="@string/results_header"
      android:textColor="@android:color/white"
      android:textSize="32sp" />
  </RelativeLayout>

  <androidx.recyclerview.widget.RecyclerView
    android:id="@+id/music_recycler_view"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_below="@id/list_header"
    android:paddingLeft="10dp"
    android:paddingRight="10dp"
    app:setAdapter='@{viewModel.getAdapter()}'/>

  <ProgressBar
    android:id="@+id/progress_bar"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:visibility="invisible"
    android:layout_centerInParent="true" />

</RelativeLayout>
</layout>

music_item.xml


<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:app="http://schemas.android.com/apk/res-auto"
  xmlns:tools="http://schemas.android.com/tools">

  <data>
    <variable name="musicItemData" type="com.dj.music.model.MusicItemData" />
  </data>

<RelativeLayout
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:padding="10dp"
  tools:context=".fragments.MusicListFragment">

  <ImageView
    android:id="@+id/music_item_image"
    android:layout_width="100dp"
    android:layout_height="100dp"
    android:layout_marginRight="8dp"
    app:loadImage='@{musicItemData.imageUrl}'
    tools:src="@drawable/poster_image_xhdpi"/>

  <TextView
    android:id="@+id/music_item_artist"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginBottom="8dp"
    android:text='@{musicItemData.artistName ?? "No Artist Name available"}'
    android:textSize="18sp"
    android:textColor="@android:color/white"
    android:layout_toRightOf="@id/music_item_image"/>

  <TextView
    android:id="@+id/music_item_album"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginBottom="8dp"
    android:text='@{musicItemData.album ?? "No Album desc available"}'
    android:textSize="18sp"
    android:textColor="@android:color/white"
    android:layout_toRightOf="@id/music_item_image"
    android:layout_below="@id/music_item_artist"/>

  <TextView
    android:id="@+id/music_item_track_name"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text='@{musicItemData.trackName ?? "No Track Name available"}'
    android:textSize="18sp"
    android:textColor="@android:color/white"
    android:layout_toRightOf="@id/music_item_image"
    android:layout_below="@id/music_item_album"/>

</RelativeLayout>
</layout>

*********** I am not getting this far, so this piece is just informational ***********
MusicDetailFragment.kt


package com.dj.music.fragments

import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import com.dj.music.R
import com.dj.music.model.MusicItemData
import com.dj.music.model.MusicSharedViewModel
import com.dj.music.utils.ServiceHelper

class MusicDetailFragment: Fragment() {
    private val TAG = MusicDetailFragment::class.qualifiedName

    private val musicSharedViewModel: MusicSharedViewModel by activityViewModels()

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        val view = inflater.inflate(R.layout.music_detail_fragment, container, false)
        Log.i(TAG, "Music Detail Fragment created")

        initMusicDetailFragment(view)

        return view
    }

    private fun initMusicDetailFragment(view: View) {
        Log.i(TAG, "Bind the Music Detail Fragment to the music item data")

        val picasso = ServiceHelper.getPicasso(view.context)
        val musicItemData: MusicItemData? = musicSharedViewModel.getMutableMusicItemDataClick()

        val imageView = view.findViewById<ImageView>(R.id.music_image)
        picasso.load(musicItemData?.imageUrl)
            .placeholder(R.drawable.poster_image_xxxhdpi)
            .into(imageView)

        if(musicItemData?.artistName != null) {
            val artist = view.findViewById<TextView>(R.id.music_artist)
            artist.text = musicItemData?.artistName
        }

        if(musicItemData?.album != null) {
            val album = view.findViewById<TextView>(R.id.music_album)
            album.text = musicItemData?.album
        }

        if(musicItemData?.trackName != null) {
            val trackName = view.findViewById<TextView>(R.id.music_track_name)
            trackName.text = musicItemData?.trackName
        }
    }
}

music_detail_fragment.xml


<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:tools="http://schemas.android.com/tools"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  tools:context=".fragments.MusicDetailFragment">

  <!-- Header aligned to top -->
  <RelativeLayout
    android:id="@+id/detail_header"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_alignParentTop="true"
    android:background="@color/purple_500"
    android:gravity="center" >

    <TextView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_margin="16dp"
      android:text="@string/detail_header"
      android:textColor="@android:color/white"
      android:textSize="32sp" />
  </RelativeLayout>

  <ImageView
    android:id="@+id/music_search_image_background"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:scaleType="centerCrop"
    android:layout_below="@id/detail_header"
    android:contentDescription="@string/content_desc_back_image"
    android:src="@drawable/music_search_background1"/>

  <ImageView
    android:id="@+id/music_image"
    android:layout_width="200dp"
    android:layout_height="200dp"
    android:layout_centerHorizontal="true"
    android:layout_centerVertical="true"
    android:layout_marginBottom="16dp"
    android:contentDescription="@string/content_desc_album_image"
    tools:src="@drawable/poster_image_xxxhdpi"/>

  <TextView
    android:id="@+id/music_artist"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_centerHorizontal="true"
    android:textSize="28sp"
    android:textColor="@android:color/white"
    android:layout_marginBottom="8dp"
    android:layout_below="@id/music_image"/>

  <TextView
    android:id="@+id/music_album"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_centerHorizontal="true"
    android:textSize="28sp"
    android:textColor="@android:color/white"
    android:layout_marginBottom="8dp"
    android:layout_below="@id/music_artist"/>

  <TextView
    android:id="@+id/music_track_name"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_centerHorizontal="true"
    android:textSize="28sp"
    android:textColor="@android:color/white"
    android:layout_below="@id/music_album"/>

</RelativeLayout>

This topic was automatically closed after 166 days. New replies are no longer allowed.