web-dev-qa-db-ja.com

LiveDataのリストにアイテムが追加されたときにオブザーバーに通知する

LiveDataのリストにアイテムが追加されると、Observerイベントを取得する必要があります。しかし、私が理解している限りでは、古いリストを新しいリストに置き換えたときにのみイベントが受信されます。たとえば、次のことをするとき:

list.value = mutableListOf(IssuePost(UserEntity(name, email, photoUrl), issueEntity))

オブザーバーはイベントを取得します。しかし、値にアイテムを追加するだけでは、Observerは沈黙しています。必要なものを実装する方法を教えてください。

26

内部的に、LiveDataは各変更をバージョン番号(intとして保存されている単純なカウンター)として追跡します。 setValue()を呼び出すと、このバージョンがインクリメントされ、すべてのオブザーバーが新しいデータで更新されます(オブザーバーのバージョン番号がLiveDataのバージョンよりも小さい場合のみ)。

このプロセスを開始する唯一の方法は、setValue()またはpostValue()を呼び出すことです。副作用は、LiveDataの基になるデータ構造が変更された場合(コレクションへの要素の追加など)、これをオブザーバーに通知することは何も起こりません。

したがって、リストに項目を追加した後にsetValue()を呼び出す必要があります。これにアプローチするための2つの方法を以下に示しました。

オプション1

リストをLiveDataの外部に保持し、リストの内容が変更されるたびに参照で更新します。

private val mIssuePosts = ArrayList<IssuePost>()
private val mIssuePostLiveData = MutableLiveData<List<IssuePost>>()

fun addIssuePost(issuePost: IssuePost) {
   mIssuePosts.add(issuePost)
   mIssuePostLiveData.value = mIssuePosts
}

オプション2

LiveDataを介してリストを追跡し、リストの内容が変更されるたびにLiveDataを独自の値で更新します。

private val mIssuePostLiveData = MutableLiveData<List<IssuePost>>()

init {
   mIssuePostLiveData.value = ArrayList()
}

fun addIssuePost(issuePost: IssuePost) {
    mIssuePostLiveData.value?.add(issuePost)
    mIssuePostLiveData.value = mIssuePostLiveData.value
}

これらのソリューションのいずれかを使用すると、オブザーバーに通知するためだけに現在のリストを変更するたびに新しいリストを作成する必要がなくなります。

更新:

GnzltがKotlin拡張関数を使用してLiveDataをそれ自体に割り当ててコードを簡素化するという彼の答えで言及しているように、私はしばらくの間、同様の手法を使用しています。これは基本的にオプション2の自動化です:)それをお勧めします。

29
Programmer001

Kotlin Extension Functionを使用して簡単にしています:

fun <T> MutableLiveData<T>.notifyObserver() {
    this.value = this.value
}

次に、次のようにMutableLiveDataで使用します。

fun addIssuePost(issuePost: IssuePost) {
    mIssuePostLiveData.value?.add(issuePost)
    mIssuePostLiveData.notifyObserver()
}
24
Gnzlt

これはどう?

public class ListLiveData<T> extends LiveData<List<T>> {
    public void addAll(List<T> items) {
        if (getValue() != null && items != null) {
            getValue().addAll(items);
            setValue(getValue());
        }
    }

    public void clear() {
        if (getValue() != null) {
            getValue().clear();
            setValue(getValue());
        }
    }

    @Override public void setValue(List<T> value) {
        super.setValue(value);
    }

    @Nullable @Override public List<T> getValue() {
        return super.getValue();
    }
}

// add changed listener
    mMessageList.observe(mActivity, new Observer() {
        @Override public void onChanged(@Nullable Object o) {
            notifyDataSetChanged();
        }
    });
2
user3682351

@ user3682351に触発されたのが、私のソリューションです。 LiveData値のプロパティを個別に更新することはできないため、操作ごとに値を更新する必要があるようです。これは本質的に、HashMapのプロパティを変更するための便利なメソッドを備えたライブデータの小さなラッパーです。

import androidx.lifecycle.LiveData

/**
 * Hash Map Live Data
 *
 * Some convenience methods around live data for HashMaps. Putting a value on this will also update the entire live data
 * as well
 */
class HashMapLiveData<K, V> : LiveData<HashMap<K, V>>() {

    /**
     * Put a new value into this HashMap and update the value of this live data
     * @param k the key
     * @param v the value
     */
    fun put(k: K, v: V) {
        val oldData = value
        value = if (oldData == null) {
            hashMapOf(k to v)
        } else {
            oldData.put(k, v)
            oldData
        }
    }

    /**
     * Add the contents to the HashMap if there is one existing, otherwise set the value to this HashMap and update the
     * value of this live data
     * @param newData the HashMap of values to add
     */
    fun putAll(newData: HashMap<K, V>) {
        val oldData = value
        value = if (oldData != null) {
            oldData.putAll(newData)
            oldData
        } else {
            newData
        }
    }

    /**
     * Remove a key value pair from this HashMap and update the value of this live data
     * @param key the key to remove
     */
    fun remove(key: K) {
        val oldData = value
        if (oldData != null) {
            oldData.remove(key)
            value = oldData
        }
    }

    /**
     * Clear all data from the backing HashMap and update the value of this live data
     */
    fun clear() {
        val oldData = value
        if (oldData != null) {
            oldData.clear()
            value = oldData
        }
    }

    var value: HashMap<K, V>?
        set(value) = super.setValue(value)
        get() = super.getValue()

}

0
William Reed

私は同じ問題にぶつかり、「value = value」または別のメソッドへの呼び出しをどこにでも追加するのは非常に苦痛であり、非常にエラーが発生しやすいと判断しました。

そのため、コレクションをラップする独自のクラスを作成しました。 Observeは、コレクション自体ではなく、コレクションのコンテンツを監視します。

コードは、GitHubの ObservableCollections で入手できます。

これは最初のバージョンなので、問題を許してください。 gradle includeではまだ利用できないため、ダウンロードしてライブラリモジュールとしてアプリケーションに追加する必要があります。

現在、次のコレクションが含まれています。

ArrayDeque
ArrayList
LinkedList
Queue
Stack
Vector

時間が許す限り、さらに追加されます。特定のものをリクエストしてください。

そして、問題を報告してください。

0
theblitz