Skip to content

Commit

Permalink
better reorder control
Browse files Browse the repository at this point in the history
  • Loading branch information
levinli303 committed Mar 28, 2020
1 parent 9873fa1 commit e5be9e2
Show file tree
Hide file tree
Showing 9 changed files with 119 additions and 79 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ open class SeparatorRecyclerViewAdapter(private val separatorHeight: Int = 1,
val item1 = values[index1]
val item2 = values[index2]
if (item1 is RecyclerViewItem && item2 is RecyclerViewItem && swapItem(item1, item2)) {
values[index2] = item2
values[index1] = item1
values[index1] = item2
values[index2] = item1
notifyItemMoved(index1, index2)
return true
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,20 @@ package space.celestia.mobilecelestia.common

import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.common_text_list_item.view.*
import space.celestia.mobilecelestia.R

public class CommonTextViewHolder(parent: ViewGroup):
RecyclerView.ViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.common_text_list_item, parent, false)) {
public val title = itemView.title
public val detail = itemView.detail
public var accessory = itemView.accessory
interface BaseTextItemHolder {
val title: TextView
val accessory: ImageView
}

class CommonTextViewHolder(parent: ViewGroup):
RecyclerView.ViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.common_text_list_item, parent, false)), BaseTextItemHolder {
override val title = itemView.title
override var accessory = itemView.accessory
val detail = itemView.detail
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ class FavoriteItemFragment : TitledFragment() {
private var listener: Listener? = null

var favoriteItem: FavoriteBaseItem? = null
private val listAdapter by lazy { FavoriteItemRecyclerViewAdapter(favoriteItem!!, listener) }
private val itemHelper by lazy { ItemTouchHelper(FavoriteItemItemTouchCallback()) }
private val listAdapter by lazy { FavoriteItemRecyclerViewAdapter(favoriteItem!!, listener, this.itemHelper) }

override val title: String
get() = favoriteItem!!.title
Expand All @@ -40,7 +41,7 @@ class FavoriteItemFragment : TitledFragment() {
with(view) {
layoutManager = LinearLayoutManager(context)
adapter = listAdapter
ItemTouchHelper(FavoriteItemItemTouchCallback(listAdapter)).attachToRecyclerView(this)
itemHelper.attachToRecyclerView(this)
}
}
return view
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
package space.celestia.mobilecelestia.favorite

import android.view.LayoutInflater
import android.view.Menu
import android.view.View
import android.view.ViewGroup
import android.widget.PopupMenu
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.RecyclerView
import space.celestia.mobilecelestia.common.CommonSectionV2
import space.celestia.mobilecelestia.common.CommonTextViewHolder
import space.celestia.mobilecelestia.common.RecyclerViewItem
import space.celestia.mobilecelestia.common.SeparatorHeaderRecyclerViewAdapter
import kotlinx.android.synthetic.main.common_reorderable_text_list_item.view.*
import space.celestia.mobilecelestia.R
import space.celestia.mobilecelestia.common.*
import space.celestia.mobilecelestia.core.CelestiaScript
import space.celestia.mobilecelestia.favorite.FavoriteItemFragment.Listener
import space.celestia.mobilecelestia.utils.CelestiaString
Expand All @@ -23,11 +23,12 @@ interface FavoriteBaseItem : RecyclerViewItem, Serializable {
val children: List<FavoriteBaseItem>
val isLeaf: Boolean
val title: String
}

interface MutableFavoriteBaseItem : FavoriteBaseItem {
val supportedItemActions: List<FavoriteItemAction>
get() = listOf()
}

interface MutableFavoriteBaseItem : FavoriteBaseItem {
fun insert(newItem: FavoriteBaseItem, index: Int)

fun append(newItem: FavoriteBaseItem) {
Expand All @@ -48,7 +49,7 @@ class FavoriteRoot : FavoriteBaseItem {
override val children: List<FavoriteBaseItem>
get() = listOf(
FavoriteTypeItem(FavoriteType.Script),
FavoriteBookmarkItem(currentBookmarkRoot)
FavoriteBookmarkRootItem(currentBookmarkRoot)
)
override val title: String
get() = CelestiaString("Favorites", "")
Expand Down Expand Up @@ -80,7 +81,7 @@ class FavoriteScriptItem(val script: CelestiaScript) : FavoriteBaseItem {
get() = true
}

class FavoriteBookmarkItem(val bookmark: BookmarkNode) : MutableFavoriteBaseItem {
open class FavoriteBookmarkItem(val bookmark: BookmarkNode) : MutableFavoriteBaseItem {
override val children: List<FavoriteBaseItem>
get() = if (bookmark.isLeaf) listOf() else bookmark.children!!.map { FavoriteBookmarkItem(it) }
override val title: String
Expand Down Expand Up @@ -112,6 +113,12 @@ class FavoriteBookmarkItem(val bookmark: BookmarkNode) : MutableFavoriteBaseItem
}
}

class FavoriteBookmarkRootItem(bookmark: BookmarkNode) : FavoriteBookmarkItem(bookmark) {
// Root item does not support any customization
override val supportedItemActions: List<FavoriteItemAction>
get() = listOf()
}

fun updateCurrentScripts(scripts: List<CelestiaScript>) {
currentScripts = scripts
}
Expand All @@ -131,10 +138,11 @@ private var currentBookmarkRoot: BookmarkNode = BookmarkNode("Bookmarks", "", ar
class FavoriteItemRecyclerViewAdapter private constructor(
private val item: FavoriteBaseItem,
private var children: List<FavoriteBaseItem>,
private val listener: Listener?
private val listener: Listener?,
private val helper: ItemTouchHelper
) : SeparatorHeaderRecyclerViewAdapter(listOf(CommonSectionV2(children))) {

constructor(item: FavoriteBaseItem, listener: Listener?) : this(item, item.children, listener)
constructor(item: FavoriteBaseItem, listener: Listener?, helper: ItemTouchHelper) : this(item, item.children, listener, helper)

val editable: Boolean
get() = item is MutableFavoriteBaseItem
Expand All @@ -146,49 +154,48 @@ class FavoriteItemRecyclerViewAdapter private constructor(
}

override fun itemViewType(item: RecyclerViewItem): Int {
if (item is FavoriteTypeItem) {
return FAVORITE_TYPE
}
if (item is FavoriteScriptItem) {
return FAVORITE_SCRIPT
}
if (item is FavoriteBookmarkItem) {
return FAVORITE_BOOKMARK

if (item is FavoriteBaseItem) {
return if (editable) FAVORITE_EDITABLE else FAVORITE_CONST
}
return super.itemViewType(item)
}

override fun createVH(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
if (viewType == FAVORITE_TYPE || viewType == FAVORITE_SCRIPT || viewType == FAVORITE_BOOKMARK) {
if (viewType == FAVORITE_CONST) {
return CommonTextViewHolder(parent)
}
if (viewType == FAVORITE_EDITABLE) {
return CommonReorderableTextViewHolder(parent)
}
return super.createVH(parent, viewType)
}

override fun bindVH(holder: RecyclerView.ViewHolder, item: RecyclerViewItem) {
if (holder is CommonTextViewHolder && item is FavoriteBaseItem) {
if (holder is BaseTextItemHolder && item is FavoriteBaseItem) {
holder.title.text = item.title
holder.accessory.visibility = if (item.isLeaf) View.GONE else View.VISIBLE
if (holder is CommonReorderableTextViewHolder) {
// Can reorder
holder.dragView.setOnLongClickListener {
helper.startDrag(holder)
return@setOnLongClickListener true
}
}
if (item is MutableFavoriteBaseItem && item.supportedItemActions.isNotEmpty()) {
val actions = item.supportedItemActions
holder.itemView.setOnLongClickListener {
val popup = PopupMenu(it.context, it)
for (i in actions.indices) {
val action = actions[i]
popup.menu.add(Menu.NONE, i, Menu.NONE, CelestiaString(action.toString(), ""))
}
popup.setOnMenuItemClickListener { menuItem ->
when (actions[menuItem.itemId]) {
setupPopupMenu(popup, actions) { menuItem ->
when (menuItem) {
FavoriteItemAction.Delete -> {
listener?.deleteFavoriteItem(children.indexOf(item))
}
FavoriteItemAction.Rename -> {
listener?.renameFavoriteItem(item)
}
}
return@setOnMenuItemClickListener true
}
popup.show()
return@setOnLongClickListener true
}
} else {
Expand All @@ -199,6 +206,18 @@ class FavoriteItemRecyclerViewAdapter private constructor(
super.bindVH(holder, item)
}

private fun setupPopupMenu(menu: PopupMenu, actions: List<FavoriteItemAction>, handler: (FavoriteItemAction) -> Unit) {
for (i in actions.indices) {
val action = actions[i]
menu.menu.add(Menu.NONE, i, Menu.NONE, CelestiaString(action.toString(), ""))
}
menu.setOnMenuItemClickListener { menuItem ->
handler(actions[menuItem.itemId])
return@setOnMenuItemClickListener true
}
menu.show()
}

override fun swapItem(item1: RecyclerViewItem, item2: RecyclerViewItem): Boolean {
val index1 = children.indexOf(item1)
val index2 = children.indexOf(item2)
Expand All @@ -214,20 +233,27 @@ class FavoriteItemRecyclerViewAdapter private constructor(
updateSectionsWithHeader(listOf(CommonSectionV2(children)))
}

inner class CommonReorderableTextViewHolder(parent: ViewGroup):
RecyclerView.ViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.common_reorderable_text_list_item, parent, false)), BaseTextItemHolder {
override val title = itemView.title
override var accessory = itemView.accessory
val dragView = itemView.drag_accessory
}

private companion object {
const val FAVORITE_TYPE = 0
const val FAVORITE_SCRIPT = 1
const val FAVORITE_BOOKMARK = 2
const val FAVORITE_CONST = 0
const val FAVORITE_EDITABLE = 1
}
}

class FavoriteItemItemTouchCallback(val adapter: FavoriteItemRecyclerViewAdapter):
class FavoriteItemItemTouchCallback():
ItemTouchHelper.Callback() {
override fun getMovementFlags(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder
): Int {
val dragFlags = if (adapter.editable && viewHolder is CommonTextViewHolder) ItemTouchHelper.UP or ItemTouchHelper.DOWN else 0
val adapter = recyclerView.adapter as FavoriteItemRecyclerViewAdapter
val dragFlags = if (adapter.editable && viewHolder is BaseTextItemHolder) ItemTouchHelper.UP or ItemTouchHelper.DOWN else 0
return makeMovementFlags(dragFlags, 0)
}

Expand All @@ -236,7 +262,8 @@ class FavoriteItemItemTouchCallback(val adapter: FavoriteItemRecyclerViewAdapter
viewHolder: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder
): Boolean {
if (viewHolder is CommonTextViewHolder && target is CommonTextViewHolder) {
if (viewHolder is BaseTextItemHolder && target is BaseTextItemHolder) {
val adapter = recyclerView.adapter as FavoriteItemRecyclerViewAdapter
val fromPos = viewHolder.adapterPosition
val toPos = target.adapterPosition
return adapter.swapItem(fromPos, toPos)
Expand All @@ -247,4 +274,5 @@ class FavoriteItemItemTouchCallback(val adapter: FavoriteItemRecyclerViewAdapter
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {}

override fun isItemViewSwipeEnabled(): Boolean { return false }
override fun isLongPressDragEnabled(): Boolean { return false }
}
Binary file added app/src/main/res/drawable-mdpi/ic_dehaze.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/src/main/res/drawable-xhdpi/ic_dehaze.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/src/main/res/drawable-xxhdpi/ic_dehaze.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
40 changes: 40 additions & 0 deletions app/src/main/res/layout/common_reorderable_text_list_item.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="44dp"
android:orientation="horizontal"
android:clickable="true"
android:focusable="true"
style="@style/ListItemGrouped">

<TextView
android:id="@+id/title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_weight="1"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:textAppearance="?attr/textAppearanceListItem"
android:lines="1"
style="@style/PrimaryLabel"/>

<ImageView
android:id="@+id/accessory"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:src="@drawable/accessory_full_disclosure"
android:layout_marginEnd="16dp"
android:visibility="gone"
style="@style/Accessory"/>

<ImageView
android:id="@+id/drag_accessory"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:src="@drawable/ic_dehaze"
android:layout_marginEnd="16dp"
style="@style/Accessory"/>
</LinearLayout>
36 changes: 0 additions & 36 deletions app/src/main/res/layout/common_swipeable_text_list_item.xml

This file was deleted.

0 comments on commit e5be9e2

Please sign in to comment.