Home > front end >  How do I add progressBar when getting api data through Retrofit? I am using MVVM pattern.The data is
How do I add progressBar when getting api data through Retrofit? I am using MVVM pattern.The data is

Time:03-21

Now the data is fetched,but I also want the progress bar to show.I am using hilt for depedency injection.Check out my files below.I highly appreciate your feedback.I arleady have the Result class which holds the states but I am not quite sure on how to connect it with viewModel.

Api interface.kt

interface CampInterface {

    @GET("programs")   //includes endpoint
    fun getPrograms() : Call<List<Programs>>

    @GET("departments")   //includes endpoint
    fun getDepartments() : Call<List<Departments>>
}

viewModel.kt

@HiltViewModel
class campDataViewModel @Inject constructor(private val campDataRepository: CampDataRepository) : ViewModel() {

    val programList = MutableLiveData<List<Programs>>()
    val departmentList = MutableLiveData<List<Departments>>()

    val errorMessage = MutableLiveData<String>()

     fun getProgramData() {


        val response=campDataRepository.getAllPrograms()

        response.enqueue(object : Callback<List<Programs>?> {
            override fun onResponse(
                call: Call<List<Programs>?>,
                response: Response<List<Programs>?>
            ) {
                programList.postValue(response.body())
            }

            override fun onFailure(call: Call<List<Programs>?>, t: Throwable) {
                errorMessage.postValue(t.message)
            }
        })
    }


    fun getDepartmentData() {


        val response=campDataRepository.getAllDepartments()

        response.enqueue(object : Callback<List<Departments>?> {
            override fun onResponse(
                call: Call<List<Departments>?>,
                response: Response<List<Departments>?>
            ) {
                departmentList.postValue(response.body())
            }

            override fun onFailure(call: Call<List<Departments>?>, t: Throwable) {
                errorMessage.postValue(t.message)
            }
        })
    }


}

ProgramFragment.kt

@AndroidEntryPoint
class ProgramFragment  : Fragment() {

    lateinit var tv_data: TextView
    lateinit var pg_recyclerview: RecyclerView

    @Inject
    lateinit var viewModelFactory: MyViewModelFactory



    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_program, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        pg_recyclerview=view.findViewById(R.id.pg_recyclerview)

        // this creates a vertical layout Manager
        pg_recyclerview.layoutManager = LinearLayoutManager(requireContext())

        //creates an adapter
        val adapter= CampdataAdapter(listOf())
        pg_recyclerview.adapter=adapter



        val viewModel=ViewModelProviders.of(this,viewModelFactory).get(campDataViewModel::class.java)


        viewModel.programList.observe(this, Observer {
            when(it.status)
            Log.d(TAG, "onCreate: $it")
            adapter.Programs=it   //it => all items in the list
            adapter.notifyDataSetChanged()
        })
        viewModel.errorMessage.observe(this, Observer {
        })
        viewModel.getProgramData()


    }



}

Repository.kt

class CampDataRepository (private val campInterface: CampInterface) {

    fun getAllPrograms() =campInterface.getPrograms()

    fun getAllDepartments() =campInterface.getDepartments()



}

Results.kt

//for state
class Result<out T>(val status: Status, val data: T?, message: String?) {


    companion object {
        fun <T> success(data: T?): Result<T> {
            return Result(Status.SUCCESS, data, null)
        }

        fun <T> loading(message: String?): Result<T> {
            return Result(Status.LOADING, null, message)
        }

        fun <T> error(message: String?): Result<T> {
            return Result(Status.ERROR, null, message)
        }
    }

}

enum class Status {
    SUCCESS,
    LOADING,
    ERROR
}

fragmet_program.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    tools:context=".ui.ProgramFragment">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@ id/pg_recyclerview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:padding="6dp"
        >

    </androidx.recyclerview.widget.RecyclerView>


    <com.google.android.material.bottomnavigation.BottomNavigationView
        android:id="@ id/bottomNavigationView"
        android:layout_width="match_parent"
        android:layout_height="65dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="parent"
        app:menu="@menu/bottom_nav_menu"/>

    <ProgressBar
        android:id="@ id/progressBar"
        style="?android:attr/progressBarStyle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toTopOf="@ id/bottomNavigationView"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="@ id/pg_recyclerview"
        tools:visibility="visible"
        android:visibility="gone"/>

</androidx.constraintlayout.widget.ConstraintLayout>

program adapter.kt

class CampdataAdapter ( var Programs:List<Programs>) :RecyclerView.Adapter<CampdataAdapter.Campdataholder>() {


    //viewholder class
    inner class Campdataholder(itemview : View)  : RecyclerView.ViewHolder(itemview){
        val tvprogramname =itemview.findViewById<TextView>(R.id.tv_programname)
        var tvcode =itemview.findViewById<TextView>(R.id.tv_code)



    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Campdataholder{
        //layout to inflate to the recycler view
        val view = LayoutInflater.from(parent.context).inflate(R.layout.program_item,parent,false)
        return Campdataholder(view)
    }

    override fun onBindViewHolder(holder: Campdataholder, position: Int) {
        //set values or listeners for the views
        var currentprogram=Programs[position]

        holder.tvprogramname.text=currentprogram.Name
        holder.tvcode.text=currentprogram.Code




    }

    override fun getItemCount(): Int {
        return Programs.size
    }
}

CodePudding user response:

One option is to add a new variable in ViewModel val loading = MutableLiveData<Boolean>() and then observe it in view. For your example

ViewModel

val loading = MutableLiveData<Boolean>()

fun getProgramData() {
    loading.postValue(true)

    val response=campDataRepository.getAllPrograms()

    response.enqueue(object : Callback<List<Programs>?> {
        override fun onResponse(
            call: Call<List<Programs>?>,
            response: Response<List<Programs>?>
        ) {
            programList.postValue(response.body())
            loading.postValue(false)
        }

        override fun onFailure(call: Call<List<Programs>?>, t: Throwable) {
            errorMessage.postValue(t.message)
            loading.postValue(false)
        }
    })
}

Fragment

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
                
        viewModel.loading.observe(this, Observer {
            when(it) {
                true -> progressBar.visibility = View.Visible
                false -> progressBar.visibility = View.Gone
        })
}

CodePudding user response:

Change your viewModel like this

    val result = MutableLiveData<Result>()
    val departmentList = MutableLiveData<List<Departments>>()

    val errorMessage = MutableLiveData<String>()

     fun getProgramData() {

        result.postValue(Result(Status.LOADING, null, "your message"))


        val response=campDataRepository.getAllPrograms()

        response.enqueue(object : Callback<List<Programs>?> {
            override fun onResponse(
                call: Call<List<Programs>?>,
                response: Response<List<Programs>?>
            ) {
                result.postValue(Result(Status.SUCCESS, response.body(), null))
            }

            override fun onFailure(call: Call<List<Programs>?>, t: Throwable) {
                result.postValue(Result(Status.ERROR, response.body(), null))
            }
        })
    }

Then In Fragment

viewModel.result.observe(this, Observer {
  when(result.status){
    Status.SUCCESS -> {
        progressBar.visibility = View.GONE
        adapter.Programs=it.data  //it => all items in the list
        adapter.notifyDataSetChanged()
    }
    Status.ERROR -> {
        progressBar.visibility = View.GONE
        //show the error message
    }
    Status.LOADING -> {
        progressBar.visibility = View.VISIBLE
    }
}

            
 })
  • Related