JavaRush /Java Blog /Random-ID /Daftar rumit menjadi mudah
Paul Soia
Level 26
Kiyv

Daftar rumit menjadi mudah

Dipublikasikan di grup Random-ID
Halo semua. Saya memutuskan untuk mengangkat topik daftar kompleks di Android. Topik ini awalnya membingungkan saya. Tampaknya sangat sulit bagi saya, tetapi sebenarnya semuanya lebih sederhana. Dan menurut saya siapa pun yang sekarang menghadapi masalah ini akan merasakan manfaat artikel ini. Semua contoh ditulis di Kotlin. Saya mencoba menulis komentar di mana-mana dan membuatnya sejelas mungkin bagi mereka yang menulis dalam bahasa Java. Jadi inilah struktur kelas kita: Daftar rumit menjadi mudah - 1MainActivity- Ini adalah kelas utama. ListAdapter- di sini kami mengikat daftar kami untuk melihat elemen. ViewHolder(ada beberapa di antaranya) - ini adalah markup kami untuk setiap jenis elemen. Kelas item adalah data untuk daftar (pojo). File XML adalah markup ( activity_mainuntuk layar utama, sisanya untuk setiap jenis elemen daftar). Hal pertama yang perlu kita lakukan adalah menambahkan ketergantungan sehingga kita dapat menggunakan daftar (karena alasan tertentu ketergantungan ini hilang saat membuat proyek). Di file build.gradledi blok dependencieskami menambahkan baris:
implementation "androidx.recyclerview:recyclerview:1.1.0"
Selanjutnya kita menemukan file activity_main.xml: Daftar rumit menjadi mudah - 2Di sini hanya ada elemen kami untuk daftar - RecyclerView. Kami menambahkan indentasi dan format apa pun sesuai selera. Selanjutnya kita membuat kelasnya BaseViewHolder. Ini akan menjadi abstrak, karena semua kelas kita akan mewarisinya ViewHolder. Sangat mungkin untuk melakukannya tanpanya, tetapi saya tetap menyarankan untuk memastikan bahwa semua kelas yang diwarisi memiliki struktur yang sama. (di Kotlin, extendsdan implementdigunakan sebagai gantinya :. Seperti di Java, di sini Anda dapat mewarisi dari satu kelas dan mengimplementasikan banyak antarmuka) Selanjutnya, kita membuat kelas data: ItemTitle, ItemGoogledan ItemApple. Di Java, ini adalah kelas biasa dengan konstruktor dan pengambil.
data class ItemGoogle(
    val name: String,
    val product: String,
    val version: String,
    val isUse: Boolean
) : ListMarker
data class ItemApple(
    val name: String,
    val country: String,
    val year: Int
) : ListMarker
data class ItemTitle(
    val title: String,
    val amount: Int
) : ListMarker
Mari fokus pada antarmuka di sini ListMarker.
//это наш маркер. Его должны реализовать все айтемы, которые будут
//отображаться в конечном итоге в списке
interface ListMarker
Semua kelas data kami mengimplementasikan antarmuka ini untuk mengubahnya ke tipe yang sama. Kita akan membutuhkannya nanti untuk daftar kita. Selanjutnya, kita membuat viewholder untuk setiap elemen daftar. Jangan lupa bahwa masing-masing dari mereka mewarisi dari BaseViewHolder. Di kelas-kelas ini kami menentukan elemen mana viewyang sesuai dengan bidang dari kelas data.
abstract class BaseViewHolder<t>(itemView: View) : RecyclerView.ViewHolder(itemView) {
    abstract fun bind(item: T)
}
class GoogleViewHolder(view: View) : BaseViewHolder<itemgoogle>(view) {

    override fun bind(item: ItemGoogle) {
        itemView.tvName.text = item.name
        itemView.tvProduct.text = item.product
        itemView.tvVersion.text = item.version
        if (item.isUse) {
            itemView.tvProduct.visibility = View.GONE
        } else {
            itemView.tvProduct.visibility = View.VISIBLE
        }
    }
}
class AppleViewHolder(view: View) : BaseViewHolder<itemapple>(view) {

    override fun bind(item: ItemApple) {
        //можем делать так
        itemView.tvName.text = item.name
        itemView.tvCountry.text = item.country
        itemView.tvYear.text = item.year.toString()

        /*----сверху и снизу два идентичных блока----*/

        //а можем сделать такой блок и не использовать в каждой строке itemView
        with(itemView) {
            tvName.text = item.name
            tvCountry.text = item.country
            tvYear.text = item.year.toString()
        }
    }
}
class TitleViewHolder(view: View) : BaseViewHolder<itemtitle>(view) {

    override fun bind(item: ItemTitle) {
        itemView.tvTitle.text = item.title
        itemView.tvAmount.text = item.amount.toString()
    }
}
Tidak ada logika khusus di sini, semuanya sangat sederhana. Selain itu, setiap viewholder memiliki tata letaknya sendiri: list_item_title.xmlDaftar rumit menjadi mudah - 3list_item_google.xmlDaftar rumit menjadi mudah - 4list_item_apple.xmlDaftar rumit menjadi mudah - 5Sekarang mari beralih ke bagian tersulit - ListAdapter.
class ListAdapter : RecyclerView.Adapter<baseviewholder<*>>() {

    companion object {
        //задаем константы для каждого типа айтема
        private const val TYPE_TITLE = 0
        private const val TYPE_GOOGLE = 1
        private const val TYPE_APPLE = 2
    }

