ViewPager в CoordinatorLayout неожиданно сжимается

В моем приложении для Android, работающем на Android 5.1.1, у меня есть макет, использующий Toolbar с TabLayout, а под ним ViewPager. Все это собрано в CoordinatorLayout.

На первой странице ViewPager есть RecyclerView CardView товаров.

Моя проблема заключается в том, что размер моего ViewPager постоянно изменяется таким образом, что элементы моего CardView списка обрезаются.

Мой основной макет выглядит примерно так:

<android.support.design.widget.CoordinatorLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
    <android.support.design.widget.AppBarLayout
        android:layout_height="wrap_content"
        android:layout_width="match_parent" >

        <android.support.v7.widget.Toolbar
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />

        <android.support.design.widget.TabLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />

    </android.support.design.widget.AppBarLayout>

    <android.support.v4.view.ViewPager 
        app:layout_behavior="@string/appbar_scrolling_view_behavior"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</android.support.design.widget.CoordinatorLayout>

И первый фрагмент, который обслуживает мой ViewPager, выглядит так:

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

    <android.support.v7.widget.RecyclerView
        android:scrollbars="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_margin="8dp"/>

</FrameLayout>

Это делает что-то похожее на это:

введите описание изображения здесь

При нажатии кнопки в моем макете я использую startActivityForResult для вызова другого действия, а при возвращении к моему действию иногда мой список внезапно обрезается, так что видна только половина первого элемента:

введите описание изображения здесь

После горизонтального смахивания на другой пейджер в ViewPager, а затем обратно, проблема исчезает, так что, похоже, повторное расположение не сработало должным образом. Однако нажатие ДОМОЙ, а затем возобновление моей деятельности НЕ решает проблему. Обратите внимание, что это происходит, даже если я никак не изменяю свой макет, я просто возвращаюсь из вызова startActivityForResult. И да, это происходит иногда... И у меня нет запущенных фоновых потоков, которые могли бы объяснить очевидное случайное поведение.

Сначала я подумал, что это RecyclerView уменьшился, но с помощью HierarchyViewer я смог обнаружить, что на самом деле это был весь ViewPager, который уменьшился примерно до половины своей первоначальной высоты.

Я пробовал различные хаки, чтобы обойти это, включая вызов invalidate() и requestLayout() для всей моей иерархии представлений, но ничего не помогло (хотя перелистывание на другую страницу и обратно исправляет это). Кроме того, это не те решения, к которым я хочу прибегнуть... Затем я попытался изменить высоту ViewPager на wrap_content, что фактически решило эту конкретную проблему; после возвращения к моей деятельности первый элемент в моем RecyclerView никогда не обрезается, и я могу прокрутить вниз до других элементов. Однако теперь вместо этого самый последний элемент моего списка всегда обрезается, как видно на этом снимке экрана, где список прокручен до конца:

введите описание изображения здесь

Поскольку сейчас я нахожусь в такой ситуации, когда не совсем понимаю, что происходит, мне нужна помощь. Что мне действительно следует использовать в качестве layout_height для моего ViewPager и, прежде всего, почему? Для меня match_parent имеет смысл, но как я должен думать здесь? Есть ли рациональная причина, по которой мои представления обрезались при использовании match_parent, или я действительно столкнулся с ошибкой в ​​ViewPager, RecyclerView и/или CoordinatorLayout? Как мне убедиться, что мой ViewPager постоянно заполняет всю область экрана под панелью приложений, и что мой RecyclerView можно прокручивать по вертикали для правильного отображения всех элементов списка CardView?


comment
match_parent будет работать, потому что поведение макета поместит дочерний макет ниже AppBarLayout в то время, когда дочерние элементы находятся в макете.   -  person Nikola Despotoski    schedule 15.10.2015
comment
Спасибо @NikolaDespotoski, но это не относится ни к одному из моих вопросов. Используя match_parent, я сталкиваюсь со странным изменением размера окна просмотра после возврата из подактивности, что является основной проблемой.   -  person JHH    schedule 15.10.2015
comment
Возможно, вы могли бы создать MCVE. Воспроизвести это не так просто.   -  person tachyonflux    schedule 15.10.2015
comment
@karaokyo да, я включил практически весь код макета, но вы правы. Я попытаюсь сжать это в MCVE, и, надеюсь, кто-то захочет взглянуть на это до истечения срока действия награды...   -  person JHH    schedule 20.10.2015
comment
Попробуйте сохранить layout:height из ViewPager как 0dp и добавить layout:weight как 1   -  person JavaGhost    schedule 20.10.2015
comment
@JavaGhost layout_weight применим к LinearLayout, а не к CoordinatorLayout, который является расширенным FrameLayout. Это не поможет.   -  person JHH    schedule 21.10.2015
comment
Вы проверили иерархию представлений в Android Device Monitor — View Hierarchy for UI Automator? Там вы можете увидеть, что именно происходит - какой вид обрезается или, может быть, появляется выше. Начните с него и дайте нам знать, что вы там видите.   -  person sergej shafarenka    schedule 21.10.2015
comment
Да, как упоминалось в моем вопросе, я сделал именно это.   -  person JHH    schedule 21.10.2015
comment
Кроме того, если вы посмотрите ниже, вы обнаружите, что я уже обнаружил, что проблема заключается в ошибке в appcompat.   -  person JHH    schedule 21.10.2015


Ответы (3)


У меня возникла аналогичная проблема при использовании более старой версии библиотеки поддержки.

См. следующие связанные вопросы:

https://code.google.com/p/android/issues/detail?id=176406 https://code.google.com/p/android/issues/detail?id=176187

Убедитесь, что вы используете последнюю версию Support library 23.1 на момент написания этой статьи.

person marmor    schedule 22.10.2015
comment
Спасибо, что указали на эти другие проблемы, они действительно выглядят одинаково. Вчера я обновил библиотеку поддержки безрезультатно, но, возможно, что-то не так с моей настройкой, потому что сегодня я скачал Android Studio 1.4 с нуля, взял новый SDK и повторно загрузил все зависимости. Библиотека поддержки теперь @ 23.1. И с этой настройкой я больше не могу воспроизвести проблему. Я отмечу ваш ответ как принятый ответ, поскольку он фактически решает проблему (мой собственный ответ просто использовал обходной путь). Наслаждайтесь щедростью. ;) - person JHH; 22.10.2015

Оказывается, это почти наверняка ошибка в CoordinatorLayout или, что еще более вероятно, в AppBarLayout$ScrollingViewBehavior. Пытаясь создать MCVE, я понял, что именно тот факт, что у моей подактивности был IME на экране, вызвал сокращение ViewPager - когда моя активность возобновляется после onActivityResult, ViewPager сокращается в результате уменьшения реального экрана. -estate из IME, но больше никогда не расширяется, несмотря на то, что IME больше не отображается, и тот факт, что CoordinatorLayout действительно расширяется.

После отладки и прохождения шагов onLayout и onMeasure из CoordinatorLayout и ViewPager теперь я совершенно уверен, что CoordinatorLayout неправильно передает изменение размера своим дочерним элементам.

Я обнаружил, что могу «исправить» проблему, вызвав requestLayout на моем ViewPager, но только если вызов достаточно задержан (10 мс никогда не работает, 100 мс работает большую часть времени):

protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    mHandler.postDelayed(new Runnable() {
        @Override
        public void run() {
            mViewPager.requestLayout();
        }
    }, 100);
}

Это, очевидно, не очень надежное решение, но после еще нескольких исследований выяснилось, что мне, вероятно, даже не нужен CoordinatorLayout, так как у меня действительно нет расширенного поведения прокрутки. Поэтому моим решением будет просто использовать LinearLayout или RelativeLayout в качестве корневой группы просмотра. Красиво и просто, не надо ничего усложнять.

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >
    <android.support.design.widget.AppBarLayout
        android:layout_height="wrap_content"
        android:layout_width="match_parent" >

        <android.support.v7.widget.Toolbar
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />

        <android.support.design.widget.TabLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />

    </android.support.design.widget.AppBarLayout>

    <android.support.v4.view.ViewPager 
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</LinearLayout>

Однако я попытаюсь сжать это до простого примера и сообщить об ошибке в Google.

Что касается того, что я должен использовать в качестве высоты для моего ViewPager, мое первоначальное использование match_parent все еще кажется разумным. Тот факт, что wrap_content решил проблему (но вызвал другие проблемы), вероятно, связан с несоответствиями, вызванными ошибкой.

person JHH    schedule 21.10.2015
comment
Оказывается, эта ошибка действительно уже исправлена ​​в библиотеке поддержки. Спасибо @marmor за указание на это в stackoverflow.com/a/33275205/1226020 - person JHH; 22.10.2015

В вашем фрагменте просто удалите frameLayout и сделайте родителем recyclerView... Надеюсь, это сработает:

 <android.support.v7.widget.RecyclerView
        android:scrollbars="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_margin="8dp">


<android.support.v7.widget.RecyclerView/>
person Chordin4tion    schedule 20.10.2015
comment
Спасибо за ваше предложение, но нет, это не сработало. Как я уже сказал, размер всего ViewPager неожиданно изменяется, когда моя активность возобновляется, даже если для него установлено значение match_parent, и изменение макета одного из фрагментов, используемых в качестве дочерних страниц, не похоже на то, что это будет помощь. Опять же, я не исключаю, что здесь есть ошибка в библиотеке совместимости приложений, поэтому, конечно, возможно, что изменение, которое кажется бессмысленным, может косвенно решить проблему, но, к сожалению, этого не произошло. В чем заключалась ваша идея? - person JHH; 20.10.2015
comment
почему бы вам не попробовать заменить свой фрагмент другим, который вы используете.... или если вы вызываете действие, сначала завершите текущее действие, а затем вызовите его, нажав кнопку "Назад" из вызванного действия... - person Chordin4tion; 21.10.2015
comment
Что ж, это может сработать, но на самом деле я не ищу долгосрочных обходных путей, таких как отказ от базовых вещей, таких как startActivityForResult. Это может сработать, так как моя проблема, по-видимому, связана со временем, но это сделает мой код более беспорядочным, мне придется обрабатывать больше состояний, и производительность, вероятно, немного пострадает. - person JHH; 21.10.2015