包含 headers 部分的 RealmBaseAdapter

RealmBaseAdapter with section headers

我正在尝试为 Android 使用 realm 数据存储,并且我正在尝试构建一个应用程序向用户显示 ListView 中的选项列表,有点像用户联系人列表。每个部分(如 A、B、C 等)的首字母应为 header。有没有办法用 RealmBaseAdapter 来实现这个?

目前我正在使用它 ArrayAdapter 并且我只是有一个填充了值的数组,但如果可能的话,我想使用它的适配器从 Realm 中提取数据。我知道在 iOS 中使用 NSFetchedResultsController 非常简单。我们如何将 RealmResults 分成几个部分?

RealmBaseAdapter 不能与 ExpandableListAdapter 一起使用(我假设您正在使用它来创建部分?),所以现在您唯一的选择是创建您自己的实现。但是 RealmResults 也是一个列表,因此它应该可以与 ArrayAdapter 无缝协作。

您还可以在此处查看更多详细信息:https://github.com/realm/realm-java/issues/978

这是我用的:

package com.poterion.android.library.adapters

import android.widget.BaseExpandableListAdapter
import io.realm.*

/**
 * @author Jan Kubovy <jan@kubovy.eu>
 */
abstract class RealmExpandableListAdapter<out Group : Any, Item : RealmModel>(
        private val itemGroupsProvider: (Item) -> Collection<Group?>,
        private val groupsProvider: (Collection<Item>) -> List<Group?>,
        private var adapterData: OrderedRealmCollection<Item>?) : BaseExpandableListAdapter() {
    private val listener: RealmChangeListener<OrderedRealmCollection<Item>>?

    protected val groups: List<Group?>
        get() {
            return adapterData?.takeIf { isDataValid }?.let(groupsProvider) ?: emptyList()
        }

    private val isDataValid: Boolean
        get() = adapterData?.isValid == true

    init {
        if (adapterData?.isManaged == false)
            throw IllegalStateException("Only use this adapter with managed list, for un-managed lists you can just use the BaseAdapter")
        this.listener = RealmChangeListener { notifyDataSetChanged() }
        adapterData?.takeIf { isDataValid }?.also { addListener(it) }
    }

    private fun addListener(data: OrderedRealmCollection<Item>) {
        when (data) {
            is RealmResults<Item> -> data.addChangeListener((listener as RealmChangeListener<RealmResults<Item>>))
            is RealmList<Item> -> data.addChangeListener((listener as RealmChangeListener<RealmList<Item>>))
            else -> throw IllegalArgumentException("RealmCollection not supported: " + data.javaClass)
        }
    }

    private fun removeListener(data: OrderedRealmCollection<Item>) {
        when (data) {
            is RealmResults<Item> -> data.removeChangeListener((listener as RealmChangeListener<RealmResults<Item>>))
            is RealmList<Item> -> data.removeChangeListener((listener as RealmChangeListener<RealmList<Item>>))
            else -> throw IllegalArgumentException("RealmCollection not supported: " + data.javaClass)
        }
    }

    override fun getGroupCount(): Int = groups.size

    override fun getChildrenCount(groupPosition: Int): Int = adapterData?.takeIf { isDataValid }?.let { data ->
        val g = groups[groupPosition]
        data.filter { g == null || groups(it).contains(g) }.size
    } ?: 0

    override fun getGroup(groupPosition: Int): Group? = if (groups.size > groupPosition) groups[groupPosition] else null

    override fun getChild(groupPosition: Int, childPosition: Int): Item? = children(groupPosition)
        .takeIf { it.size > childPosition }?.get(childPosition)

    override fun notifyDataSetChanged() {
        super.notifyDataSetChanged()
    }

    private fun children(groupPosition: Int): List<Item> {
        return getGroup(groupPosition)
                    ?.let { g -> adapterData?.takeIf { isDataValid }?.filter { groups(it).contains(g) } } ?: emptyList()
    }
}

和用法:

class PersonListAdapter(realm: Realm) :
        RealmExpandableListAdapter<String, Person>(
                itemGroupsProvider = { person -> arrayOf(person.group, null) },
                groupsProvider = { people -> people.map { it.group } },
                adapterData = realm.where(Person::class.java)
                        .findAllSortedAsync("lastName", Sort.ASCENDING, "firstName", Sort.ASCENDING)) {

    override fun getGroupId(groupPosition: Int) = getGroup(groupPosition).id

    override fun getChildId(groupPosition: Int, childPosition: Int) = getChild(groupPosition, childPosition).id

    override fun hasStableIds() = true

    override fun getGroupView(groupPosition: Int, isExpanded: Boolean, convertView: View?, parent: ViewGroup?): View {
        // ... Item View here ...
    }

    override fun getChildView(groupPosition: Int, childPosition: Int, isLastChild: Boolean,
                              convertView: View?, parent: ViewGroup?): View {
        // ... Group View here ...
    }

    override fun isChildSelectable(groupPosition: Int, childPosition: Int) = true
}