    //здесь можно использовать обычный ArrayList
    //сюда добавляются все айтемы, которые реализовали интерфейс ListMarker
    //How вариант можно было сделать mutableListOf<any>() и обойтись без интерфейса
    private val items = mutableListOf<listmarker>()

    internal fun swapData(list: List<listmarker>) {
        items.clear()
        items.addAll(list)
        notifyDataSetChanged()
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder<*> {
        return when(viewType) {
            //задаем разметку для каждого типа айтема
            TYPE_TITLE -> TitleViewHolder(parent.inflate(R.layout.list_item_title))
            TYPE_GOOGLE -> GoogleViewHolder(parent.inflate(R.layout.list_item_google))
            TYPE_APPLE -> AppleViewHolder(parent.inflate(R.layout.list_item_apple))
            else -> throw IllegalArgumentException("Invalid view type")
        }
    }

    override fun getItemViewType(position: Int): Int {
        return when (items[position]) {
            is ItemTitle -> TYPE_TITLE
            is ItemGoogle -> TYPE_GOOGLE
            is ItemApple -> TYPE_APPLE
            else -> throw IllegalArgumentException("Invalid type of item $position")
        }
    }

    override fun getItemCount(): Int {
        //этот метод определяет размер списка
        return items.size
    }

    override fun onBindViewHolder(holder: BaseViewHolder<*>, position: Int) {
        val element = items[position]
        when (holder) {
            //отправляем каждый айтем к своему ViewHolder
            is TitleViewHolder -> holder.bind(element as ItemTitle)
            is GoogleViewHolder -> holder.bind(element as ItemGoogle)
            is AppleViewHolder -> holder.bind(element as ItemApple)
            else -> throw IllegalArgumentException()
        }
    }
}
</listmarker></listmarker></any></baseviewholder<*>
Ketika kita mewarisi kelas kita dari RecyclerView.Adapter, kita perlu mengganti tiga metode: onCreateViewHolder, getItemCount, onBindViewHolder. onCreateViewHolder- di sini kita menginisialisasi kelas kita ViewHolder. getItemCount— metode ini bertanggung jawab atas ukuran daftar. onBindViewHolder- di sini kita meneruskan elemen daftar ke kelas kita ViewHolder. Untuk daftar reguler dengan satu tipe elemen, ini sudah cukup. Namun untuk tipe yang berbeda kita masih perlu mendefinisikan ulang metodenya getItemViewType(untuk ini kita menggunakan konstanta yang berada di bagian atas kelas adaptor kita. Di Java Anda dapat menggunakan finalvariabel untuk ini). Juga di Java, onBindViewHolderalih-alih ekspresi, whenAnda bisa menggunakan ekspresi reguler if. Dan akhirnya, mari beralih ke kelas utama kita - MainActivity.
class MainActivity : AppCompatActivity() {

    private val adapter = ListAdapter()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        initRecyclerView()
    }

    private fun initRecyclerView() {
        rvList.layoutManager = LinearLayoutManager(this)

        //здесь мы задаем разделитель между айтемами, чтоб они не сливались друг с другом
        val divider = DividerItemDecoration(this, LinearLayoutManager.VERTICAL)
        rvList.addItemDecoration(divider)

        rvList.adapter = adapter
        stubData()
    }

    private fun stubData() {
        val list = mutableListOf<listmarker>()
        list.add(ItemTitle("title1", 4))
        list.add(ItemGoogle("android", "product1", "17.0v", true))
        list.add(ItemGoogle("no name", "product2", "3.1v", false))
        list.add(ItemApple("macOs", "USA", 2005))
        list.add(ItemApple("iOs", "China", 2007))
        list.add(ItemTitle("title2", 2))
        list.add(ItemGoogle("map", "product3", "23.0v", true))
        list.add(ItemApple("car", "England", 2018))
        list.add(ItemTitle("title3", 0))
        //отправляем все данные в адаптер
        adapter.swapData(list)
    }
}
</listmarker>
RecyclerViewDi sinilah tempat kami dan diinisialisasi ListAdapter. Ini adalah kode boilerplate cantik yang banyak ditemui. Dalam metode ini, stubDatasaya mengisi daftar dengan data (seperti yang Anda lihat, data dengan elemen berbeda) dan meneruskan daftar ini ke adaptor. Selanjutnya, kita meluncurkan aplikasi kita, dan kita akan melihat sesuatu seperti ini di layar kita: Daftar rumit menjadi mudah - 6Seperti yang Anda lihat, ada elemen berbeda dalam satu daftar, yang merupakan tujuan kita. PS Lupa menyebutkan Extension. Ini adalah tampilannya:
//это расширение для класса ViewGroup. Теперь мы можем по всему проекту использовать
//короткое inflate instead of длинного LayoutInflater.from(context).inflate
fun ViewGroup.inflate(@LayoutRes layoutRes: Int, attachToRoot: Boolean = false): View =
    LayoutInflater.from(context).inflate(layoutRes, this, attachToRoot)
Ini bukan kelas, tapi file, itulah sebabnya tidak ada nama di dalamnya. Tapi sekarang kita bisa menggunakannya di adaptor secara sederhana, inflatebukan di struktur yang panjang. Sayangnya, ini tidak disediakan untuk Java, jadi tulislah LayoutInflater.from(context).inflate. Itu saja. Sampai jumpa lagi :) Tautan ke GitHub
Komentar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION