JavaRush /Blog Java /Random-MS /Senarai kompleks dipermudahkan
Paul Soia
Tahap
Kiyv

Senarai kompleks dipermudahkan

Diterbitkan dalam kumpulan
Hai semua. Saya memutuskan untuk membangkitkan topik senarai kompleks dalam Android. Topik ini membingungkan saya pada mulanya. Ia kelihatan sangat sukar bagi saya, tetapi semuanya sebenarnya lebih mudah. Dan saya fikir sesiapa yang kini berhadapan dengan masalah ini akan mendapati artikel itu berguna. Semua contoh ditulis dalam Kotlin. Saya cuba menulis komen di mana-mana dan menjadikannya sejelas mungkin bagi mereka yang menulis dalam Java. Jadi inilah struktur kelas kami: Senarai kompleks dipermudahkan - 1MainActivity- Ini adalah kelas utama. ListAdapter- di sini kami mengikat senarai kami untuk melihat elemen. ViewHolder(terdapat beberapa daripadanya) - ini ialah penanda kami untuk setiap jenis elemen. Kelas item ialah data untuk senarai (pojo). Fail Xml ialah penanda ( activity_mainuntuk skrin utama, selebihnya untuk setiap jenis elemen senarai). Perkara pertama yang perlu kita lakukan ialah menambah kebergantungan supaya kita boleh menggunakan senarai (atas sebab tertentu kebergantungan ini hilang semasa membuat projek). Dalam fail build.gradledalam blok dependencieskami menambah baris:
implementation "androidx.recyclerview:recyclerview:1.1.0"
Seterusnya kami mencari fail activity_main.xml: Senarai kompleks dipermudahkan - 2Di sini hanya terdapat elemen kami untuk senarai - RecyclerView. Kami menambah sebarang inden dan pemformatan secukup rasa. Seterusnya kita buat kelas BaseViewHolder. Ia akan menjadi abstrak, kerana semua kelas kami akan mewarisi daripadanya ViewHolder. Ia agak mustahil untuk dilakukan tanpa itu, tetapi saya masih mengesyorkan memastikan bahawa semua kelas pewarisan adalah sama dalam struktur. (dalam Kotlin, extendsdan sebaliknya implementdigunakan :. Seperti dalam Java, di sini anda boleh mewarisi daripada satu kelas dan melaksanakan banyak antara muka) Seterusnya, kami mencipta kelas data kami: ItemTitle, ItemGoogledan ItemApple. Di Jawa, ini adalah kelas biasa dengan pembina 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 antara muka di sini ListMarker.
//это наш маркер. Его должны реализовать все айтемы, которые будут
//отображаться в конечном итоге в списке
interface ListMarker
Semua kelas data kami melaksanakan antara muka ini untuk menghantarnya kepada jenis yang sama. Kami akan memerlukan ini kemudian untuk senarai kami. Seterusnya, kami mencipta pemegang paparan kami untuk setiap elemen senarai. Jangan lupa bahawa setiap daripada mereka mewarisi daripada BaseViewHolder. Dalam kelas ini kami menentukan elemen yang viewsepadan dengan medan daripada 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 logik khusus di sini, semuanya sangat mudah. Selain itu, setiap pemegang paparan mempunyai reka letak sendiri: list_item_title.xmlSenarai kompleks dipermudahkan - 3list_item_google.xmlSenarai kompleks dipermudahkan - 4list_item_apple.xmlSenarai kompleks dipermudahkan - 5Sekarang mari kita beralih ke bahagian yang paling sukar - 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<*>
Apabila kami mewarisi kelas kami daripada RecyclerView.Adapter, kami perlu mengatasi tiga kaedah: onCreateViewHolder, getItemCount, onBindViewHolder. onCreateViewHolder- di sini kami memulakan kelas kami ViewHolder. getItemCount— kaedah ini bertanggungjawab untuk saiz senarai. onBindViewHolder- di sini kami menghantar elemen senarai ke kelas kami ViewHolder. Untuk senarai biasa dengan satu jenis elemen, ini sudah memadai. Tetapi untuk jenis yang berbeza kami masih perlu mentakrifkan semula kaedah getItemViewType(untuk ini kami menggunakan pemalar yang berada di bahagian atas kelas penyesuai kami. Di Java anda boleh menggunakan finalpembolehubah untuk ini). Juga dalam Java, onBindViewHolderbukannya ungkapan, whenanda boleh menggunakan if. Dan akhirnya, mari kita 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 kami dan dimulakan ListAdapter. Ini adalah kod boilerplate yang cantik yang banyak ditemui. Dalam kaedah itu, stubDatasaya mengisi senarai dengan data (seperti yang anda lihat, data dengan elemen yang berbeza) dan menyerahkan senarai ini kepada penyesuai. Seterusnya, kami melancarkan aplikasi kami, dan kami akan melihat sesuatu seperti ini pada skrin kami: Senarai kompleks dipermudahkan - 6Seperti yang anda lihat, terdapat elemen berbeza dalam satu senarai, yang merupakan matlamat kami. PS Terlupa untuk menyebut Extension. Inilah rupanya:
//это расширение для класса 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, tetapi fail, itulah sebabnya tiada nama di dalamnya. Tetapi kini kita boleh menggunakannya dalam penyesuai semata-mata inflatedan bukannya struktur panjang. Malangnya, ini tidak disediakan untuk Java, jadi tulis LayoutInflater.from(context).inflate. Itu sahaja. Sehingga lain kali :) Pautan ke GitHub
Komen
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION