【问题标题】:ConstraintLayout barrier doesn't work correct for all view holders of RecylcerViewConstraintLayout 屏障不适用于 RecyclerView 的所有视图持有者
【发布时间】:2022-01-11 13:34:13
【问题描述】:

我在一行中有三个 TextView(Rating、VotesRating 和 Votes),但只能同时看到其中一个。这就是为什么我添加了一个Barrier 和那些TextViews movie_rating,movie_votes,movie_rating_votes 的ID。然后我使用Barrier 在它下面添加另一个TextView(带有电影描述)。但正如您在屏幕截图中看到的那样,描述文本可以在Barrier 的上方。这是一个非常有问题的库......

对于大多数 ViewHolders 来说都可以,这太疯狂了

implementation "androidx.constraintlayout:constraintlayout:2.1.2"

完整布局

<com.google.android.material.card.MaterialCardView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_margin="@dimen/margin_0_5x"
    android:background="?android:attr/selectableItemBackground"
    android:clickable="true"
    android:focusable="true"
    android:onClick="@{(v) -> holder.onMovieClicked.invoke(movie)}"
    android:paddingHorizontal="@dimen/margin_2x"
    app:cardBackgroundColor="#2a2a2a">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <ImageView
            android:id="@+id/movie_image"
            android:layout_width="72dp"
            android:layout_height="0dp"
            android:scaleType="centerCrop"
            app:imageUrl="@{movie.imageUrl}"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintDimensionRatio="67:98"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintVertical_bias="0"
            tools:background="@drawable/placeholder_movie" />

        <TextView
            android:id="@+id/movie_title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginHorizontal="@dimen/margin_1x"
            android:layout_marginTop="@dimen/margin_0_25x"
            android:text="@{String.format(@string/movie_title, movie.number, movie.title, movie.year)}"
            android:textColor="@color/colorTextLight"
            android:textSize="16sp"
            app:layout_constrainedWidth="true"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0"
            app:layout_constraintStart_toEndOf="@id/movie_image"
            app:layout_constraintTop_toTopOf="parent"
            tools:text="1. Red Notice (2021)" />

        <TextView
            android:id="@+id/movie_certificate_runtime_genre_rating"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginHorizontal="@dimen/margin_1x"
            android:layout_marginTop="@dimen/margin_0_25x"
            android:text="@{movie.certificateRuntimeGenrerating}"
            android:textColor="#c9c9c9"
            android:textSize="12sp"
            app:layout_constrainedWidth="true"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0"
            app:layout_constraintStart_toEndOf="@id/movie_image"
            app:layout_constraintTop_toBottomOf="@id/movie_title"
            app:visible="@{movie.certificateRuntimeGenrerating != null}"
            tools:text="PG-13 | 118 min | Action, Comedy, Crime" />

        <TextView
            android:id="@+id/movie_rating"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="@dimen/margin_1x"
            android:layout_marginTop="@dimen/margin_0_25x"
            android:layout_marginEnd="@dimen/margin_1x"
            android:drawablePadding="@dimen/margin_0_5x"
            android:gravity="bottom"
            android:text="@{movie.rating}"
            android:textColor="@color/colorTextLight"
            android:textSize="12sp"
            app:drawableStartCompat="@drawable/ic_star_rate"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0"
            app:layout_constraintStart_toEndOf="@id/movie_image"
            app:layout_constraintTop_toBottomOf="@id/movie_certificate_runtime_genre_rating"
            app:visible="@{movie.rating != null &amp;&amp; movie.votes == null}"
            tools:text="7.7" />

        <TextView
            android:id="@+id/movie_rating_votes"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="@dimen/margin_1x"
            android:layout_marginTop="@dimen/margin_0_25x"
            android:layout_marginEnd="@dimen/margin_1x"
            android:drawablePadding="@dimen/margin_0_5x"
            android:gravity="bottom"
            android:text="@{String.format(@string/ph_3_spaced, movie.rating, @string/text_separator, movie.votes)}"
            android:textColor="@color/colorTextLight"
            android:textSize="12sp"
            app:drawableStartCompat="@drawable/ic_star_rate"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0"
            app:layout_constraintStart_toEndOf="@id/movie_rating"
            app:layout_constraintTop_toBottomOf="@id/movie_certificate_runtime_genre_rating"
            app:visible="@{movie.rating != null &amp;&amp; movie.rating != null}"
            tools:text="7.7 | Votes: 20,215" />

        <TextView
            android:id="@+id/movie_votes"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="@dimen/margin_1x"
            android:layout_marginTop="@dimen/margin_0_5x"
            android:layout_marginEnd="@dimen/margin_1x"
            android:text="@{movie.votes}"
            android:textColor="@color/colorTextLight"
            android:textSize="12sp"
            app:layout_constrainedWidth="true"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0"
            app:layout_constraintStart_toEndOf="@id/movie_rating_votes"
            app:layout_constraintTop_toBottomOf="@id/movie_certificate_runtime_genre_rating"
            app:visible="@{movie.votes != null &amp;&amp; movie.rating == null}"
            tools:text="Votes: 20,215" />

        <androidx.constraintlayout.widget.Barrier
            android:id="@+id/movie_rating_votes_bottom_barrier"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:barrierDirection="bottom"
            app:constraint_referenced_ids="movie_rating,movie_rating_votes,movie_votes" />

        <TextView
            android:id="@+id/movie_desc"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginHorizontal="@dimen/margin_1x"
            android:layout_marginTop="@dimen/margin_0_25x"
            android:text="@{movie.description}"
            android:textColor="@color/colorTextDescLight"
            android:textSize="12sp"
            app:layout_constrainedWidth="true"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0"
            app:layout_constraintStart_toEndOf="@id/movie_image"
            app:layout_constraintTop_toBottomOf="@id/movie_rating_votes_bottom_barrier"
            app:visible="@{movie.description != null}"
            tools:text="When a single mom and her two kids arrive in a small town, they begin to discover their connection to the original Ghostbusters and the secret legacy their grandfather left behind." />

        <Space
            android:id="@+id/movie_bottom_space"
            android:layout_width="wrap_content"
            android:layout_height="@dimen/margin_0_5x"
            app:layout_constraintTop_toBottomOf="@id/movie_desc"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent" />

    </androidx.constraintlayout.widget.ConstraintLayout>

</com.google.android.material.card.MaterialCardView>

尺寸

<dimen name="margin_1x">8dp</dimen>
<dimen name="margin_0_25x">2dp</dimen>
<dimen name="margin_0_5x">4dp</dimen>
<dimen name="margin_0_75x">6dp</dimen>
<dimen name="margin_1_5x">12dp</dimen>
<dimen name="margin_2x">16dp</dimen>
<dimen name="margin_2_5x">20dp</dimen>
<dimen name="margin_3x">24dp</dimen>
<dimen name="margin_4x">32dp</dimen>
<dimen name="margin_5x">40dp</dimen>
<dimen name="margin_6x">48dp</dimen>

绑定

@JvmStatic
@BindingAdapter("visible")
fun visible(view: View, visible: Boolean?) {
    view.isVisible = visible == true
}

RecyclerView 和 ViewHolder 类

class MoviesAdapter(
    private val onMovieClicked: (movie: Movie) -> Unit
) : PagingDataAdapter<Movie, MoviesAdapter.MovieViewHolder>(MovieComparator) {

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

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MovieViewHolder {
        val layoutInflater = LayoutInflater.from(parent.context)
        return MovieViewHolder(
            LayoutItemMovieBinding.inflate(layoutInflater, parent, false),
            onMovieClicked
        )
    }

    inner class MovieViewHolder(
        binding: LayoutItemMovieBinding,
        val onMovieClicked: (movie: Movie) -> Unit
    ) : BaseViewHolder<LayoutItemMovieBinding, Movie>(binding) {

        init {
            binding.holder = this
        }

        override fun bind(data: Movie, position: Int) {
            binding.movie = data
        }
    }

    private object MovieComparator : DiffUtil.ItemCallback<Movie>() {
        override fun areItemsTheSame(oldItem: Movie, newItem: Movie) = oldItem.id == newItem.id

        override fun areContentsTheSame(oldItem: Movie, newItem: Movie) = oldItem == newItem
    }
}

【问题讨论】:

  • app:barrierAllowsGoneWidgets="true" 添加到屏障有帮助吗?发布适配器的相关部分怎么样?这似乎发生在 RecyclerView 内部,重新绑定是如何发生的?您是否将其他视图设置为“消失”或“不可见”?这究竟是什么时候发生的?您能否提供一个简单的示例项目来复制它?你不可能发布一个 CL 问题,发布一个充满自定义内容(尺寸、颜色、资产)的布局,并期望 SO 用户找出问题所在,同时诅咒一个库有问题。
  • 您可能还应该添加app:layout_constrainedWidth="true" 和所有 3 个 TextView 对应的高度,因为当您使用其中一个或另一个时,它们可能会消失。 (你为什么使用三个小部件对我来说也有点神秘),如果这是一个演示问题,我认为不应该将决定放在 viewHolder 中来决定要做什么,并添加更多 约束(不是双关语)视图系统试图在不到 60 毫秒的时间内实时测量和布置列表。如果有的话,请使用自定义视图并封装所有内容。但那是另一回事。
  • 最后,如果屏障无法发挥作用,很可能您在布局已经测量/布局(或正在布局的过程中)时隐藏/显示了 textView,因此假定屏障所有三个引用的 Id 都消失了,因此以这种方式完成了它的工作。当你决定展示一个并设定它的价值时,已经太晚了。这也是未知的,因为我们既没有看到您的适配器,也没有看到您设置/获取此数据的时间。
  • @MartinMarconcini 你现在可以看到整个代码
  • @MartinMarconcini app:layout_constrainedWidth="true",是的,我忘了添加两个文本视图,已修复,但仍然与此问题无关

标签: android android-recyclerview android-constraintlayout android-viewholder constraintlayout-barrier


【解决方案1】:

如果没有整个布局,很难判断会发生什么,但请尝试以下操作:

从“movie_desc”中删除app:layout_constraintBottom_toTopOf="@id/movie_bottom_space"

movie_bottom_space 中删除app:layout_constraintBottom_toBottomOf="parent"

app:layout_constraintTop_toBottomOf="@id/movie_desc" 添加到movie_bottom_space

确保 ConstraintLayout 的高度为 wrap_content

这将堆叠视图并让 ConstraintLayout 的整体高度随其内容而波动。

【讨论】:

  • ConstraintLayout has wrap_content 当然,如果不是wrap_content.,一个视图持有者会占据整个屏幕其他东西没有帮助,我已经尝试删除movie_bottom_space 视图和app:layout_constraintBottom_toTopOf="@id/movie_bottom_space",没有任何帮助
  • 我提供了完整版面
  • @user924 谢谢,有帮助。这是一个奇怪的问题。我想知道它是否与this reported defect 有关。屏障似乎不合适。
【解决方案2】:

这是此已知错误的变体: https://github.com/androidx/constraintlayout/issues/422 您可以关闭优化器来修复它。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-03-29
    • 1970-01-01
    • 2017-10-04
    • 2016-04-24
    • 1970-01-01
    • 2020-01-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多