如何在Android TV上移动网格项目?
我正在为Android TV开发一个网格界面(使用如何在Android TV上移动网格项目?,android,android-recyclerview,leanback,Android,Android Recyclerview,Leanback,我正在为Android TV开发一个网格界面(使用VerticalGridSupportFragment),我正在寻找一种允许用户在网格中移动项目的方法 其想法是网格包含许多电视频道,用户应该能够更改电视频道的顺序(排序/重新排序)。我建议的解决方案是通过单击来选择频道。然后通道变得“粘滞”,允许您移动它。当您对该位置满意时,再次单击频道,确认其新位置 显而易见的解决办法是按照以下思路做一些事情: getVerticalGridView()?.let { it.setOnChildSe
VerticalGridSupportFragment
),我正在寻找一种允许用户在网格中移动项目的方法
其想法是网格包含许多电视频道,用户应该能够更改电视频道的顺序(排序/重新排序)。我建议的解决方案是通过单击来选择频道。然后通道变得“粘滞”,允许您移动它。当您对该位置满意时,再次单击频道,确认其新位置
显而易见的解决办法是按照以下思路做一些事情:
getVerticalGridView()?.let {
it.setOnChildSelectedListener { _, _, position, _ ->
// Move the item in the previous position to the new position
adapter.move(oldPosition, position)
// Set old position to currently selected position.
oldPosition = position
}
}
fun VerticalGridSupportFragment.getVerticalGridView(): VerticalGridView? {
return VerticalGridSupportFragment::class.java.getDeclaredField("mGridViewHolder")?.let {
it.isAccessible = true
return (it.get(this) as VerticalGridPresenter.ViewHolder).gridView
}
}
问题是,adapter.move()
会导致另一个选中的子事件
我试图通过临时删除选择侦听器来避免此问题,而是保留一个ObjectAdapter.DataObserver来通知我onItemMoved()
事件,在这些事件中,我设置了所选位置并再次设置了选择侦听器
这似乎也不完全有效
不可能使用ItemTouchHelper
,因为它是为触摸而设计的,并且不像我们在安卓电视上那样使用遥控器
当你在主屏幕上重新排列应用程序快捷方式时,官方的Android TV launcher应用程序正在做一些类似于我所需要的事情,但我想不出一个方法来让它工作。找到了一个解决方案,这似乎也是谷歌在Android TV launcher上使用的方法 简而言之:创建一个自定义的
VerticalGridView
并覆盖其focusSearch()
方法来确定如何移动/交换项目
类似于此:
class EditableVerticalGridView @JvmOverloads constructor(context: Context,
attrs: AttributeSet? = null,
defStyle: Int = 0) :
VerticalGridView(context, attrs, defStyle) {
override fun focusSearch(focused: View, direction: Int): View {
return if (focused.isSelected) {
swapItemsIfNeeded(focused, direction)
} else super.focusSearch(focused, direction)
}
private fun swapItemsIfNeeded(focused: View, direction: Int): View {
val position = getChildAdapterPosition(focused)
if (!itemAnimator.isRunning) {
if (canMoveInDirection(position, direction)) {
when (direction) {
FOCUS_LEFT -> moveChannel(position, position - 1)
FOCUS_UP -> moveChannel(position, position - NUM_COLUMN)
FOCUS_RIGHT -> moveChannel(position, position + 1)
FOCUS_DOWN -> moveChannel(position, position + NUM_COLUMN)
}
}
}
return focused
}
private fun canMoveInDirection(position: Int, direction: Int): Boolean {
when (direction) {
FOCUS_LEFT -> {
return position % NUM_COLUMN > 0
}
FOCUS_UP -> {
return position - NUM_COLUMN >= 0
}
FOCUS_RIGHT -> {
return !(position % NUM_COLUMN >= (NUM_COLUMN - 1) ||
position >= adapter.itemCount - 1)
}
FOCUS_DOWN -> {
return position + NUM_COLUMN <= adapter.itemCount - 1
}
else -> {
return false
}
}
}
private fun moveChannel(fromPosition: Int, toPosition: Int) {
(adapter as AllowedChannelAdapter).moveChannel(fromPosition, toPosition)
}
companion object {
private const val NUM_COLUMN: Int = 6
}
}
fun moveChannel(from: Int, to: Int) {
var offset = 1
if (from >= 0 && from <= channelItems.size - 1 && to >= 0 && to <= channelItems.size - 1) {
val fromItem = channelItems[from]
channelItems[from] = channelItems[to]
channelItems[to] = fromItem
notifyItemMoved(from, to)
val positionDifference = to - from
if (Math.abs(positionDifference) > 1) {
if (positionDifference > 0) {
offset = -1
}
notifyItemMoved(to + offset, from)
}
}
}