два горизонтальных вида рециркулятора бок о бок, если есть свободное место, или один над другим, если нет

#android #android-recyclerview #android-constraintlayout

Вопрос:

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

В большинстве случаев на большинстве устройств будет достаточно места для отображения обоих представлений переработчика в их полноте (т. е. прокрутка не требуется) бок о бок в одной строке (т. е. начало RV2 ограничено концом RV1).

Однако, если устройство имеет особенно маленький экран или несколько крайних случаев, когда в каждом представлении более 4 или 5 значков, будет недостаточно места, чтобы отобразить их во всей полноте в одной строке, и в этом случае я хочу показать каждый вид переработчика в отдельной строке (т. Е. Верхняя часть RV2 ограничена нижней частью RV1).

(В очень редком случае, когда все еще недостаточно места, чтобы показать один из RV в его полноте, как только они находятся в отдельных строках, тогда я согласен с горизонтальной прокруткой RV!)

В принципе, если значков всего несколько, то все они выглядят намного лучше в одном ряду, но если места нет, я бы предпочел разделить их на две строки, чем прокручивать.

Легко ли это достижимо?

Вот мой текущий файл макета, в котором RV складываются друг на друга

 <?xml version="1.0" encoding="utf-8"?>
<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="wrap_content"
    android:descendantFocusability="beforeDescendants"
    android:layout_marginVertical="5dp">

    <de.hdodenhof.circleimageview.CircleImageView
        android:id="@ id/ivPersonImage"
        android:layout_width="64dp"
        android:layout_height="64dp"
        android:layout_marginTop="8dp"
        android:layout_marginBottom="8dp"
        android:background="@drawable/circular_shadow"
        android:src="@drawable/ic_dummy_profile"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <com.myProject.android.custom.InstantAutoComplete
        android:id="@ id/auto_etPersonName"
        style="@style/regular"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginEnd="8dp"
        android:background="@drawable/bg_outline_grey"
        android:gravity="start"
        android:hint="@string/enterNameOrSelectBuddies"
        android:imeOptions="actionDone"
        android:inputType="textEmailAddress|textPersonName"
        android:padding="8dp"
        android:textColor="@color/textColor"
        android:textSize="16sp"
        android:visibility="gone"
        app:layout_constraintBottom_toTopOf="@ id/tvPersonName"

        app:layout_constraintEnd_toStartOf="@ id/ivRemovePerson"
        app:layout_constraintStart_toEndOf="@ id/ivPersonImage"
        app:layout_constraintTop_toTopOf="@id/ivPersonImage" />

    <TextView
        android:id="@ id/tvPersonName"
        style="@style/regular"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:ellipsize="end"
        android:maxLines="1"
        android:paddingHorizontal="8dp"
        android:text="name goes here"
        android:textColor="@color/appThemeColor"
        android:textSize="18sp"
        android:visibility="visible"
        app:layout_constraintBottom_toTopOf="@id/rvSport"
        app:layout_constraintEnd_toStartOf="@ id/ivRemovePerson"
        app:layout_constraintStart_toEndOf="@ id/ivPersonImage"
        app:layout_constraintTop_toBottomOf="@ id/auto_etPersonName" />

    <androidx.recyclerview.widget.RecyclerView
        android:id="@ id/rvSport"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:orientation="horizontal"
        android:visibility="visible"
        app:layoutManager=".WrapContentLinearLayoutManager"
        app:layout_constraintBottom_toTopOf="@id/rvLevel"
        app:layout_constraintEnd_toStartOf="@ id/ivRemovePerson"
        app:layout_constraintStart_toEndOf="@ id/ivPersonImage"
        app:layout_constraintTop_toBottomOf="@id/tvPersonName"
        app:layout_constraintHorizontal_bias="0"/>

    <androidx.recyclerview.widget.RecyclerView
        android:id="@ id/rvLevel"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:orientation="horizontal"
        app:layoutManager=".WrapContentLinearLayoutManager"
        app:layout_constraintBottom_toBottomOf="@id/ivPersonImage"
        app:layout_constraintEnd_toStartOf="@ id/ivRemovePerson"
        app:layout_constraintStart_toEndOf="@ id/ivPersonImage"
        app:layout_constraintTop_toBottomOf="@id/rvSport" />

    <ImageView
        android:id="@ id/ivRemovePerson"
        android:layout_width="24dp"
        android:layout_height="24dp"
        android:layout_marginEnd="16dp"
        android:background="?selectableItemBackgroundBorderless"
        android:src="@drawable/ic_delete_bin_24dp"
        app:layout_constraintBottom_toBottomOf="@id/ivPersonImage"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@id/barrier2"
        app:layout_constraintTop_toTopOf="@id/ivPersonImage" />


    <androidx.constraintlayout.widget.Barrier
        android:id="@ id/barrier2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:barrierDirection="end"
        app:constraint_referenced_ids="auto_etPersonName,tvPersonName"/>
