#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>