JavaRush /Java Blog /Random-KO /복잡한 목록을 쉽게 만들어보세요
Paul Soia
레벨 26
Kiyv

복잡한 목록을 쉽게 만들어보세요

Random-KO 그룹에 게시되었습니다
안녕하세요 여러분. 나는 안드로이드에서 복잡한 목록이라는 주제를 제기하기로 결정했습니다. 이 주제는 처음에 나를 당황하게 만들었습니다. 나에게는 매우 어려워 보였지만 실제로는 모든 것이 더 간단합니다. 그리고 나는 지금 이 문제에 직면한 누구에게나 이 기사가 유용하다고 생각할 것이라고 생각합니다. 모든 예제는 Kotlin으로 작성되었습니다. 나는 어디서나 주석을 작성하려고 노력했으며 Java로 작성하는 사람들을 위해 가능한 한 명확하게 설명했습니다. 클래스 구조는 다음과 같습니다. 복잡한 목록을 쉽게 만들어보세요 - 1MainActivity- 이것이 메인 클래스입니다. ListAdapter- 여기서 목록을 뷰 요소에 바인딩합니다. ViewHolder(여러 가지가 있습니다) - 이것은 각 요소 유형에 대한 마크업입니다. 항목 클래스는 목록(pojo)에 대한 데이터입니다. XML 파일은 마크업입니다( activity_main기본 화면의 경우 나머지는 각 목록 요소 유형에 대한 것입니다). 가장 먼저 해야 할 일은 목록을 사용할 수 있도록 종속성을 추가하는 것입니다(어떤 이유로 프로젝트를 생성할 때 이 종속성이 누락되었습니다). build.gradle블록의 파일에 dependencies다음 줄을 추가합니다.
implementation "androidx.recyclerview:recyclerview:1.1.0"
다음으로 파일을 찾습니다 activity_main.xml. 복잡한 목록을 쉽게 만들어보세요 - 2여기에는 목록에 대한 요소만 있습니다 RecyclerView. 취향에 따라 들여쓰기와 서식을 추가합니다. 다음으로 클래스를 생성합니다 BaseViewHolder. 우리의 모든 클래스가 그것으로부터 상속받게 되므로 그것은 추상적일 것입니다 ViewHolder. 그것 없이도 가능하지만 모든 상속 클래스의 구조가 동일한지 확인하는 것이 좋습니다. (Kotlin에서는 extends및 가 대신 implement사용됩니다 . Java에서와 마찬가지로 여기서는 하나의 클래스에서 상속하고 많은 인터페이스를 구현할 수 있습니다.) 다음으로 데이터 클래스인 , 및 를 :만듭니다 . Java에서는 생성자와 게터가 있는 일반 클래스입니다. ItemTitleItemGoogleItemApple
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
여기서는 인터페이스에 중점을 두겠습니다 ListMarker.
//это наш маркер. Его должны реализовать все айтемы, которые будут
//отображаться в конечном итоге в списке
interface ListMarker
모든 데이터 클래스는 이 인터페이스를 구현하여 동일한 유형으로 변환합니다. 나중에 목록에 필요할 것입니다. 다음으로 목록의 각 요소에 대한 뷰홀더를 만듭니다. 그들 각각은 에서 상속받는다는 것을 잊지 마세요 BaseViewHolder. view이러한 클래스에서는 데이터 클래스의 필드에 해당하는 요소를 지정합니다 .
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()
    }
}
여기에는 특별한 논리가 없으며 모든 것이 매우 간단합니다. 또한 각 뷰홀더에는 고유한 레이아웃이 있습니다. list_item_title.xml복잡한 목록을 쉽게 만들어 보세요 - 3list_item_google.xml복잡한 목록을 쉽게 만들어 보세요 - 4list_item_apple.xml복잡한 목록을 쉽게 만들어 보세요 - 5이제 가장 어려운 부분인 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<*>
에서 클래스를 상속할 때 , , RecyclerView.Adapter세 가지 메서드를 재정의해야 합니다 . - 여기서는 클래스를 초기화합니다 . — 이 방법은 목록 크기를 담당합니다. - 여기서는 목록 요소를 클래스에 전달합니다 . 하나의 요소 유형을 가진 일반 목록의 경우 이것으로 충분합니다. 그러나 다른 유형의 경우 여전히 메소드를 재정의해야 합니다 (이를 위해 어댑터 클래스의 최상위에 있는 상수를 사용합니다. Java에서는 이를 위해 변수를 사용할 수 있습니다). 또한 Java에서는 표현식 대신 일반 . 그리고 마지막으로 메인 클래스인 으로 넘어가겠습니다 . onCreateViewHoldergetItemCountonBindViewHolderonCreateViewHolderViewHoldergetItemCountonBindViewHolderViewHoldergetItemViewTypefinalonBindViewHolderwhenifMainActivity
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이것이 우리의 and 가 초기화되는 곳입니다 ListAdapter. 이것은 많은 사람들이 접했던 꽤 상용구 코드입니다. 이 방법에서는 stubData목록을 데이터(보시다시피 다양한 요소가 포함된 데이터)로 채우고 이 목록을 어댑터에 전달했습니다. 다음으로 애플리케이션을 실행하면 화면에 다음과 같은 내용이 표시됩니다. 복잡한 목록을 쉽게 만들어 보세요 - 6보시다시피 하나의 목록에 다양한 요소가 있는데 이것이 바로 우리의 목표였습니다. 추신: Extension. 다음과 같이 보입니다.
//это расширение для класса 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)
이것은 클래스가 아니라 파일이므로 내부에 이름이 없습니다. inflate하지만 이제 긴 구조 대신 간단히 어댑터에서 사용할 수 있습니다 . 아쉽게도 Java에서는 제공되지 않으므로 LayoutInflater.from(context).inflate. 그게 다야. 다음 시간까지 :) GitHub 링크
코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION