Przeglądaj źródła

Refactoring of recycler view adapters

Vadik Sirekanyan 7 lat temu
rodzic
commit
1b3d3b3db1

+ 0 - 53
app/src/main/java/com/sirekanyan/knigopis/common/Adapter.kt

@@ -1,53 +0,0 @@
-package com.sirekanyan.knigopis.common
-
-import android.support.annotation.IdRes
-import android.support.v7.widget.RecyclerView
-import android.view.View
-import android.view.ViewGroup
-import com.sirekanyan.knigopis.common.extensions.inflate
-
-class Adapter<T>(
-    private val items: List<T>,
-    private val layout: (T) -> Int
-) {
-
-    val binders = mutableMapOf<@IdRes Int, (View, Int) -> Unit>()
-    @Suppress("MemberVisibilityCanPrivate")
-    val recyclerViewAdapter = object : RecyclerView.Adapter<ViewsHolder>() {
-        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
-            parent.inflate(viewType).let { rootView ->
-                ViewsHolder(rootView, binders.mapValues { (key, _) ->
-                    rootView.findViewById<View>(key)
-                })
-            }
-
-        override fun onBindViewHolder(holder: ViewsHolder, position: Int) =
-            binders.forEach { (id, binder) ->
-                holder.views[id]?.let { view ->
-                    binder(view, position)
-                }
-            }
-
-        override fun getItemCount() = items.size
-
-        override fun getItemViewType(position: Int) = layout(items[position])
-    }
-
-    inline fun <reified V : View> bind(@IdRes id: Int, crossinline binder: V.(Int) -> Unit): Adapter<T> {
-        binders[id] = { view, position ->
-            binder(view as V, position)
-        }
-        return this
-    }
-
-    inline fun <reified V : View> bind2(@IdRes id: Int, crossinline binder: V.(Int, RecyclerView.Adapter<ViewsHolder>) -> Unit): Adapter<T> {
-        binders[id] = { view, position ->
-            binder(view as V, position, recyclerViewAdapter)
-        }
-        return this
-    }
-
-    fun get() = recyclerViewAdapter
-}
-
-class ViewsHolder(rootView: View, val views: Map<Int, View>) : RecyclerView.ViewHolder(rootView)

+ 0 - 49
app/src/main/java/com/sirekanyan/knigopis/common/adapter/AbstractBooksAdapter.kt

@@ -1,49 +0,0 @@
-package com.sirekanyan.knigopis.common.adapter
-
-import android.support.annotation.LayoutRes
-import android.support.v7.widget.RecyclerView
-import android.view.ViewGroup
-import com.sirekanyan.knigopis.common.extensions.inflate
-import com.sirekanyan.knigopis.repository.model.Book
-import com.sirekanyan.knigopis.repository.model.BookHeader
-import com.sirekanyan.knigopis.repository.model.FinishedBook
-
-private const val HEADER_TYPE = 0
-private const val ITEM_TYPE = 1
-
-abstract class AbstractBooksAdapter(
-    private val elements: List<Book>,
-    @LayoutRes private val headerLayout: Int,
-    @LayoutRes private val itemLayout: Int
-) : RecyclerView.Adapter<BookViewHolder>() {
-
-    abstract fun bindHeaderViewHolder(holder: BookHeaderViewHolder, header: BookHeader, i: Int)
-
-    abstract fun bindItemViewHolder(holder: BookItemViewHolder, book: FinishedBook)
-
-    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
-        if (viewType == HEADER_TYPE) {
-            BookHeaderViewHolder(parent.inflate(headerLayout))
-        } else {
-            BookItemViewHolder(parent.inflate(itemLayout))
-        }
-
-    override fun onBindViewHolder(holder: BookViewHolder, position: Int) {
-        val element = elements[position]
-        when (holder) {
-            is BookHeaderViewHolder -> bindHeaderViewHolder(holder, element as BookHeader, position)
-            is BookItemViewHolder -> bindItemViewHolder(holder, element as FinishedBook)
-        }
-    }
-
-    override fun getItemCount() =
-        elements.size
-
-    override fun getItemViewType(position: Int) =
-        if (elements[position] is BookHeader) {
-            HEADER_TYPE
-        } else {
-            ITEM_TYPE
-        }
-
-}

+ 0 - 64
app/src/main/java/com/sirekanyan/knigopis/common/adapter/BookViewHolder.kt

@@ -1,64 +0,0 @@
-package com.sirekanyan.knigopis.common.adapter
-
-import android.support.annotation.StringRes
-import android.support.v7.widget.RecyclerView
-import android.view.View
-import com.sirekanyan.knigopis.R
-import com.sirekanyan.knigopis.common.extensions.setSquareImage
-import com.sirekanyan.knigopis.common.extensions.showNow
-import kotlinx.android.synthetic.main.header.view.*
-import kotlinx.android.synthetic.main.user_book.view.*
-
-sealed class BookViewHolder(view: View) : RecyclerView.ViewHolder(view)
-
-class BookHeaderViewHolder(private val view: View) : BookViewHolder(view) {
-
-    private val context = view.context
-
-    fun setTitle(title: String) {
-        view.book_title.text = title
-    }
-
-    fun setTitle(@StringRes titleRes: Int) {
-        view.book_title.text = context.getString(titleRes)
-    }
-
-    fun setBooksCount(count: Int) {
-        view.books_count.text = context.resources.getQuantityString(
-            R.plurals.common_header_books,
-            count,
-            count
-        )
-        view.books_count.showNow()
-    }
-
-    fun showTopDivider(visible: Boolean) {
-        view.header_divider.showNow(visible)
-    }
-
-}
-
-class BookItemViewHolder(private val view: View) : BookViewHolder(view) {
-
-    fun setTitle(title: String) {
-        view.bookTitle.text = title
-    }
-
-    fun setAuthor(author: String) {
-        view.bookAuthor.text = author
-    }
-
-    fun setNotes(notes: String) {
-        view.bookNotes.showNow(notes.isNotEmpty())
-        view.bookNotes.text = notes
-    }
-
-    fun setOnLongClick(onLongClick: (View) -> Boolean) {
-        view.setOnLongClickListener(onLongClick)
-    }
-
-    fun setBookImageUrl(url: String?) {
-        view.bookImage.setSquareImage(url)
-    }
-
-}

+ 14 - 120
app/src/main/java/com/sirekanyan/knigopis/feature/books/BooksAdapter.kt

@@ -1,127 +1,21 @@
 package com.sirekanyan.knigopis.feature.books
 
-import android.app.AlertDialog
-import android.view.View
-import android.widget.ImageView
-import android.widget.ProgressBar
-import android.widget.TextView
+import android.view.ViewGroup
 import com.sirekanyan.knigopis.R
-import com.sirekanyan.knigopis.Router
-import com.sirekanyan.knigopis.common.Adapter
-import com.sirekanyan.knigopis.common.extensions.hideNow
-import com.sirekanyan.knigopis.common.extensions.setSquareImage
-import com.sirekanyan.knigopis.common.extensions.showNow
-import com.sirekanyan.knigopis.common.extensions.toast
-import com.sirekanyan.knigopis.common.io2main
-import com.sirekanyan.knigopis.common.logError
-import com.sirekanyan.knigopis.common.setProgressSmoothly
-import com.sirekanyan.knigopis.common.view.dialog.DialogFactory
-import com.sirekanyan.knigopis.common.view.dialog.createDialogItem
-import com.sirekanyan.knigopis.repository.KAuth
-import com.sirekanyan.knigopis.repository.api.Endpoint
-import com.sirekanyan.knigopis.repository.model.Book
-import com.sirekanyan.knigopis.repository.model.BookHeader
-import com.sirekanyan.knigopis.repository.model.FinishedBook
-import com.sirekanyan.knigopis.repository.model.PlannedBook
+import com.sirekanyan.knigopis.common.adapter.CommonAdapter
+import com.sirekanyan.knigopis.common.extensions.inflate
+import com.sirekanyan.knigopis.model.BookDataModel
+import com.sirekanyan.knigopis.model.BookModel
 
 class BooksAdapter(
-    private val api: Endpoint,
-    private val auth: KAuth,
-    private val router: Router,
-    private val dialogs: DialogFactory,
-    private val books: MutableList<Book>,
-    private val bookHeaders: MutableList<BookHeader>
-) {
+    private val onClick: (BookDataModel) -> Unit,
+    private val onLongClick: (BookDataModel) -> Unit
+) : CommonAdapter<BookModel>() {
+
+    override fun onCreateHeaderViewHolder(parent: ViewGroup) =
+        BookHeaderViewHolder(parent.inflate(R.layout.header))
+
+    override fun onCreateDataViewHolder(parent: ViewGroup) =
+        BookDataViewHolder(parent.inflate(R.layout.book), onClick, onLongClick)
 
-    fun build() = Adapter(books) {
-        if (it is BookHeader) {
-            R.layout.header
-        } else {
-            R.layout.book
-        }
-    }
-        .bind2<View>(R.id.book_item_container) { bookIndex, adapter ->
-            val onDeleteConfirmed = { book: Book ->
-                val index = books.indexOfFirst { it.id == book.id }
-                if (index >= 0) {
-                    when (book) {
-                        is FinishedBook -> api.deleteFinishedBook(book.id, auth.getAccessToken())
-                        is PlannedBook -> api.deletePlannedBook(book.id, auth.getAccessToken())
-                        else -> throw UnsupportedOperationException()
-                    }
-                        .io2main()
-                        .subscribe({}, {
-                            context.toast(R.string.books_error_delete)
-                            logError("cannot delete finished book", it)
-                        })
-                    books.removeAt(index)
-                    bookHeaders.removeAt(index)
-                    adapter.notifyItemRemoved(index)
-                }
-            }
-            val onDeleteClicked = { book: Book ->
-                AlertDialog.Builder(context)
-                    .setTitle(R.string.books_title_confirm_delete)
-                    .setMessage(
-                        context.getString(
-                            R.string.books_message_confirm_delete,
-                            book.fullTitle
-                        )
-                    )
-                    .setNegativeButton(R.string.common_button_cancel) { d, _ -> d.dismiss() }
-                    .setPositiveButton(R.string.books_button_confirm_delete) { d, _ ->
-                        onDeleteConfirmed(book)
-                        d.dismiss()
-                    }
-                    .show()
-            }
-            val book = books[bookIndex]
-            setOnClickListener {
-                router.openBookScreen(book)
-            }
-            setOnLongClickListener {
-                dialogs.showDialog(
-                    book.fullTitle,
-                    createDialogItem(R.string.books_button_edit, R.drawable.ic_edit) {
-                        router.openBookScreen(book)
-                    },
-                    createDialogItem(R.string.books_button_delete, R.drawable.ic_delete) {
-                        onDeleteClicked(book)
-                    }
-                )
-                true
-            }
-        }
-        .bind<ProgressBar>(R.id.book_progress) {
-            val book = books[it]
-            progress = 0
-            if (book is PlannedBook) {
-                showNow()
-                setProgressSmoothly(book.priority)
-            } else {
-                hideNow()
-            }
-        }
-        .bind<ImageView>(R.id.book_image) {
-            setSquareImage(books[it].bookImageUrl)
-        }
-        .bind<TextView>(R.id.book_title) {
-            text = books[it].titleOrDefault
-        }
-        .bind<View>(R.id.header_divider) {
-            visibility = if (it == 0) View.INVISIBLE else View.VISIBLE
-        }
-        .bind<TextView>(R.id.books_count) {
-            val count = (books[it] as? BookHeader)?.count ?: 0
-            text = resources.getQuantityString(
-                R.plurals.common_header_books,
-                count,
-                count
-            )
-            showNow(count > 0)
-        }
-        .bind<TextView>(R.id.book_author) {
-            text = books[it].authorOrDefault
-        }
-        .get()
 }

+ 8 - 27
app/src/main/java/com/sirekanyan/knigopis/feature/notes/NotesAdapter.kt

@@ -1,40 +1,21 @@
 package com.sirekanyan.knigopis.feature.notes
 
-import android.support.v7.widget.RecyclerView
+import android.support.v7.recyclerview.extensions.ListAdapter
 import android.view.ViewGroup
 import com.sirekanyan.knigopis.R
-import com.sirekanyan.knigopis.Router
+import com.sirekanyan.knigopis.common.adapter.SimpleItemCallback
 import com.sirekanyan.knigopis.common.extensions.inflate
-import com.sirekanyan.knigopis.repository.model.note.Note
+import com.sirekanyan.knigopis.model.NoteModel
 
 class NotesAdapter(
-    private val notes: List<Note>,
-    private val router: Router
-) : RecyclerView.Adapter<NoteViewHolder>() {
+    private val onClick: (NoteModel) -> Unit
+) : ListAdapter<NoteModel, NoteViewHolder>(SimpleItemCallback(NoteModel::id)) {
 
-    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NoteViewHolder {
-        val view = parent.inflate(R.layout.note)
-        return NoteViewHolder(view)
-    }
-
-    override fun getItemCount(): Int {
-        return notes.size
-    }
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
+        NoteViewHolder(parent.inflate(R.layout.note), onClick)
 
     override fun onBindViewHolder(holder: NoteViewHolder, position: Int) {
-        val note = notes[position]
-        val user = note.user
-        val avatar = note.user.avatarUrl
-        holder.setTitle(note.title)
-        holder.setAuthor(note.author)
-        holder.setNotes(note.notes)
-        holder.setTimestamp(note.fixedCreatedAt.time)
-        holder.setNickname(user.name)
-        holder.view.setOnClickListener {
-            router.openUserScreen(user.id, user.name, avatar)
-        }
-        holder.setAvatarUrl(avatar)
-        holder.setBookImageUrl(note.bookImageUrl)
+        holder.setNote(getItem(position))
     }
 
 }

+ 20 - 0
app/src/main/java/com/sirekanyan/knigopis/feature/user/UserBooksAdapter.kt

@@ -0,0 +1,20 @@
+package com.sirekanyan.knigopis.feature.user
+
+import android.view.ViewGroup
+import com.sirekanyan.knigopis.R
+import com.sirekanyan.knigopis.common.adapter.CommonAdapter
+import com.sirekanyan.knigopis.common.extensions.inflate
+import com.sirekanyan.knigopis.model.BookDataModel
+import com.sirekanyan.knigopis.model.BookModel
+
+class UserBooksAdapter(
+    private val onLongClick: (BookDataModel) -> Unit
+) : CommonAdapter<BookModel>() {
+
+    override fun onCreateHeaderViewHolder(parent: ViewGroup) =
+        UserBookHeaderViewHolder(parent.inflate(R.layout.header))
+
+    override fun onCreateDataViewHolder(parent: ViewGroup) =
+        UserBookDataViewHolder(parent.inflate(R.layout.user_book), onLongClick)
+
+}