[JAVA] Anmerkung: Kotlin + Raum + RecyclerView + Datenbindung (Teil 2)

Hinweise zur Datenbindung mit Kotlin und Recycler View w/ LiveData, DiffUtil

Vom letzten Mal (Memo: Kotlin + Room + RecyclerView + DataBinding) Es wurden verschiedene Unterstützungsbibliotheken hinzugefügt, daher werde ich es erneut schreiben.

Umgebung

Library Version -
AndroidStudio 3.2 Beta 2
Kotlin 1.2.51
Room 1.0.0 not use
RxJava2 2.1.16
Dagger2 2.15
SupportLibrary 28.0.0-alpha3

Was du machen willst

Quelle & Anmerkungen

DI-Teil wird auch dieses Mal weggelassen Da Dao (Raum) und Repository nicht erstellt werden, wird nur die Schnittstelle verwendet.

Repository Nur ein Repository. Ich denke, es ist nicht viel anders als Dao.

BookmarkRepository.kt


interface BookmarkRepository{
    fun bookmark(bookmarkId: String): Single<Bookmark>
    fun bookmarks(userId: String): Flowable<List<Bookmark>>
    fun store(bookmark: Bookmark): Completable
}

Usecase Usecase zum Abrufen ungelesener Bücher aus dem Repository

class GetReadingAndUnreadBooks(private val repository: BookmarkRepository){

    fun execute(params: Params): Flowable<List<Bookmark>> =
        repository.bookmarks(params.userId)
                .map { it.filter { isReading(it) }
                        .sortedBy { it.state().sort }
                        .sortedByDescending { it.latestDate() }
                }
                .distinctUntilChanged()

    private fun isReading(bookmark: Bookmark): Boolean =
            when (bookmark.state()) {
                ReadState.Unread -> true
                ReadState.Reading -> true
                else -> false
            }

    class Params(val userId: String)
}

ViewModel

Injizieren Sie das Repository. Holen Sie sich ungelesene Lesezeichen als LiveData mit uncpletedItems (). Sie können eine Liste der Lesezeichen von Room with Flowable abrufen, indem Sie "load ()" aufrufen. Wenn die Lesezeichenliste aktualisiert wird, werden Sie automatisch benachrichtigt, wenn Sie unvollständige Elemente () abonniert haben.

BookstackViewModel.kt


class BookstackViewModel @Inject constructor(private val bookmarkRepository: BookmarkRepository) : ViewModel() {

    private val usecase =  GetReadingAndUnreadBooks(bookmarkRepository)

    val userId = MutableLiveData<String>()

    private val _uncompletedItems = MutableLiveData<List<Bookmark>>()

    fun uncompletedItems() = _uncompletedItems as LiveData<List<Bookmark>>

    fun load(): Disposable {
        val id = userId.value?: throw IllegalStateException("User id is must be not null.")
        return usecase.execute(GetReadingAndUnreadBooks.Params(id))
                .subscribeOn(Schedulers.computation())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe {
                    _uncompletedItems.postValue(it)
                }
    }
}

Dies ist nicht mit Room verbunden. Eigenschaften (text, idtext), die hauptsächlich die Daten von Listenelementen anzeigen, Es verfügt über eine Methode, die die Interaktion von Listenelementen empfängt. (update ()) Vorerst habe ich die Zeit aktualisiert, als ich auf einen Artikel geklickt habe.

UnreadItemViewModel.kt


class UnreadItemViewModel: ViewModel() {

    val text = MutableLiveData<String>()
    val idText = MutableLiveData<String>()

    private val item = MutableLiveData<Bookmark>().apply {
        this.observeForever {
            it?.apply {
                text.postValue(this.comment)
                idText.postValue(this.id)
            }
        }
    }
    fun setBookmark(bookmark: Bookmark) {
        item.postValue(bookmark)
    }
    fun bookmark() = item as LiveData<Bookmark>
    fun update() {
        item.value?.copy(comment =  SimpleDateFormat("yyyy/mm/dd HH:mm:ss").format(Date()))?.apply {
            item.postValue(this)
        }

    }
}

RecyclerViewAdapter

Diesmal ist Kimo. Abonnieren Sie das oben erstellte "BookstackViewModel.uncompletedItems ()". Zu diesem Zeitpunkt wird AppCompatActivity im Konstruktor angegeben, um "LifecycleOwner" oder "Lifecycle" zu übergeben.

Rufen Sie update () auf, wenn die Daten benachrichtigt werden. In update wird DiffUtil verwendet, um die Änderung zu benachrichtigen. LiveData ist eine Aktualisierungsbenachrichtigung. Wenn Sie also denselben Wert festlegen, werden Sie über den festgelegten Betrag benachrichtigt. Da das Element nur aktualisiert werden soll, wenn der Inhalt jedes Elements geändert wird, verwenden Sie DiffUtil, um ViewHolder nur zu binden, wenn der Inhalt geändert wird.

BookstackRecyclerViewAdapter.kt


class BookstackRecyclerViewAdapter(val context: AppCompatActivity, viewModel: BookstackViewModel): RecyclerView.Adapter<BookstackRecyclerViewAdapter.ViewHolder>() {

    init {
        viewModel.uncompletedItems().observe({ context.lifecycle }, {it?.apply {
            update(this)
        }})
    }

    private lateinit var recyclerView: RecyclerView

    private var items: MutableList<Bookmark> = arrayListOf()

    override fun onAttachedToRecyclerView(recyclerView: RecyclerView) {
        super.onAttachedToRecyclerView(recyclerView)
        this.recyclerView = recyclerView
    }

    private fun update(list: List<Bookmark>) {
        val adapter = recyclerView.adapter as BookstackRecyclerViewAdapter
        val diff = DiffUtil.calculateDiff(DiffCallback(adapter.items, list))
        diff.dispatchUpdatesTo(adapter)
        this.items.clear()
        this.items.addAll(list)
    }

    override fun getItemCount() = this.items.size

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        val vm = UnreadItemViewModel()
        vm.setBookmark(items[position])
        holder.binding.vm = vm
    }

    override fun onCreateViewHolder(parent: ViewGroup, position: Int): ViewHolder {
        val inflater = LayoutInflater.from(parent.context)
        val binding: ItemUnreadBookBinding = DataBindingUtil.inflate(inflater, R.layout.item_unread_book, parent, false)
        binding.setLifecycleOwner(context)
        return ViewHolder(binding)
    }

    class ViewHolder(val binding: ItemUnreadBookBinding): RecyclerView.ViewHolder(binding.root)

    class DiffCallback(private val oldList: List<Bookmark>, private val newList: List<Bookmark>): DiffUtil.Callback() {

        override fun areContentsTheSame(oldPosition: Int, newPosition: Int) = oldList[oldPosition] == (newList[newPosition])

        override fun areItemsTheSame(oldPosition: Int, newPosition: Int) = oldList[oldPosition].id == (newList[newPosition]).id

        override fun getNewListSize() = newList.size

        override fun getOldListSize() = oldList.size
    }
}

Activity Kürzung

Fragment In Fragment ist Adapter für RecyclerView festgelegt. Darüber hinaus wurde die Liste der ungelesenen Bücher für ViewModel erstellt.

BookstackFragment.kt


class BookstackFragment : Fragment(), Injectable {
   private lateinit var binding: BookstackFragmentBinding

    //Abkürzung

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                              savedInstanceState: Bundle?): View? {
        
        this.binding = DataBindingUtil.inflate(inflater, R.layout.bookstack_fragment, container, false)
        this.binding.setLifecycleOwner(this.activity as AppCompatActivity)
        this.binding.list.adapter = BookstackRecyclerViewAdapter(this.activity as AppCompatActivity, viewModel)
        this.binding.list.layoutManager = LinearLayoutManager(context)
        this.binding.list.setHasFixedSize(true)
        
        return binding.root
    }

    override fun onStart() {
        super.onStart()
        disposable = viewModel.load()
    }

    override fun onStop() {
        super.onStop()
        disposable?.apply {
            if (isDisposed) return
            dispose()
        }
    }
    //Abkürzung
}

Bewegung

croped.gif

Frage

Dinge die zu tun sind

das ist alles.

Recommended Posts

Anmerkung: Kotlin + Raum + RecyclerView + Datenbindung (Teil 2)
[Memo] JSUG-Studiengruppe 2020 Teil 1 Frühling x Kotlin
Kotlin Memorandum-Code Edition (Teil 1)