first off all sorry for my bad english. Im trying to recieve Data from my self-written python Backend(REST-API) in my Android APP.
ApiService.kt:
private const val Base_URL = "http://192.168.178.93:5000/api/"
private val moshi = Moshi.Builder()
.add(KotlinJsonAdapterFactory())
.build()
private val retrofit = Retrofit.Builder()
.addConverterFactory(MoshiConverterFactory.create(moshi))
.baseUrl(Base_URL)
.build()
interface TodoApiService{
@GET("todo")
suspend fun getToDo(): List<ToDo>
}
object ToDoApi{
val retrofitService : TodoApiService by lazy {
retrofit.create(TodoApiService::class.java)
}
}
MainActivityViewModel.kt:
class MainActivityViewModel : ViewModel() {
of the most recent request
private val _status = MutableLiveData<String>()
val status: LiveData<String> = _status
private val _toDo = MutableLiveData<List<ToDo>>()
val toDo: LiveData<List<ToDo>> = _toDo
init {
getToDo()
}
private fun getToDo() {
viewModelScope.launch{
try {
_toDo.value = ToDoApi.retrofitService.getToDo()
_status.value = "Success $_toDo"
}catch (e: Exception){
_status.value = "Failure ${e.message}"
}
}
}
}
activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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=".MainActivity"
>
<androidx.recyclerview.widget.RecyclerView
android:scrollbars="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@ id/recycler_view"
tools:listitem="@layout/todo_item"/>
</FrameLayout>
todo_item.xml:
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp">
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="8dp">
<TextView
android:id="@ id/text_view_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="1"/>
</RelativeLayout>
</androidx.cardview.widget.CardView>
TodoAdapter.kt
class TodoAdapter(private val context: Context, private val Items: List<ToDo>):RecyclerView. Adapter<TodoAdapter.TodoViewHolder>(){
class TodoViewHolder(private val view: View) : RecyclerView.ViewHolder(view){
val textView: TextView = view.findViewById(R.id.text_view_name)
}
override fun getItemCount() = Items.size
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TodoViewHolder {
val adapterLayout = LayoutInflater.from(parent.context)
.inflate(R.layout.todo_item, parent, false)
return TodoViewHolder(adapterLayout)
}
override fun onBindViewHolder(holder: TodoViewHolder, position: Int) {
val ToDo = Items.get(position)
holder.textView.text = context.resources.getString(ToDo.Id.toInt())
}
}
MainActivity.kt:
class MainActivity : AppCompatActivity() {
private val viewModel = MainActivityViewModel()
private lateinit var binding: ActivityMainBinding
private lateinit var linearLayoutManager: LinearLayoutManager
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
linearLayoutManager = LinearLayoutManager(this)
binding.recyclerView.layoutManager = linearLayoutManager
binding.recyclerView.adapter = TodoAdapter(this, viewModel.toDo.value)
}
}
I know that i need an adapter to connect my LiveData to my recyclerView. But im not able to implement it right. Android studio tells, i cant use my MutableLiveData<List> for my Adapter that just only need a normal List(Required: List Found:List?. I cant Cast cause the data could be null. Im gratefully for Hints.
CodePudding user response:
This is because you are sending a possible nullable list from the activity to the Adapter. You must do something like this:
binding.recyclerView.adapter = TodoAdapter(this, viewModel.toDo?.value ?: listOf())
CodePudding user response:
Your use of LiveData is incorrect. You use it as a simple variable, passing its current value (which is null) to the adapter. LiveData is intended for data stream and need to be observed.
I like to add a setter for the data on the Adapter class:
fun setData(data: List<ToDo>) {
Items = data
notifyDataSetChanged()
}
And in the Activity, observe the ViewModel's live data and update the adapter when new data arrives:
class MainActivity : AppCompatActivity() {
private lateinit var adapter: TodoAdapter
override fun onCreate(savedInstanceState: Bundle?) {
...
adapter = TodoAdapter(this)
viewModel.todo.observe(this, { todos ->
adapter.setData(todos)
}
}
...
}
Now when you set value to your LiveData in the ViewModel, the adapter will be notified.