RecyclerView с горизонтальным GridLayoutManager корректирует свою высоту до самой большой строки - PullRequest
3 голосов
/ 10 июля 2019

Я хочу использовать RecyclerView с GridLayoutManager для достижения чего-то подобного:

Target

Здесь GridLayoutManager с горизонтальной ориентацией и 2 рядами. Для меня важно, что RecyclerView установлен как wrap_content.

Итак, я попытался достичь своей цели с помощью такого кода:

MainActivity.kt

class MainActivity : AppCompatActivity() {

    private val adapter = Adapter()
    private val layoutManager = GridLayoutManager(this, 2, RecyclerView.HORIZONTAL, false)

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        recyclerView.adapter = adapter
        recyclerView.layoutManager = layoutManager

        adapter.submitList(listOf(Unit, Unit, Unit, Unit))
    }
}

Adapter.kt

class Adapter : ListAdapter<Any, ItemHolder>(DiffCallback()) {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemHolder {
        val itemView = LayoutInflater.from(parent.context).inflate(viewType, parent, false)
        return ItemHolder(itemView)
    }

    override fun onBindViewHolder(holder: ItemHolder, position: Int) {
    }

    override fun getItemViewType(position: Int): Int {
        return when {
            position % 2 == 0 -> R.layout.item_small
            else -> R.layout.item_normal
        }
    }

    class DiffCallback : DiffUtil.ItemCallback<Any>() {
        override fun areItemsTheSame(oldItem: Any, newItem: Any) = false
        override fun areContentsTheSame(oldItem: Any, newItem: Any) = false
    }

    class ItemHolder(itemView: View) : RecyclerView.ViewHolder(itemView)
}

activity_main.xml

<?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"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

item_normal.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="100dp"
    android:layout_height="100dp"
    android:layout_margin="8dp"
    android:background="@color/colorAccent" />

item_small.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="100dp"
    android:layout_height="50dp"
    android:layout_margin="8dp"
    android:background="@color/colorAccent" />

К сожалению, в результате каждая строка растягивается до самой большой строки в сетке:

current

Вопрос в том, как я могу удалить расстояние между первым и вторым рядом? Имейте в виду, что у меня должна быть прокручиваемая горизонтальная сетка, ее высота зависит от ее содержимого. И предметы должны иметь фиксированный размер.

Ответы [ 3 ]

1 голос
/ 15 июля 2019

Предполагая, что вы знаете соотношения между высотами отдельных рядов, вы можете попытаться использовать SpanSizeLookup для регулировки высоты каждого ряда. В вашем простом случае меньший элемент (50dp) займет одну строку (размер диапазона 1), а более крупный элемент (100dp) займет две строки (размер диапазона 2), поэтому вся сетка в целом будет содержать 3 строки.

Конечно, для более сложной конфигурации строк отношения могут быть немного более сложными: скажем, я хотел строки высот 32dp / 48dp / 64dp, тогда отношения высот 32/144, 48/144 и 64/144 , который мы можем упростить до 2/9, 3/9, 4/9, получив всего 9 рядов, с размерами промежутков 2, 3 и 4 для отдельных элементов. В крайних случаях это может привести к большому количеству строк (когда дроби не могут быть упрощены), но, если вы используете какой-либо тип сетки (x8, x10 и т. Д.) И элементы имеют разумный размер, он все равно должен быть управляемым .

В любом случае, в вашем случае код будет таким:

val layoutManager = GridLayoutManager(this, 3, RecyclerView.HORIZONTAL, false)
layoutManager.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
    override fun getSpanSize(position: Int) = when (position % 2) {
        0 -> 1 // 50dp item
        else -> 2 // 100dp item
    }
}

Учитывая больше строк, оператор when будет становиться все более сложным, но если у вас уже есть адаптер, вы можете использовать getItemViewType, чтобы легче различать отдельные строки в операторе when.

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

0 голосов
/ 10 июля 2019

Я думаю, что GridLayoutManager по умолчанию ограничивает дочерние представления одинаковыми размерами.

Я настраиваю код для использования LinearLayoutManager, и представление для ViewHolder заключается в том, чтобы обернуть два типа представлений в RelativeLayout.

См. Код ниже:

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

    <View
        android:id="@+id/top"
        android:layout_width="100dp"
        android:layout_height="50dp"
        android:layout_margin="8dp"
        android:layout_alignParentTop="true"
        android:background="@color/colorAccent" />


    <View
        android:id="@+id/bottom"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:layout_margin="8dp"
        android:layout_below="@+id/top"
        android:background="@color/colorAccent" />
</RelativeLayout>

RecyclerAdapter:

        View view = LayoutInflater.from(viewGroup.getContext()).inflate(
                R.layout.item_normal,
                viewGroup,
                false
        );
        ViewHolder viewHolder = new ViewHolder(view);
        return viewHolder;

И установите LinearLayoutManager:

        LinearLayoutManager layoutManager = new LinearLayoutManager(this, RecyclerView.HORIZONTAL, false);
        recyclerView.setLayoutManager(layoutManager);

screenshot

0 голосов
/ 10 июля 2019

Попробуйте использовать StaggeredGridLayoutManager , чтобы иметь разные высоты. Замените ваш GridLayoutManager следующим StaggeredGridLayoutManager

StaggeredGridLayoutManager layoutManager = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL);

enter image description here

...