Lyndon2309
Lyndon2309

Reputation: 49

Populate UI from a ViewModel in Kotlin

This is the first time I am using Android Architecture. I have decided to go with MVVM structure but I am stuck at the point where I have no idea how to set the textViews in my XML with the data I pull from the database.

Checking the logs, I have seen that the function calls to my database (Firestore) does retrieve the correct data documents. Do I set the UI elements from the Activity,ViewModel or the Fragment? (Please note that I'm using a Navigation bar and controller)

Please assist me, my code is as follows:

My Single Activity:

class HomeActivity : AppCompatActivity() {

    // Create the three objects for the fragments
    lateinit var homeFragment: HomeFragment
    lateinit var visitsFragment: VisitsFragment
    lateinit var profileFragment: ProfileFragment

    // ViewModels
    lateinit var customerViewModel: CustomerViewModel



    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_home)

        // Initialize the bottom nav bar and navigation controller and then merge them
        val bottomNavigationView = findViewById<BottomNavigationView>(R.id.btm_nav)
        val navigationController = findNavController(R.id.fragmentHost)
        bottomNavigationView.setupWithNavController(navigationController)
        // Create app bar config object so that you can rename the bar ontop with the tab name
        val appBarConfiguration = AppBarConfiguration(setOf(R.id.homeFragment,R.id.visitsFragment,R.id.profileFragment))
        setupActionBarWithNavController(navigationController,appBarConfiguration)

        // View Model
        customerViewModel = ViewModelProvider(this).get(CustomerViewModel::class.java)
        customerViewModel.retrieveCustomer().observe(this, Observer { it })
    }


    // This function creates the menu object by inflating it with the resource we gave it (menu resource).
    override fun onCreateOptionsMenu(menu: Menu?): Boolean {
        menuInflater.inflate(R.menu.menu_main,menu);
        return true
    }
    // This function checks which menu item was selected and performs the task associated with the item selected.
    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        val id = item.itemId;

        // If Log Out menu item was selected
        if (id == R.id.menuLogOut){
            // Sign the user out
            FirebaseAuth.getInstance().signOut()
            // Finish this activity
            finish()
            // Start the initial activity
            val intent = Intent(this, MainActivity::class.java)
            startActivity(intent)
            // Display the message to the user
            Toast.makeText(this, "Successfully Signed Out", Toast.LENGTH_SHORT).show()
            return true
        }

        return super.onOptionsItemSelected(item)
    }
}

My View Model:

class CustomerViewModel: ViewModel() {

    val TAG = "CustomerViewModel"
    var db = Firebase.firestore
    var user = FirebaseAuth.getInstance().currentUser

    var liveData = MutableLiveData<List<Customer>>()
    var cusArray = arrayListOf<Customer>()
    var docRef = user?.uid

    fun retrieveCustomer(): MutableLiveData<List<Customer>>
    {
        db.collection("users").document(docRef.toString())
            .get()
            .addOnSuccessListener { document ->
                if (document != null)
                {
                    val data = document
                    // Set the data
                    val name = data.get("name") as String
                    val surname = data.get("surname") as String
                    val email = data.get("email") as String
                    val contactNo = data.get("contact no") as String

                    val customer = Customer(name, surname, email, contactNo)
                    cusArray.add(customer)

                    liveData.value = cusArray
                     Log.d(TAG, "DocumentSnapshot data: ${document.data}")
                }
                else
                {
                    Log.d(TAG, "No such document")
                }
            }
            .addOnFailureListener { exception ->
                Log.d(TAG, "get failed with " + exception.message, exception)
            }
        return liveData

    }

}

My Object Class

package com.CleanWheels.cleanwheels.DataClasses

data class Customer(
    val name: String?,
    val surname: String?,
    val email: String?,
    val contactNo: String?
)

My XML file (profile_fragment):

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".Fragments.ProfileFragment">

    <TextView
        android:id="@+id/banner"
        android:layout_width="499dp"
        android:layout_height="290dp"
        android:layout_alignParentStart="true"
        android:layout_alignParentTop="true"
        android:layout_alignParentEnd="true"
        android:layout_marginStart="-6dp"
        android:layout_marginTop="0dp"
        android:layout_marginEnd="0dp"
        android:background="@color/colorPrimary"
        android:layout_marginLeft="-6dp"
        android:layout_alignParentRight="true"
        android:layout_alignParentLeft="true"
        android:layout_marginRight="0dp" />

    <ImageView
        android:id="@+id/image"
        android:layout_width="154dp"
        android:layout_height="159dp"
        android:layout_alignParentTop="true"
        android:layout_alignParentEnd="true"
        android:layout_alignParentRight="true"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="53dp"
        android:layout_marginEnd="132dp"
        android:layout_marginRight="132dp"
        android:layout_marginBottom="78dp"
        android:src="@drawable/ic_action_profile" />

    <LinearLayout
        android:id="@+id/layout_1"
        android:layout_width="280dp"
        android:layout_height="40dp"
        android:layout_below="@id/banner"
        android:layout_marginLeft="80dp"
        android:layout_marginTop="100dp"
        android:layout_marginRight="20dp"
        android:orientation="vertical">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="Name:"
            android:textSize="18sp"/>

        <TextView
            android:id="@+id/profileNameUI"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:textSize="18sp"/>

    </LinearLayout>

    <LinearLayout
        android:id="@+id/layout_2"
        android:layout_width="280dp"
        android:layout_height="40dp"
        android:layout_below="@id/layout_1"
        android:layout_marginLeft="80dp"
        android:layout_marginTop="20dp"
        android:layout_marginRight="20dp"
        android:orientation="vertical">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="Surname:"
            android:textSize="18sp"/>

        <TextView
            android:id="@+id/profileSurnameUI"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:textSize="18sp"/>

    </LinearLayout>

    <LinearLayout
        android:id="@+id/layout_3"
        android:layout_width="280dp"
        android:layout_height="40dp"
        android:layout_below="@id/layout_2"
        android:layout_marginLeft="80dp"
        android:layout_marginTop="20dp"
        android:layout_marginRight="20dp"
        android:orientation="vertical">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="Email Address:"
            android:textSize="18sp"/>

        <TextView
            android:id="@+id/profileEmailUI"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:textSize="18sp"/>
    </LinearLayout>

    <LinearLayout
        android:id="@+id/layout_4"
        android:layout_width="280dp"
        android:layout_height="40dp"
        android:layout_below="@id/layout_3"
        android:layout_marginLeft="80dp"
        android:layout_marginBottom="50dp"
        android:layout_marginTop="20dp"
        android:layout_marginRight="20dp"
        android:orientation="vertical">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="Contact No:"
            android:textSize="18sp" />

        <TextView
            android:id="@+id/profileContactUI"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:textSize="18sp" />
    </LinearLayout>

