android - 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 ,此布局实现按预期工作。
观察
使用上面相同的 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 设计预览确认布局应该按照预期部分中的描述工作。
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)
}
尝试解决方案
layout_height
在photo_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>
- 以编程方式设置布局宽度和高度。
这并不理想,因为 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)
}