首页 > 解决方案 > RecyclerView 单元格 wrap_content 不适用于 ConstraintLayout

问题描述

预计

RecyclerView 的 ConstraintLayout 类型的单元格布局可扩展到显示的宽度和渲染内容的高度。

post_cell.xml

<androidx.cardview.widget.CardView 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:id="@+id/card"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/cardView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <ImageView
            android:id="@+id/postImage"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:contentDescription="@string/post_image_alt"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintDimensionRatio="@string/feed_image_ratio"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            tools:srcCompat="@tools:sample/backgrounds/scenic" />
...

对于CameraXFeed开源应用程序中的post_cell.xml ,此布局实现按预期工作。

CameraXFeed 图像

观察

使用上面相同的 RecyclerView 单元格布局,在下面概述的代码中不会扩展到父视图显示的宽度和渲染内容的高度。尽管数据绑定在 ViewHolder 中,RecyclerView 仍显示为空。

该问题与布局无关,因为 FeedAdapter.kt 中的以下日志显示了ViewHolder中的预期照片图像 URL bind

实施

构建.gradle

dependencies {
    implementation "androidx.constraintlayout:constraintlayout:1.1.3"
    implementation "androidx.cardview:cardview:1.0.0"
}

布局设计与Expect部分相同。

photo_cell.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView 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:id="@+id/card"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:elevation="@dimen/card_elevation"
    android:focusable="true">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/cardView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="?attr/selectableItemBackgroundBorderless"
        android:translationZ="0dp">

        <ImageView
            android:id="@+id/postImage"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:contentDescription="@string/photo_image_alt"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintDimensionRatio="@string/feed_image_ratio"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            tools:srcCompat="@tools:sample/backgrounds/scenic" />

    </androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>

Android Studio 设计预览确认布局应该按照预期部分中的描述工作。

Android Studio 设计视图

fragment_feed.xml

<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=".view.FeedFragment">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

FeedAdapter.kt

val DIFF_UTIL = object : DiffUtil.ItemCallback<Photo>() {
    override fun areItemsTheSame(oldItem: Photo, newItem: Photo) = oldItem.id == newItem.id
    override fun areContentsTheSame(oldItem: Photo, newItem: Photo) = oldItem == newItem
}

class FeedAdapter : PagedListAdapter<Photo, FeedAdapter.ViewHolder>(DIFF_UTIL) {

    inner class ViewHolder(private val binding: PhotoCellBinding) :
        RecyclerView.ViewHolder(binding.root) {
        fun bind(photo: Photo) {
            // Logs valid valid URLs as expected from 'photo.largeImageURL'.
            Log.v(LOG_TAG, "Debug photo ${photo.largeImageURL}")
            binding.postImage.setPostImage(photo.largeImageURL)
        }
    }

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

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        getItem(position)?.let { holder.bind(it) }
    }
}

Glide.kt

fun ImageView.setPostImage(image: String) {
    Glide.with(context)
        .load(image)
        .placeholder(R.drawable.ic_content_placeholder)
        .error(R.drawable.ic_error)
        .into(this)
}

尝试解决方案

  1. layout_heightphoto_cell.xml RecyclerView 项目布局中硬编码父根 CardView并将 ConstraintLayout 设置为android:layout_height="match_parent"而不是android:layout_height="wrap_content".

这并不理想,因为它需要为不同的设备指定尺寸,而不是基于图像高度动态的布局渲染wrap_content

photo_cell.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView 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:id="@+id/card"
    android:layout_width="match_parent"
    android:layout_height="500dp">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/cardView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:translationZ="0dp">


        <ImageView
            android:id="@+id/postImage"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:contentDescription="@string/photo_image_alt"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintDimensionRatio="@string/feed_image_ratio"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            tools:srcCompat="@tools:sample/backgrounds/scenic" />

    </androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
  1. 以编程方式设置布局宽度和高度。

这并不理想,因为 XML 布局中定义的所有属性都被覆盖了。

FeedAdapter.kt

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val inflater = LayoutInflater.from(parent.context)
        val binding = PhotoCellBinding.inflate(inflater, parent, false)
        binding.card.layoutParams = ViewGroup.LayoutParams(
            ViewGroup.LayoutParams.MATCH_PARENT,
            // Sets the height to the display width to create a 1:1 aspect ratio.
            Resources.getSystem().displayMetrics.widthPixels
        )
        return ViewHolder(binding)
    }

相关文章

标签: androidandroid-layoutkotlinandroid-recyclerviewandroid-constraintlayout

解决方案


推荐阅读