Adapter ViewBinding

xml

Activity RecyclerView

以下是Activity xml,有一個RecyclerView。

activity_main3.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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">

    <TextView
        android:id="@+id/title_tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="156dp"
        android:text="TextView"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.498"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyleView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="48dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/title_tv" />
</androidx.constraintlayout.widget.ConstraintLayout>

adapter xml

注意!!!!!! ConstraintLayout 的 layout_height 一定要是wrap_content,TextView 的 layout_height 也是 wrap_content 。
之前測試的時候,列表的資料只有三筆,但永遠清單只有一筆資料,原因就是高度設為match_parent。

article_item.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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="wrap_content">

    <TextView
        android:id="@+id/item_tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="TextView"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

LayoutManager

LayoutManager:控制 RecyclerView 項目的排列方式。

ConstraintLayout:是 item XML 中的視圖容器。

不能因為item的layout是ConstraintLayout,就把LayoutManager設為ConstraintLayout,二者不是相同的東西。

垂直

1
binding.recyleView.layoutManager = LinearLayoutManager(this)

水平

1
binding.recyleView.layoutManager = LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false)

網格

1
binding.recyleView.layoutManager = GridLayoutManager(this, 2)

Activity 呼叫 Adapter

  • 在 XML 中定義 RecyclerView
  • 在 Activity 中初始化 Adapter
  • 設置 LayoutManager
  • 將 Adapter 設置給 RecyclerView
  • 提供資料給 Adapter

Main Activity

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.LinearLayoutManager
import com.example.coroutine.adapter.ArticleItemAdapter
import com.example.coroutine.api.ArticleItem
import com.example.coroutine.databinding.ActivityMain3Binding
import kotlinx.coroutines.launch
import kotlin.getValue

class MainActivity15 : AppCompatActivity() {
  private lateinit var binding : ActivityMain3Binding
  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    binding = ActivityMain3Binding.inflate(layoutInflater)
    setContentView(binding.root)
    // 在 Activity 中初始化 Adapter
    val adapter = ArticleItemAdapter(this@MainActivity15)
    
    // 設置 LayoutManager
    binding.recyleView.layoutManager = LinearLayoutManager(this)
    
    // 將 Adapter 設置給 RecyclerView
    binding.recyleView.adapter = adapter
    
    // 提供資料給 Adapter
    val articles = listOf(
      ArticleItem("標題1", "來源1", "時間1"),
      ArticleItem("標題2", "來源2", "時間2"),
      ArticleItem("標題3", "來源3", "時間3")
    )
    adapter.setData(articles)
  }
}

ViewHolder

1
2
3
4
5
6
import androidx.recyclerview.widget.RecyclerView
import androidx.viewbinding.ViewBinding

class ItemViewHolder(val binding: ViewBinding) :
  RecyclerView.ViewHolder(binding.root) {
}

Adapter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import android.content.Context
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.example.coroutine.api.ArticleItem
import com.example.coroutine.databinding.ArticleItemBinding

class ArticleItemAdapter(private val context: Context) : RecyclerView.Adapter<ItemViewHolder>() {
  private val data = ArrayList<ArticleItem>()
  fun setData(data: List<ArticleItem>) {
    this.data.clear()
    this.data.addAll(data)
    notifyDataSetChanged()
  }

  override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemViewHolder {
    val binding = ArticleItemBinding.inflate(LayoutInflater.from(context), parent, false)
    return ItemViewHolder(binding)
  }

  override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
    val item = data[position]
    val binding = holder.binding as ArticleItemBinding
    binding.itemTv.text = "${item.title} "
  }

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

結合 stateflow 與 retrofit

viewmodel

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import androidx.lifecycle.ViewModel
import com.example.coroutine.api.ArticleList
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import androidx.lifecycle.viewModelScope
import com.example.coroutine.api.ArticleApi
import kotlinx.coroutines.launch

class ArticleViewModel : ViewModel() {
  val _articleList = MutableStateFlow<ArticleList?>(null)
  val articleList: StateFlow<ArticleList?> = _articleList
  fun getArticleList() {
    viewModelScope.launch {
      val result = ArticleApi.retrofit.articleList()
      _articleList.value = result
    }
  }
}

retrofit

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory

object RetrofitUtil {
  private const val BASE_URL =
    "https://mock.apipost.cn/app/mock/project/ced69cf2-9206-4a42-895e-dd7442a888df/"
  private val retrofit = Retrofit.Builder()
    .addConverterFactory(GsonConverterFactory.create())
    .baseUrl(BASE_URL)
    .build()

  fun <T> createService(clazz: Class<T>):T {
    return retrofit.create(clazz)
  }
}

ArticleApi.kt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import retrofit2.http.GET

data class ArticleItem(val title: String, val source: String, val time: String)

data class ArticleList(val data: List<ArticleItem>, val code: Int = -1, val message: String)

interface ArticleApi {
  @GET("article/list")
  suspend fun articleList(): ArticleList
  companion object {
    val retrofit: ArticleApi by lazy {
      RetrofitUtil.createService(ArticleApi::class.java)
    }
  }
}

Activity

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class MainActivity15 : AppCompatActivity() {
  private val viewModel by viewModels<ArticleViewModel>()
  private lateinit var binding : ActivityMain3Binding
  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    binding = ActivityMain3Binding.inflate(layoutInflater)
    setContentView(binding.root)
    val adapter = ArticleItemAdapter(this@MainActivity15)
    binding.recyleView.layoutManager = LinearLayoutManager(this)
    binding.recyleView.adapter = adapter

    // 取得資料
    viewModel.getArticleList()

    // 收集資料 並 呼叫adapter
    lifecycleScope.launch {
      viewModel.articleList.collect { value ->
        value?.data?.let {
          adapter.setData(value.data)
        }
      }
    }
  }
}

results matching ""

    No results matching ""