Как переместить элементы сетки на Android TV? - PullRequest
0 голосов
/ 17 января 2019

Я работаю над сеточным интерфейсом (используя VerticalGridSupportFragment) для Android TV и ищу способ, позволяющий пользователям перемещаться по элементам в сетке.

Идея состоит в том, что сетка содержит несколько телевизионных каналов и что пользователи должны иметь возможность изменять порядок телевизионных каналов (сортировать / переупорядочивать). Мое предлагаемое решение состоит в том, чтобы выбрать канал, щелкая по нему. Канал становится «липким», что позволяет вам перемещать его. Когда вы довольны позицией, вы снова нажимаете на канал, подтверждая его новую позицию.

Очевидное решение - сделать что-то вроде этого:

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.

Официальное приложение для запуска Android TV делает нечто похожее на то, что мне нужно, когда вы меняете ярлыки приложений на домашнем экране, но я не могу придумать, как заставить это работать.

1 Ответ

0 голосов
/ 19 января 2019

Нашел решение, которое, похоже, и используется Google для запуска Android TV.

Вкратце: создайте пользовательский 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

    }

}

... и функция moveChannel():

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)
            }
        }
    }
...