JavaRush /Blog Java /Random-VI /Danh sách phức tạp được thực hiện dễ dàng
Paul Soia
Mức độ
Kiyv

Danh sách phức tạp được thực hiện dễ dàng

Xuất bản trong nhóm
Chào mọi người. Tôi quyết định nêu chủ đề về danh sách phức tạp trong Android. Chủ đề này lúc đầu làm tôi bối rối. Đối với tôi điều đó có vẻ rất khó khăn nhưng thực ra mọi thứ lại đơn giản hơn. Và tôi nghĩ rằng bất cứ ai hiện đang phải đối mặt với vấn đề này sẽ thấy bài viết này hữu ích. Tất cả các ví dụ đều được viết bằng Kotlin. Tôi đã cố gắng viết bình luận ở mọi nơi và làm cho nó rõ ràng nhất có thể đối với những người viết bằng Java. Đây là cấu trúc lớp của chúng ta: Danh sách phức tạp được thực hiện dễ dàng - 1MainActivity- Đây là lớp chính. ListAdapter- ở đây chúng tôi liên kết danh sách của mình để xem các phần tử. ViewHolder(có một vài trong số chúng) - đây là đánh dấu của chúng tôi cho từng loại phần tử. Các lớp mục là dữ liệu cho một danh sách (pojo). Tệp Xml là các đánh dấu ( activity_mainđối với màn hình chính, phần còn lại dành cho từng loại thành phần danh sách). Điều đầu tiên chúng ta cần làm là thêm một phần phụ thuộc để có thể sử dụng danh sách (vì lý do nào đó phần phụ thuộc này bị thiếu khi tạo dự án). Trong tệp build.gradletrong khối, dependencieschúng tôi thêm dòng:
implementation "androidx.recyclerview:recyclerview:1.1.0"
Tiếp theo, chúng tôi tìm thấy tệp activity_main.xml: Danh sách phức tạp được thực hiện dễ dàng - 2Ở đây chỉ có phần tử của chúng tôi cho danh sách - RecyclerView. Chúng tôi thêm bất kỳ thụt lề và định dạng nào theo sở thích. Tiếp theo chúng ta tạo lớp BaseViewHolder. Nó sẽ trừu tượng vì tất cả các lớp của chúng ta sẽ kế thừa từ nó ViewHolder. Bạn hoàn toàn có thể thực hiện mà không cần nó, nhưng tôi vẫn khuyên bạn nên đảm bảo rằng tất cả các lớp kế thừa đều có cấu trúc giống nhau. (trong Kotlin extendsimplementđược sử dụng thay thế :. Giống như trong Java, ở đây bạn có thể kế thừa từ một lớp và triển khai nhiều giao diện) Tiếp theo, chúng ta tạo các lớp dữ liệu: ItemTitle, ItemGoogleItemApple. Trong Java, đây là những lớp thông thường có hàm tạo và getters.
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
Hãy tập trung vào giao diện ở đây ListMarker.
//это наш маркер. Его должны реализовать все айтемы, которые будут
//отображаться в конечном итоге в списке
interface ListMarker
Tất cả các lớp dữ liệu của chúng tôi đều triển khai giao diện này để chuyển chúng thành cùng loại. Chúng tôi sẽ cần điều này sau cho danh sách của chúng tôi. Tiếp theo, chúng tôi tạo trình xem cho từng thành phần của danh sách. Đừng quên rằng mỗi người trong số họ kế thừa từ BaseViewHolder. Trong các lớp này, chúng tôi chỉ định phần tử nào viewtương ứng với trường từ các lớp dữ liệu.
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()
    }
}
Không có logic cụ thể nào ở đây, mọi thứ cực kỳ đơn giản. Ngoài ra, mỗi viewholder đều có bố cục riêng: list_item_title.xmlDanh sách phức tạp được thực hiện dễ dàng - 3list_item_google.xmlDanh sách phức tạp được thực hiện dễ dàng - 4list_item_apple.xmlDanh sách phức tạp được thực hiện dễ dàng - 5Bây giờ hãy chuyển sang phần khó nhất - 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<*>
Khi kế thừa lớp của mình từ RecyclerView.Adapter, chúng ta sẽ cần ghi đè ba phương thức: onCreateViewHolder, getItemCount, onBindViewHolder. onCreateViewHolder- ở đây chúng ta khởi tạo các lớp của mình ViewHolder. getItemCount— phương pháp này chịu trách nhiệm về kích thước danh sách. onBindViewHolder- ở đây chúng ta chuyển các thành phần danh sách cho các lớp của mình ViewHolder. Đối với một danh sách thông thường có một loại phần tử, điều này là đủ. Nhưng đối với các loại khác nhau, chúng ta vẫn cần xác định lại phương thức getItemViewType(để làm điều này, chúng ta sử dụng các hằng số ở đầu lớp bộ điều hợp. Trong Java, bạn có thể sử dụng finalcác biến cho việc này). Ngoài ra, trong Java, onBindViewHolderthay vì biểu thức, whenbạn có thể sử dụng if. Và cuối cùng, hãy chuyển sang lớp chính của chúng ta - 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>
RecyclerViewĐây là nơi chúng tôi và được khởi tạo ListAdapter. Đây là một đoạn mã soạn sẵn khá hay mà nhiều người đã gặp phải. Trong phương pháp này, stubDatatôi đã điền dữ liệu vào danh sách (như bạn có thể thấy, dữ liệu với các phần tử khác nhau) và chuyển danh sách này đến bộ chuyển đổi. Tiếp theo, chúng ta khởi chạy ứng dụng của mình và sẽ thấy một cái gì đó như thế này trên màn hình: Danh sách phức tạp được thực hiện dễ dàng - 6Như bạn có thể thấy, có nhiều thành phần khác nhau trong một danh sách, đó là mục tiêu của chúng ta. PS Quên đề cập đến Extension. Đây là những gì nó trông giống như:
//это расширение для класса 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)
Đây không phải là một lớp mà là một tập tin, đó là lý do tại sao không có tên bên trong nó. Nhưng bây giờ chúng ta có thể sử dụng nó trong bộ chuyển đổi một cách đơn giản inflatethay vì một cấu trúc dài. Thật không may, điều này không được cung cấp cho Java, vì vậy hãy viết LayoutInflater.from(context).inflate. Đó là tất cả. Cho đến lần sau :) Liên kết tới GitHub
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION