Android использует HorizontalScrollView для ландшафта и ScrollView для портрета

#android

Вопрос:

Я хочу использовать разную компоновку для альбомного и портретного экрана. В портрете я хочу использовать HorizontalScrollView, который прокручивается горизонтально. В альбомной ориентации я хочу использовать ScrollView для вертикальной прокрутки.

Они хорошо работают, за исключением того, что, когда я переключаю направление своего устройства с портретной на альбомную, он падает, жалуясь, что HorizontalScrollView не может быть перенесен в ScrollView.

Должен ли он перезагрузить макет вместо приведения компонентов? Стек весь в коде для Android, а не в моем коде, поэтому я не знаю, что с ним делать. Пожалуйста, помогите, спасибо!

Это файл макета в файле res/layout:

 <androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <HorizontalScrollView
        android:id="@ id/hsv_tools"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#d0d0d0"
        android:visibility="visible"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent">

        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="#b0b0b0"
            android:orientation="horizontal"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent">

            <TextView
                android:id="@ id/tools_draw"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:gravity="center_horizontal"
                android:layout_marginRight="3dp"
                android:text="@string/tools_draw"
                android:textSize="10sp"
                app:drawableTopCompat="@drawable/tools_new" />
 

И это макет в res/макет-земля:

 <androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ScrollView
        android:id="@ id/hsv_tools"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:background="#d0d0d0"
        android:visibility="visible"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintTop_toTopOf="parent">

        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="#b0b0b0"
            android:orientation="vertical"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintTop_toTopOf="parent">
 

Сбой происходит, когда я поворачиваю мобильный телефон, трассировка стека:

 java.lang.RuntimeException: Unable to start activity ComponentInfo: java.lang.ClassCastException: android.widget.HorizontalScrollView$SavedState cannot be cast to android.widget.ScrollView$SavedState
    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2604)
    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2670)
    at android.app.ActivityThread.handleRelaunchActivity(ActivityThread.java:4370)
    at android.app.ActivityThread.access$1200(ActivityThread.java:178)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1523)
    at android.os.Handler.dispatchMessage(Handler.java:111)
    at android.os.Looper.loop(Looper.java:207)
    at android.app.ActivityThread.main(ActivityThread.java:5845)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:907)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:768)
Caused by: java.lang.ClassCastException: android.widget.HorizontalScrollView$SavedState cannot be cast to android.widget.ScrollView$SavedState
    at android.widget.ScrollView.onRestoreInstanceState(ScrollView.java:1834)
    at android.view.View.dispatchRestoreInstanceState(View.java:14953)
    at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:3249)
    at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:3255)
    at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:3255)
    at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:3255)
    at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:3255)
    at android.view.View.restoreHierarchyState(View.java:14931)
    at com.android.internal.policy.PhoneWindow.restoreHierarchyState(PhoneWindow.java:2068)
    at android.app.Activity.onRestoreInstanceState(Activity.java:1036)
    at android.app.Activity.performRestoreInstanceState(Activity.java:991)
    at android.app.Instrumentation.callActivityOnRestoreInstanceState(Instrumentation.java:1170)
    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2577)
 

Комментарии:

1. Пожалуйста, поделитесь своим кодом и трассировкой стека, чтобы мы могли помочь.

2. Я в замешательстве, почему вы пытаетесь использовать вид горизонтальной прокрутки для вертикальной прокрутки и почему вы не можете использовать только вид прокрутки для этой вертикальной прокрутки?

3. Извините за опечатку в описании. Я имею в виду, что на портретном экране я хочу, чтобы горизонтальный экран прокрутки прокручивался горизонтально, на альбомном экране я хочу, чтобы вид прокрутки прокручивался вертикально.

Ответ №1:

Вопреки тому, что вы можете подумать, HorizontalScrollView не наследуется от ScrollView.

У них обоих есть внутренние классы SavedState, но они разные.

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

Затем, когда Действие/Фрагмент будет воссоздан, ваш макет будет увеличен, а состояние восстановлено, и поскольку вы используете один и тот же идентификатор как для представления прокрутки, так и для представления горизонтальной прокрутки, сохраненное состояние, помещенное в пакет, будет для представления прокрутки, как это было до изменения ориентации.

Это то, что приводит к исключению ClassCastException.

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

Ответ №2:

Я не думаю, что кто-нибудь сможет помочь вам с просмотром журнала выполнения и кода, не могли бы вы скопировать оба в свой вопрос?

Извините, что не хватило репутации, чтобы прокомментировать.

Ответ №3:

Просто некоторые дальнейшие выводы, основанные на ответе Майкла.

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

Для моей проблемы я создал как HorizontalScrollView, так и ScrollView и добавил суффикс _hsv и _sv в идентификатор. Для портрета _sv не используется, поэтому установите видимость в «Пропало», для ландшафта установите _hsv видимость в «Пропало».

В коде я проверяю, какой из них используется, проверяя видимость == Вид.Ушел или нет. Это решает все мои проблемы.

Спасибо Майклу и всем остальным!