iT邦幫忙

2021 iThome 鐵人賽

DAY 9
0
自我挑戰組

Android kotlin &MVVM系列 第 9

Android學習筆記09

把coroutines跟retrofit結合進recyclerview
這邊最需要注意的是如果你retrofit拿到的值是int而不是string必須要stringvalueof(),否則android會預設為xml的數值而報錯

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:app="http://schemas.android.com/apk/res-auto">
    <data>
        <variable
            name="Post"
            type="com.example.myapplication.Post" />
        <variable
            name="viewmodel"
            type="com.example.myapplication.ViewModel" />
    </data>
    <androidx.constraintlayout.widget.ConstraintLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical" android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="@{()-> viewmodel.onClick(Post)}">
        <TextView
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
            android:padding="5dp"
            android:textSize="16sp"
            android:text="@{String.valueOf(Post.id)}" />
    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

這邊第25行就是數值轉換
activity

class Activity:AppCompatActivity() {
    lateinit var binding: ActivityMainBinding

    private val mViewModel by lazy {
        initViewModel(application, ViewModel::class.java)
    }

    private val mAdapter by lazy {
        Adapter<Post>(R.layout.item_view,mViewModel)
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
        binding.recyclerView.adapter = mAdapter
        mViewModel.data.observe(this, Observer { post ->
            mAdapter.updateData(post)
        })
        mViewModel.coroutine()
    }
}

adapter

class Adapter<T>(
    @LayoutRes val resource: Int,
    val viewmodel:ViewModel
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {

    private val data: List<T>
        get() = _data.toList()
    private val _data: MutableList<T> = mutableListOf()

    fun updateData(list: ArrayList<T>){
        _data.clear()
        _data.addAll(list)
        notifyDataSetChanged()
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        val layoutInflater = LayoutInflater.from(parent.context)
        val binding = DataBindingUtil.inflate<ViewDataBinding>(layoutInflater, resource, parent,false)
        return MyViewHolder(binding)
    }

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

    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        if (holder is Adapter<*>.MyViewHolder){
            val item = data[position]
            if (item != null)
                holder.setupData(item)
        }

    }

    inner class MyViewHolder( var binding: ViewDataBinding):RecyclerView.ViewHolder(binding.root){
        fun setupData(post: Any){
            binding.setVariable(BR.Post, post)
            binding.setVariable(BR.viewmodel,viewmodel)
        }
    }

}

viewmodel

class ViewModel(application: Application): AndroidViewModel(application) {


    val data = MutableLiveData<ArrayList<Post>>()

    fun onClick(post: Post){
        Toast.makeText(getApplication(), post.id, Toast.LENGTH_SHORT).show()
    }

    private val myScope = object: CoroutineScope{
        override val coroutineContext: CoroutineContext
            get() = job
    }
    private val job = Job()

    fun coroutine(){
        myScope.launch(Dispatchers.Main){
//            progressBar.visibility = View.VISIBLE
            retrofit()
//            progressBar.visibility = View.GONE
        }
    }

    private suspend fun retrofit() = withContext(Dispatchers.IO){
        val response = GoRetrofit.api.getAPI()
        if (response.isSuccessful) {
            data.postValue(response.body() as ArrayList<Post>)
        }
    }

    override fun onCleared() {
        myScope.cancel()
        super.onCleared()
    }

}

這邊要注意的是postvalue(),如果在main線程是可以使用setvalue(),但如過是在用後台線程就必須要用postvalue()


上一篇
Android學習筆記08
下一篇
Android學習筆記10
系列文
Android kotlin &MVVM30

尚未有邦友留言

立即登入留言