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.
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 |
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
}
das ist alles.