質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
Android

Androidは、Google社が開発したスマートフォンやタブレットなど携帯端末向けのプラットフォームです。 カーネル・ミドルウェア・ユーザーインターフェイス・ウェブブラウザ・電話帳などのアプリケーションやソフトウェアをひとつにまとめて構成。 カーネル・ライブラリ・ランタイムはほとんどがC言語/C++、アプリケーションなどはJavaSEのサブセットとAndroid環境で書かれています。

Kotlin

Kotlinは、ジェットブレインズ社のアンドリー・ブレスラフ、ドミトリー・ジェメロフが開発した、 静的型付けのオブジェクト指向プログラミング言語です。

Q&A

0回答

2634閲覧

[RecyclerView] DBの更新による並び替えの挙動

iMASAKI

総合スコア12

Android

Androidは、Google社が開発したスマートフォンやタブレットなど携帯端末向けのプラットフォームです。 カーネル・ミドルウェア・ユーザーインターフェイス・ウェブブラウザ・電話帳などのアプリケーションやソフトウェアをひとつにまとめて構成。 カーネル・ライブラリ・ランタイムはほとんどがC言語/C++、アプリケーションなどはJavaSEのサブセットとAndroid環境で書かれています。

Kotlin

Kotlinは、ジェットブレインズ社のアンドリー・ブレスラフ、ドミトリー・ジェメロフが開発した、 静的型付けのオブジェクト指向プログラミング言語です。

0グッド

0クリップ

投稿2020/09/03 00:29

編集2020/12/26 05:17

問題になっていること

ItemTouchHelper.SimpleCallbackを用いてRecyclerViewの並び替えの実装をしています。
並び替えをする際にDBと同期するようにしているのですが、DBの更新によるコールバックを受けると、
並び替えが期待するものと違った動作をしてしまいます。

kotlin

1// DBからの更新を受け取り、リストを更新 2Repository.items.observe(viewLifecycleOwner) { 3 adapter.submitList(it) 4}
  • 初期状態

初期状態

  • 並び替え直後

最初のタイルを最後へ移動します。
並び替え直後

  • DBのコールバックによる更新
    DBから並び替えが行われたデータの通知が来ます。
    このデータは(1,2,3,4,0)と正しく並び替えられていて、リストの更新は起きないことが期待されます。

 しかし、実装によって以下のようにリストが勝手に更新されてしまいます。
DBのコールバックによる更新

並び替えの実装

この実装の方法によって問題となっている並び替えが発生しないことがあります。
まず、問題が発生してしまう実装から提示します。
ItemListでItemの順番を管理しています。
ややこしいですが、私はDBにFirestoreを利用していて、どうしてもこのような実装になってしまいます。

kotlin

1// Itemの順番をIDにより管理する 2data class ItemList( 3 val itemIds: List<Int> 4) 5// データの中身 6data class Item( 7 val id: Int, 8 val value: Int 9)

kotlin

1object Repository { 2 private val _items = List(5) { Item(id = it, value = it) } 3 private val _list = ItemList(itemIds = List(5) { it }) 4 5 private val list = MutableLiveData(_list) 6 val items: LiveData<List<Item>> = list.map { list -> 7 // データをlist.itemIdsの順番に並び替える 8 list.itemIds.map { id -> 9 _items.first { it.id == id } 10 } 11 } 12 13 suspend fun reorder(start: Int, end: Int) { 14 delay(500) 15 if (start < end) { 16 for (i in start until end) { 17 Collections.swap(_list.itemIds, i, i + 1) 18 } 19 } else { 20 for (i in start downTo end + 1) { 21 Collections.swap(_list.itemIds, i, i - 1) 22 } 23 } 24 list.value = _list 25 } 26}

次に、問題になっている並び替えが発生しない実装です。
素直にItemをListで保持し、それを並び替えています。

kotlin

1object Repository { 2 private val _items = List(5) { Item(id = it, value = it) } 3 val items: MutableLiveData<List<Item>> = MutableLiveData(_items) 4 5 suspend fun reorder(start: Int, end: Int) { 6 delay(500) 7 if (start < end) { 8 for (i in start until end) { 9 Collections.swap(_items, i, i + 1) 10 } 11 } else { 12 for (i in start downTo end + 1) { 13 Collections.swap(_items, i, i - 1) 14 } 15 } 16 items.value = _items 17 } 18}

RecyclerView周りの実装

kotlin

1class MyDiffUtil : DiffUtil.ItemCallback<Item>() { 2 override fun areItemsTheSame(oldItem: Item, newItem: Item): Boolean { 3 return oldItem.id == newItem.id 4 } 5 6 override fun areContentsTheSame(oldItem: Item, newItem: Item): Boolean { 7 return oldItem == newItem 8 } 9}
class MyViewHolder(private val binding: FragmentFirstBinding) : RecyclerView.ViewHolder(binding.root) { companion object { fun from(parent: ViewGroup): MyViewHolder { val inflater = LayoutInflater.from(parent.context) val binding = FragmentFirstBinding.inflate(inflater, parent, false) return MyViewHolder(binding) } } fun bind(num: Int) { binding.myText.text = num.toString() } }
class MyAdapter : ListAdapter<Item, MyViewHolder>(MyDiffUtil()) { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder { return MyViewHolder.from(parent) } override fun onBindViewHolder(holder: MyViewHolder, position: Int) { return holder.bind(getItem(position).value) } }

ItemTouchHelper

DBの更新頻度を減らすために、並び替え終了時にDBを更新しています

kotlin

1class MyItemTouchHelperCallback( 2 private val callback: Callback 3) : SimpleCallback( 4 UP or DOWN or LEFT or RIGHT, 5 0 6) { 7 private var start: Int? = null 8 private var end: Int? = null 9 10 override fun onMove( 11 recyclerView: RecyclerView, 12 viewHolder: RecyclerView.ViewHolder, 13 target: RecyclerView.ViewHolder 14 ): Boolean { 15 val from = viewHolder.adapterPosition 16 val to = target.adapterPosition 17 18 start = start ?: from 19 end = to 20 21 recyclerView.adapter?.notifyItemMoved(from, to) 22 return true 23 } 24 25 override fun clearView(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) { 26 super.clearView(recyclerView, viewHolder) 27 if (start != null && end != null) 28 callback.reorder(start!!, end!!) 29 30 start = null 31 end = null 32 } 33 34 override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) { 35 } 36 37 interface Callback { 38 fun reorder(start: Int, end: Int) 39 } 40}

kotlin

1class MainFragment : Fragment(), MyItemTouchHelperCallback.Callback { 2 override fun onCreateView( 3 inflater: LayoutInflater, 4 container: ViewGroup?, 5 savedInstanceState: Bundle? 6 ): View? { 7 // ... 8 Repository.items.observe(viewLifecycleOwner) { 9 adapter.submitList(it) 10 } 11 val itemTouchHelper = ItemTouchHelper(MyItemTouchHelperCallback(this)) 12 itemTouchHelper.attachToRecyclerView(binding.listView) 13 14 return binding.root 15 } 16 17 override fun reorder(start: Int, end: Int) { 18 lifecycleScope.launch { 19 Repository.reorder(start, end) 20 } 21 } 22}

回答していただきたいこと

データの本体とデータの順番を分けた実装で、どのようにすれば正しく並び替えができるのかを教えていただきたいです。
また、何故この方法ではうまく行かないのかを教えていただけると助かります。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだ回答がついていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問