Android TV перемещает фокус между RecyclerViews

Я создаю приложение для Android TV со следующим макетом: введите описание изображения здесь

Оба списка слева и справа представляют собой RecyclerViews с вертикальными LinearLayoutManagers, представление заголовка является статическим. Навигация с D-PAD работает нормально в пределах одного списка, но при переключении с одного списка на другой возникают проблемы. Фокус перемещается, скажем, с элемента 5 списка 1 на элемент 5 списка 2. Когда список 2 короткий и содержит менее 5 элементов, он просто теряет фокус.

Я хочу, чтобы последний сфокусированный индекс элемента был сохранен, и когда пользователь перемещается по list1-list2-list1, элемент с этим индексом снова получает фокус, а также предотвращает потерю фокуса представлениями. Есть ли хорошее решение для этого?

Требуемое поведение: пользователь переходит к началу списка и нажимает «ВВЕРХ» — фокус остается там, где был, ничего не происходит. пользователь переходит в конец списка и нажимает «ВНИЗ» — фокус остается там, где он был, ничего не происходит. пользователь переходит от списка 1 к списку 2 — элемент, который ранее имел фокус в списке 2, получает фокус или элемент 0 получает фокус, если ранее ни один из них не был сфокусирован.

основной макет:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >

    <ImageView
        android:id="@+id/iv_background"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:focusable="false"
        />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_marginBottom="27dp"
        android:layout_marginLeft="48dp"
        android:layout_marginRight="48dp"
        android:layout_marginTop="27dp"
        android:orientation="horizontal"
        >

        <LinearLayout
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="2"
            android:orientation="vertical"
            >

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:focusable="false"
                android:orientation="horizontal"
                >

                <ImageView
                    android:id="@+id/iv_poster"
                    android:layout_width="@dimen/episodes_list_item_height"
                    android:layout_height="@dimen/episodes_list_image_width"
                    />

                <LinearLayout
                    android:layout_width="0dp"
                    android:layout_height="match_parent"
                    android:layout_weight="1"
                    android:orientation="vertical"
                    >

                    <TextView
                        android:id="@+id/tv_title"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        />

                    <FrameLayout
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        >

                        <TextView
                            android:id="@+id/tv_release_date"
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:layout_gravity="left"
                            />

                        <TextView
                            android:id="@+id/tv_rating"
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:layout_gravity="right"
                            android:drawableLeft="@drawable/ic_star_white_24dp"
                            android:drawablePadding="@dimen/view_padding_small"
                            />

                    </FrameLayout>

                    <TextView
                        android:id="@+id/tv_description"
                        android:layout_width="match_parent"
                        android:layout_height="0dp"
                        android:layout_weight="1"
                        />
                </LinearLayout>

            </LinearLayout>

            <android.support.v7.widget.RecyclerView
                android:id="@+id/rv_seasons_list"
                android:layout_width="match_parent"
                android:layout_height="0dp"
                android:layout_weight="1"
                android:focusable="false"
                />

        </LinearLayout>

        <android.support.v7.widget.RecyclerView
            android:id="@+id/rv_episodes_list"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="3"
            android:focusable="false"
            />

    </LinearLayout>

</FrameLayout>

макет элемента, используемый для обоих списков:

<?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="@dimen/seasons_list_item_height"
    android:orientation="horizontal"
    android:focusable="true"
    >

    <TextView
        android:id="@+id/tv_title"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:gravity="center_vertical"
        android:layout_weight="1"
        />

    <TextView
        android:id="@+id/tv_episodes_count"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:gravity="center_vertical"
        android:ems="10"
        />

</LinearLayout>

person Євген Гарастович    schedule 04.02.2016    source источник
comment
не могли бы вы опубликовать свои xml файлы?   -  person Jay Rathod RJ    schedule 05.02.2016
comment
Итак, вы хотите переместить фокус между обоими представлениями переработчика, не могли бы вы объяснить немного больше?   -  person Jay Rathod RJ    schedule 06.02.2016
comment
Правильно. я хочу запомнить последнюю сфокусированную позицию в recyclerviews. и когда пользователь переходит из списка1 в список2 и обратно в список1, я хочу восстановить ранее сфокусированный элемент списка1. то же самое касается списка2. также я хочу, чтобы фокус не перемещался, когда пользователь нажимает «вверх» на крестовине, когда элемент 1 в списке 1 сфокусирован и т. д.   -  person Євген Гарастович    schedule 08.02.2016
comment
Вы пробовали какой-либо код, чтобы получить фокус для последней позиции, если нет, то проверьте эту ссылку stackoverflow.com/questions/26682277/   -  person Jay Rathod RJ    schedule 08.02.2016
comment
jaydroider, выделение и фокусировка - разные вещи. я могу получить последнюю сфокусированную позицию, но не вижу способа восстановить ее.   -  person Євген Гарастович    schedule 08.02.2016
comment
Вы имеете в виду, что когда вы используете удаленную навигацию, вы сталкиваетесь с этой проблемой?   -  person Jay Rathod RJ    schedule 08.02.2016
comment
именно это. я использую стандартный пульт плеера Nexus (d-pad)   -  person Євген Гарастович    schedule 08.02.2016
comment
не могли бы вы добавить больше деталей к вашему предложению? что именно мне попробовать?   -  person Євген Гарастович    schedule 08.02.2016
comment
Вы пробовали сделать их братьями и сестрами? У меня есть что-то похожее на то, что вы делаете, и оно отлично работает для меня, пока они находятся в одном и том же относительном макете. Я знаю, что это усложняет стиль, но, по крайней мере, вещи останутся в фокусе.   -  person alex_milhouse    schedule 13.02.2016
comment
проверьте мой ответ, чтобы увидеть лучший способ сделать это   -  person Євген Гарастович    schedule 15.02.2016


Ответы (2)


Копание в источниках библиотеки Google Leanback ответило моим плательщикам. Если вы столкнулись с той же проблемой, просто оберните свои RecyclerView с помощью это и не забудьте установить

private boolean mPersistFocusVertical = false;

если вы хотите, чтобы фокус сохранялся при поиске по горизонтали, как в моем макете.

person Євген Гарастович    schedule 15.02.2016
comment
Как вы использовали этот частный класс? Можете ли вы привести пример кода решения, вы имеете в виду его обертывание в xml? - person adrianrdzv; 02.03.2016
comment
просто скопируйте код. у него нет зависимостей с приватным доступом. меня устраивает. - person Євген Гарастович; 06.04.2016
comment
Ссылка не работает (404). Не могли бы вы предоставить правильную ссылку? - person BArtWell; 14.07.2016
comment
это класс PersistentFocusWrapper из библиотеки Google v17-leanback. Вы можете найти последнюю версию. Теперь он доступен здесь: android.googlesource.com/platform/frameworks/support/+/master/ - person Євген Гарастович; 15.07.2016
comment
@ЄвгенГарастович Были ли у вас сбои при этом? Я понял: stackoverflow.com/questions/39856750/ - person BArtWell; 04.10.2016

Измените основной макет, как показано ниже.

Для направленной навигации мы можем добавить такие свойства, как nextFocusDown, nextFocusLeft, nextFocusRight, nextFocusUp, чтобы сосредоточить внимание на соответствующих элементах в соответствии с нашими требованиями.

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
>

<ImageView
    android:id="@+id/iv_background"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:focusable="false"
    />

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_marginBottom="27dp"
    android:layout_marginLeft="48dp"
    android:layout_marginRight="48dp"
    android:layout_marginTop="27dp"
    android:orientation="horizontal"
    >

    <LinearLayout
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="2"
        android:orientation="vertical"
        >

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:focusable="false"
            android:orientation="horizontal"
            >

            <ImageView
                android:id="@+id/iv_poster"
                android:layout_width="@dimen/episodes_list_item_height"
                android:layout_height="@dimen/episodes_list_image_width"
                />

            <LinearLayout
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:orientation="vertical"
                >

                <TextView
                    android:id="@+id/tv_title"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    />

                <FrameLayout
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    >

                    <TextView
                        android:id="@+id/tv_release_date"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_gravity="left"
                        />

                    <TextView
                        android:id="@+id/tv_rating"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_gravity="right"
                        android:drawableLeft="@drawable/ic_star_white_24dp"
                        android:drawablePadding="@dimen/view_padding_small"
                        />

                </FrameLayout>

                <TextView
                    android:id="@+id/tv_description"
                    android:layout_width="match_parent"
                    android:layout_height="0dp"
                    android:layout_weight="1"
                    />
            </LinearLayout>

        </LinearLayout>

        <android.support.v7.widget.RecyclerView
            android:id="@+id/rv_seasons_list"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"
            android:focusable="false"
            android:nextFocusDown="@+id/rv_episodes_list"
            />

    </LinearLayout>

    <android.support.v7.widget.RecyclerView
        android:id="@+id/rv_episodes_list"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="3"
        android:focusable="false"
        android:nextFocusUp="@+id/rv_seasons_list"
        />

</LinearLayout>

Hope this will solve your problem !!

person Jay Rathod RJ    schedule 08.02.2016
comment
Спасибо за идею, но это не помогает. Мне нужно, чтобы фокус оставался там, где он достигает верхней/нижней части списка. И это никак не повлияло на перемещение фокуса влево/вправо. - person Євген Гарастович; 08.02.2016
comment
поместите снимок экрана вашего приложения с соответствующими строками, которые у вас есть. - person Jay Rathod RJ; 08.02.2016
comment
я использовал ваш код для создания небольшого тестового проекта здесь [email protected]/jgarin /tv-test.git, когда вы нажимаете и удерживаете джойстик вверх/вниз, фокус перемещается из одного списка в другой, а предыдущая позиция не сохраняется/восстанавливается - person Євген Гарастович; 09.02.2016
comment
попробуйте получить последнюю позицию фокуса списка 1 и сохранить в настройках общего доступа. - person Jay Rathod RJ; 09.02.2016
comment
получить и хранить не проблема, я могу справиться с этим. но восстановление фокуса есть. есть идеи по этому поводу? - person Євген Гарастович; 09.02.2016
comment
возможный дубликат stackoverflow .com/questions/27194044/ проверьте это - person Jay Rathod RJ; 09.02.2016