<LinearLayout
    android:id="@+id/layout_5"
    android:layout_width="65dp"
    android:layout_height="220dp"
    android:layout_below="@id/banner"
    android:layout_alignParentStart="true"
    android:layout_alignParentLeft="true"
    android:layout_marginStart="0dp"
    android:layout_marginLeft="0dp"
    android:layout_marginTop="90dp"
    android:orientation="vertical">

    <ImageView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="0dp"
        android:layout_marginBottom="5dp"
        android:layout_weight="1"
        android:src="@drawable/ic_action_profile_name_ui"/>

    <ImageView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="5dp"
        android:layout_marginBottom="5dp"
        android:layout_weight="1"
        android:src="@drawable/ic_action_profile_surname_ui"/>

    <ImageView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="5dp"
        android:layout_marginBottom="5dp"
        android:layout_weight="1"
        android:src="@drawable/ic_action_profile_email_ui"/>
    <ImageView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="5dp"
        android:layout_marginBottom="0dp"
        android:layout_weight="1"
        android:src="@drawable/ic_action_profile_contact_ui"/>


</LinearLayout>

The profile_fragment class file:

import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup


// TODO: Rename parameter arguments, choose names that match
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
private const val ARG_PARAM1 = "param1"
private const val ARG_PARAM2 = "param2"

/**
 * A simple [Fragment] subclass.
 * Use the [ProfileFragment.newInstance] factory method to
 * create an instance of this fragment.
 */
class ProfileFragment : Fragment() {
    // TODO: Rename and change types of parameters
    private var param1: String? = null
    private var param2: String? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        arguments?.let {
            param1 = it.getString(ARG_PARAM1)
            param2 = it.getString(ARG_PARAM2)
        }
    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {

        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_profile, container, false)
    }

    companion object {
        /**
         * Use this factory method to create a new instance of
         * this fragment using the provided parameters.
         *
         * @param param1 Parameter 1.
         * @param param2 Parameter 2.
         * @return A new instance of fragment ProfileFragment.
         */
        // TODO: Rename and change types and number of parameters
        @JvmStatic
        fun newInstance(param1: String, param2: String) =
            ProfileFragment().apply {
                arguments = Bundle().apply {
                    putString(ARG_PARAM1, param1)
                    putString(ARG_PARAM2, param2)
                }
            }
    }
}

Then finally, the View I am trying to populate enter image description here

Upvotes: 0

Views: 1365

Answers (1)

TRK P
TRK P

Reputation: 386

You need to move all the logic to ProfileFragment once you navigate to ProfileFragment data will be set. Example:

ProfileFragment

class ProfileFragment : Fragment() {

private val customerViewModel: CustomerViewModel by viewModels()

override fun onCreateView(
    inflater: LayoutInflater, container: ViewGroup?,
    savedInstanceState: Bundle?
): View? {
    // Inflate the layout for this fragment
    val view = inflater.inflate(R.layout.fragment_profile, container, false)
    val name = view.findViewById<TextView>(R.id.name)
    val surname = view.findViewById<TextView>(R.id.surname)
    val email = view.findViewById<TextView>(R.id.email)
    val contact = view.findViewById<TextView>(R.id.contact)

    //calling initially here
    customerViewModel.retrieveCustomer()
    customerViewModel.liveData.observe(viewLifecycleOwner, Observer {
        //customer index at 0
        val customer = it[0]
        name.text = customer.name
        surname.text = customer.surname
        email.text = customer.email
        contact.text = customer.contactNo
    })

    return view
}

CustomerViewModel.kt

class CustomerViewModel(application: Application) : AndroidViewModel(application) {
var liveData = MutableLiveData<List<Customer>>()

fun retrieveCustomer(){
    //Your logic to get data from Firebase or any remote or db
    val listOfCustomer = mutableListOf<Customer>()
    val customer = Customer("name", "surname","email", "contqct")
    listOfCustomer.add(customer)
    liveData.postValue(listOfCustomer)
}

}

fragment_profile.xml

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

<TextView
    android:id="@+id/name"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:hint="Name"
    android:padding="8dp"
    android:textSize="24dp"
    />
<TextView
    android:id="@+id/surname"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:hint="Surname"
    android:padding="8dp"
    android:textSize="24dp"
    />
<TextView
    android:id="@+id/email"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:hint="Name"
    android:padding="8dp"
    android:textSize="24dp"
    />
<TextView
    android:id="@+id/contact"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:hint="Name"
    android:padding="8dp"
    android:textSize="24dp"
    />
</LinearLayout>

Upvotes: 1

Related Questions