</androidx.constraintlayout.widget.ConstraintLayout>
 

Edit:

I’ve got closer using flow helper to hold the two recycler views.

 
    <androidx.recyclerview.widget.RecyclerView
        android:id="@ id/rvSport"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:orientation="horizontal"
        app:layoutManager=".WrapContentLinearLayoutManager"
        app:layout_constrainedWidth="true"
        tools:itemCount="3"/>

    <androidx.recyclerview.widget.RecyclerView
        android:id="@ id/rvLevel"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:orientation="horizontal"
        app:layoutManager=".WrapContentLinearLayoutManager"
        app:layout_constrainedWidth="true"
        tools:itemCount="8"/>

    

<androidx.constraintlayout.helper.widget.Flow
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    app:constraint_referenced_ids="rvSport,rvLevel"
    android:orientation="horizontal"
    app:flow_wrapMode="chain"
    app:layout_constraintStart_toEndOf="@id/ivPersonImage"
    app:layout_constraintEnd_toStartOf="@ id/barrier2"
    app:layout_constraintTop_toBottomOf="@id/tvPersonName"
    app:layout_constraintBottom_toBottomOf="@id/ivPersonImage"
    app:flow_horizontalStyle="packed"
    app:flow_horizontalBias="0"
    app:flow_horizontalGap="10dp"
    app:layout_constrainedWidth="true"
    app:flow_horizontalAlign="start"

    />
 

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

Например, если X представляет ограничения, Y-содержимое RV1, а Z-содержимое RV2, я получаю это:

Оба помещаются в одну строку — правильно отображается в одной строке

XXXXXXXXXXX
ГГГГ ЗЗЗ

Один больше — правильно отображается на отдельных строках (я могу добавить столько Zs, сколько захочу, он остается в пределах ограничений, а представление Z recycler прокручивается горизонтально)

XXXXXXXXXX

ГГГГ ЗЗЗЗЗЗЗЗЗЗ

Оба RV слишком велики, чтобы отображаться в одной строке, но ни один из них не слишком велик для своей собственной строки (т. Е. Для принудительной горизонтальной прокрутки). Показывает неправильно, со 2-м RV, перекрывающим его ограничения.

XXXXXXXXXX
ГГГГ ЗЗЗЗЗЗЗЗ

Похоже app:layout_constrainedWidth="true" , что это неправильно работает с помощником потока

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

1. Рассмотрите возможность использования FlexboxLayout .

Ответ №1:

Да, вы можете легко достичь этого, используя только два вида переработчика. Вам просто нужно изменить ограничения программно в соответствии с вашими требованиями. Вот образец:

  ConstraintLayout constraintLayout = findViewById(R.id.parent_layout);
 ConstraintSet constraintSet = new ConstraintSet();
 constraintSet.clone(constraintLayout);
 constraintSet.connect(R.id.imageView,ConstraintSet.RIGHT,R.id.check_answer1,ConstraintSet.RIGHT,0);
 constraintSet.connect(R.id.imageView,ConstraintSet.TOP,R.id.check_answer1,ConstraintSet.TOP,0);
 constraintSet.applyTo(constraintLayout);
 

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

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

Ответ №2:

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

Вот полный файл, который теперь работает на 100% так, как задумывалось!

 <?xml version="1.0" encoding="utf-8"?>
<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="wrap_content"
    xmlns:tools="http://schemas.android.com/tools"
    android:descendantFocusability="beforeDescendants"
    android:layout_marginVertical="5dp">

    <de.hdodenhof.circleimageview.CircleImageView
        android:id="@ id/ivPersonImage"
        android:layout_width="64dp"
        android:layout_height="64dp"
        android:layout_marginTop="8dp"
        android:layout_marginBottom="8dp"
        android:background="@drawable/circular_shadow"
        android:src="@drawable/ic_dummy_profile"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintEnd_toStartOf="@id/flowSportsLevels"
app:layout_constraintHorizontal_bias="0"        />

    <com.myproject.android.custom.InstantAutoComplete
        android:id="@ id/auto_etPersonName"
        style="@style/regular"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginEnd="8dp"
        android:background="@drawable/bg_outline_grey"
        android:gravity="start"
        android:hint="@string/enterNameOrSelectBuddies"
        android:imeOptions="actionDone"
        android:inputType="textEmailAddress|textPersonName"
        android:padding="8dp"
        android:textColor="@color/textColor"
        android:textSize="16sp"
        android:visibility="gone"
        app:layout_constraintBottom_toTopOf="@ id/tvPersonName"

        app:layout_constraintEnd_toStartOf="@ id/ivRemovePerson"
        app:layout_constraintStart_toEndOf="@ id/ivPersonImage"
        app:layout_constraintTop_toTopOf="@id/ivPersonImage" />

    <TextView
        android:id="@ id/tvPersonName"
        style="@style/regular"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:ellipsize="end"
        android:maxLines="1"
        android:paddingHorizontal="8dp"
        tools:text="name here"
        android:textColor="@color/appThemeColor"
        android:textSize="18sp"
        android:visibility="visible"
        app:layout_constraintBottom_toTopOf="@id/flowSportsLevels"
        app:layout_constraintEnd_toStartOf="@ id/ivRemovePerson"
        app:layout_constraintStart_toEndOf="@ id/ivPersonImage"
        app:layout_constraintTop_toBottomOf="@ id/auto_etPersonName" />

    <androidx.recyclerview.widget.RecyclerView
        android:id="@ id/rvSport"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:orientation="horizontal"
        app:layoutManager=".WrapContentLinearLayoutManager"
        app:layout_constrainedWidth="true"
        tools:itemCount="3"/>

    <androidx.recyclerview.widget.RecyclerView
        android:id="@ id/rvLevel"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:orientation="horizontal"
        app:layoutManager=".WrapContentLinearLayoutManager"
        app:layout_constrainedWidth="true"
        tools:itemCount="4"/>



<androidx.constraintlayout.helper.widget.Flow
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    app:constraint_referenced_ids="rvSport,rvLevel"
    android:orientation="horizontal"
    app:flow_wrapMode="chain"
    app:layout_constraintStart_toEndOf="@id/ivPersonImage"
    app:layout_constraintEnd_toStartOf="@ id/barrier2"
    app:layout_constraintTop_toBottomOf="@id/tvPersonName"
    app:layout_constraintBottom_toBottomOf="@id/ivPersonImage"
    app:flow_horizontalStyle="packed"
    app:flow_horizontalBias="0"
    app:flow_horizontalGap="10dp"
    app:layout_constrainedWidth="true"
    app:flow_horizontalAlign="start"
    android:id="@ id/flowSportsLevels"
    android:layout_marginHorizontal="10dp"
    />

    <ImageView
    android:id="@ id/ivRemovePerson"
    android:layout_width="24dp"
    android:layout_height="24dp"
    android:layout_marginEnd="16dp"
    android:background="?selectableItemBackgroundBorderless"
    android:src="@drawable/ic_delete_bin_24dp"
    app:layout_constraintBottom_toBottomOf="@id/ivPersonImage"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toEndOf="@id/barrier2"
    app:layout_constraintTop_toTopOf="@id/ivPersonImage" />
    <androidx.constraintlayout.widget.Barrier
        android:id="@ id/barrier2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:barrierDirection="end"
        app:constraint_referenced_ids="auto_etPersonName,tvPersonName"/>
   
</androidx.constraintlayout.widget.ConstraintLayout>