【问题标题】:RecyclerView item layout_weight, sizes not updatingRecyclerView 项目 layout_weight,尺寸未更新
【发布时间】:2016-08-18 20:45:25
【问题描述】:

我的 RecyclerView 项目中有两个并排的文本视图。如果视图很宽,其中一个视图具有 layout_weight="1" 以保持另一个视图可见。

问题是当视图被回收时视图大小没有更新,所以当我滚动列表时 textView id:name 宽度不正确。

我应该以某种方式强制视图更新布局吗?

我的 RecyclerView 项目:

<LinearLayout
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginLeft="16dp"
    android:gravity="center_vertical"
    android:orientation="horizontal">

    <TextView
        android:id="@+id/name"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginRight="8dp"
        android:layout_weight="1"
        android:ellipsize="end"
        android:maxLines="1" />

    <LinearLayout
        android:id="@+id/code_container"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical"
        android:background="@drawable/background_code"
        android:orientation="vertical">

        <TextView
            android:id="@+id/code"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:ellipsize="end"
            android:paddingBottom="1dp"
            android:paddingLeft="7dp"
            android:paddingRight="7dp"
            android:paddingTop="1dp"
            android:singleLine="true"/>

    </LinearLayout>

</LinearLayout>

我正在使用 RecyclerView v. 23.4.0。

更新

1.我已尝试在适配器 onBindViewHolder() 的末尾调用我的 RecyclerView 项目布局根 requestLayout。

这没有任何影响,我的观点没有更新。

@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
    ...
    viewHolder.mLayoutRoot.requestLayout();
}

2. 问题可能是调用requestLayout时视图还没有完全绘制好?所以我尝试设置 GlobalLayoutListener 并在那里调用 requestLayout 。我认为这没什么帮助,但如果我有足够的项目,在我的列表中并非所有视图都已更新。 Owerall 在我的列表中我最多有 10 项。

ViewTreeObserver vto = viewHolder.mLayoutRoot.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
    @Override
    public void onGlobalLayout() {
        viewHolder.mLayoutRoot.requestLayout();
    }
});

3. 我尝试使用具有shrinkColumns 属性的TableLayout 来接近类似的布局,但结果是一样的。名称文本视图宽度并不总是正确设置。另外,看起来 requestLayout 根本没有影响(或者我在错误的地方调用它?)

4. (2016 年 8 月 28 日)

为了重现这一点,我将代码精简到最低限度。

我设法使用 setIsRecyclable(false) 解决了这个问题,就像 dossik 建议的那样,但我认为这在性能方面不是很好的解决方案?

这是我的完整列表项:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="wrap_content"
    android:layout_height="150dp"
    android:layout_marginLeft="16dp"
    android:orientation="horizontal">

    <TextView
        android:id="@+id/name"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginRight="8dp"
        android:layout_weight="1"
        android:ellipsize="end"
        android:maxLines="1" />

    <TextView
        android:id="@+id/code"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@drawable/background_stop_code_small"
        android:ellipsize="end"
        android:paddingBottom="1dp"
        android:paddingLeft="7dp"
        android:paddingRight="7dp"
        android:paddingTop="1dp"
        android:singleLine="true"
        android:textSize="11sp"/>

</LinearLayout>

我的适配器代码:

我也尝试调用 invalidate(),但看不到任何影响。

@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
    Item item = getItem(position);
    NormalViewHolder viewHolder = (NormalViewHolder) holder;

    viewHolder.mName.setText(item.getName());
    //viewHolder.mName.invalidate(); // No help
    viewHolder.mCode.setText(item.getCode());
    //viewHolder.mCode.invalidate(); // No help
}

预期行为:

+----------------------------------------+
|[Name][Code]                            |
+----------------------------------------+
|[Name qwertyuiopasdfghjklzxcvb...][Code]|
+----------------------------------------+

如果我向上/向下滚动列表,这就是现在的工作方式。所以布局没有更新,代码也没有对齐名称。此外,名称并不总是占用足够的空间。

+----------------------------------------+
|[Name][Code]                            |
+----------------------------------------+
|[Name qwer...]                [Code]    |
+----------------------------------------+
|[Name qwertyuiopa][Code]                |
+----------------------------------------+
|[Name qwe]        [Code]                |
+----------------------------------------+

【问题讨论】:

  • 你能发布一些关于这个问题的照片/视频吗?您还可以发布您的onBindViewHolder 方法实现的完整代码吗?
  • 请截图?
  • 为什么第二个文本视图在线性布局中?愿意为我详细说明这里的逻辑吗?
  • 确保您没有调用 Recycle View 类的方法,即 setHasFixedSize(true)。建议使用false参数调用该方法。
  • 我已经回答了你的问题,看看这是不是你想要做的。

标签: android android-layout android-recyclerview


【解决方案1】:

我分享我不需要使用的解决方案

setIsRecyclable(false)

在您的 xml 中使用以下自定义 TextView。

public class TextViewForRecyclerView extends android.support.v7.widget.AppCompatTextView {
    public TextViewForRecyclerView(Context context) {
        super(context);
    }

    public TextViewForRecyclerView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public TextViewForRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    public void setText(CharSequence text, BufferType type) {
        super.setText(text, type);
        requestLayout();
    }
}

【讨论】:

  • 谢谢!当 RecyclerView 由使用数据绑定的库支持时,这是唯一对我有用的解决方案。
  • requestLayout() 在 onBindViewHolder() 中工作
【解决方案2】:

此 xml 对我有用,无需使用 setIsRecyclable(false);
在某些 android 版本(如 kitkat)中,代码:android:ellipsize="end" 仅适用于 android:singleLine="true",因此将其添加到第一个 TextView "@+id/name" 即可!

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="150dp"
android:layout_marginLeft="16dp"
android:orientation="horizontal">
   <TextView
    android:id="@+id/name"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:layout_marginRight="8dp"
    android:layout_weight="1"
    android:ellipsize="end"
    android:singleLine="true"
    android:maxLines="1"
    />
   <TextView
    android:id="@+id/code"
    android:layout_weight="0"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="@drawable/background_stop_code_small"
    android:ellipsize="end"
    android:paddingBottom="1dp"
    android:paddingLeft="7dp"
    android:paddingRight="7dp"
    android:paddingTop="1dp"
    android:singleLine="true"
    android:textSize="11sp"/>
</LinearLayout>


结果:

【讨论】:

  • 不幸的是,这对我不起作用。我的问题是我有 2 个彼此相邻的文本视图(在左侧),第一个有背景。回收时背景渲染不正确。
  • 你在 ConstraintLayout 中使用那些文本视图吗?
  • 我通过改变 backgroundTint 而不是背景克服了这个问题。 (我有 2 个不同的 XML 形状,只是颜色不同)。关于您的问题,我尝试过线性和约束布局,结果相同。现在我正在使用约束布局。
【解决方案3】:
As it seems code is of constant length, you can use following UI deisgn;
<RelativeLayout
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginLeft="16dp"
    android:gravity="center_vertical">

    <TextView
        android:id="@+id/name"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:align_parentLeft="true"
        android:layout_toLeftOf="@+id/code"
        android:layout_marginRight="8dp"
        android:ellipsize="end"
        android:maxLines="1" />

        <TextView
            android:id="@+id/code"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@drawable/background_code"
            android:gravity="center"
            android:align_ParentRight="true"
            android:ellipsize="end"
            android:paddingBottom="1dp"
            android:paddingLeft="7dp"
            android:paddingRight="7dp"
            android:paddingTop="1dp"
            android:singleLine="true"/>

</RelativeLayout>

【讨论】:

  • 实际上代码可以是可变长度的,我最初的问题对此有点误导。
【解决方案4】:

RecyclerView 将重用 ViewHolder 对象,因此它们的布局将被膨胀一次,然后在列表的整个生命周期内重用。尝试在您的根目录LinearLayout 上调用requestLayout

Usage of forceLayout(), requestLayout() and invalidate()

【讨论】:

  • 我尝试将 viewHolder.mLayoutRoot.requestLayout() 调用添加到我的适配器 onBindViewHolder 的末尾,但我的布局仍然没有正确更新。如果文本名称发生更改,LinearLayout code_container widht 不会更新。
【解决方案5】:

您需要使两个TextView 视图无效,并确保在为它们设置新的文本值后执行此操作:

@Override
public void onBindViewHolder(ItemHolder holder, int position)
{
    ...

    holder.nameTextView.invalidate();
    holder.codeTextView.invalidate();
}

作为替代方案,如果您不介意代码视图向右对齐,您可以将 LinearLayout 替换为 RelativeLayout 并在名称视图和代码容器之间设置适当的约束:

<TextView
    android:id="@+id/name"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginRight="8dp"
    android:layout_centerVertical="true"
    android:layout_alignParentStart="true"
    android:layout_alignParentLeft="true"
    android:layout_toLeftOf="@+id/code_container"
    android:layout_toStartOf="@+id/code_container"
    android:ellipsize="end"
    android:maxLines="1" />

<LinearLayout
    android:id="@+id/code_container"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_centerVertical="true"
    android:layout_alignParentEnd="true"
    android:layout_alignParentRight="true"
    android:background="@drawable/background_code"
    android:orientation="vertical">

    <TextView
        android:id="@+id/code"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:ellipsize="end"
        android:paddingBottom="1dp"
        android:paddingLeft="7dp"
        android:paddingRight="7dp"
        android:paddingTop="1dp"
        android:singleLine="true"/>
</LinearLayout>

【讨论】:

    【解决方案6】:

    我对 recyclerView 有非常相似的问题。 不幸的是,当物品回收时,它有时无法正常工作。

    对我来说帮助关闭可回收。

            setIsRecyclable(false);
    

    你可以在 ViewHolder 的构造函数中这样做:

    class Holder extends RecyclerView.ViewHolder {
    
        public Holder(View itemView) {
            super(itemView);
            setIsRecyclable(false);
    
        }
    }
    

    当然,这不是很棒的解决方案,但它是唯一对我有帮助的解决方案。

    希望对你有帮助。

    【讨论】:

    • 谢谢!到目前为止,这只是可行的解决方案,但我希望有更好的方法来刷新我需要的视图?
    • 不幸的是,这只是一种可行的解决方案。在我的情况下,第一个文本视图的背景看起来被剪掉了(圆角背景)
    【解决方案7】:

    我终于得到了正确的答案。作为我的测试,你可以在你的方法onBindViewHolder中添加两行,如下:

    @Override
    public void onBindViewHolder(ItemHolder holder, int position)
    {
        holder.mName.setSingleLine(false);    //first line
        holder.mName.setText("name");
        holder.mCode.setText("");
        holder.mName.setSingleLine(true);    //second line
    }
    

    希望对你有帮助。

    【讨论】:

      【解决方案8】:

      您也应该为内部线性布局添加权重,然后它将是

      <TextView
          android:id="@+id/name"
          android:layout_width="0dp"
          android:layout_height="wrap_content"
          android:layout_marginRight="8dp"
          android:layout_weight="2"
          android:ellipsize="end"
          android:maxLines="1" />
      
      <LinearLayout
          android:id="@+id/code_container"
          android:layout_width="0dp"
          android:layout_weight="1"
          android:layout_height="wrap_content"
          android:layout_gravity="center_vertical"
          android:background="@drawable/background_code"
          android:orientation="vertical">
      
          <TextView
              android:id="@+id/code"
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:ellipsize="end"
              android:paddingBottom="1dp"
              android:paddingLeft="7dp"
              android:paddingRight="7dp"
              android:paddingTop="1dp"
              android:singleLine="true"/>
      
      </LinearLayout>
      

      试试这个对你有用

      【讨论】:

        【解决方案9】:

        我在使用 RecyclerView 的类似聊天的 Activity 中遇到了布局刷新问题:我从 onBindViewHolder 调用了一个方法来调整任何聊天消息的边距,代码如下: .. RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) this.mItemView.getLayoutParams(); lp.setMargins(0,0,50,0);

        【讨论】:

          【解决方案10】:

          试试这个我没有使用编辑器,但如果xml有任何问题,它会自动更正

          <?xml version="1.0" encoding="utf-8"?>
          <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
          xmlns:tools="http://schemas.android.com/tools"
          android:layout_width="match_parent" //i changed this
          android:layout_height="150dp"
          android:layout_marginLeft="16dp"
          android:weightsum="1"//added this
          android:orientation="horizontal">
          
          <TextView
              android:id="@+id/name"
              android:layout_width="0dp"
              android:layout_height="wrap_content"
              android:layout_marginRight="8dp"
              android:layout_weight="1"
              android:ellipsize="end"
              android:singleLine="true" />//added this
          
          <TextView
              android:id="@+id/code"
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:background="@drawable/background_stop_code_small"
              android:ellipsize="end"
              android:paddingBottom="1dp"
              android:paddingLeft="7dp"
              android:paddingRight="7dp"
              android:paddingTop="1dp"
              android:singleLine="true"
              android:textSize="11sp"/>
          
          </LinearLayout>
          

          以上方法都可以

          如果您仍然坚持更新布局,请始终知道您需要的更新是否是测量,然后您需要致电 View.invalidate()View.requestLayout()

          【讨论】:

          • 我无法将 root linearLayout 宽度设置为 match_parent,因为我的代码视图始终位于屏幕右侧。代码需要在名称视图之后。
          【解决方案11】:

          尝试使用这个,我遇到了同样的问题,我用这个解决了它:

          @Override
              public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
                  View rootView = LayoutInflater.from(context).inflate(R.layout.review_data, null, false);
                  RecyclerView.LayoutParams lp = new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
                  rootView.setLayoutParams(lp);
                  return new ViewHolder(rootView);
              }
          

          像上面一样在 onCreateViewHolder 中设置布局参数。

          【讨论】:

            【解决方案12】:

            只在其中一个上设置权重是错误的,因为当code 很长时,name 会消失。 我不知道你想达到什么目的,但即使使用简单的 ListView 和如下所示的简单布局,它们也永远不会消失,我认为它们看起来更好:

            <?xml version="1.0" encoding="utf-8"?>
            <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
            xmlns:tools="http://schemas.android.com/tools"
            android:layout_width="wrap_content"
            android:layout_height="150dp"
            android:gravity="left"
            android:layout_marginLeft="16dp"
            android:orientation="horizontal">
            
            <TextView
                android:id="@+id/name"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_weight=".5"
                android:layout_marginRight="8dp"
                android:ellipsize="end"
                android:text="dfsdd45te5sf"
                android:maxLines="1" />
            
            <TextView
                android:id="@+id/code"
                android:layout_weight=".5"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:background="@android:color/holo_green_dark"
                android:ellipsize="end"
                android:paddingBottom="1dp"
                android:paddingLeft="7dp"
                android:paddingRight="7dp"
                android:paddingTop="1dp"
                android:layout_marginRight="1dp"
                android:text="ertertete5tte5tte5e5te5t"
                android:singleLine="true"
                android:textSize="12sp"/>
            </LinearLayout>
            

            【讨论】:

              【解决方案13】:

              我想你期待像这样

              在顶部容器 LinearLayout 中将 "wrap_content" 更改为 "match_parent"

              查看本教程 http://wiki.workassis.com/android-recyclerview-example/

              【讨论】:

                【解决方案14】:

                嗯,我没有得到你所面临的确切问题......但我可以解决一个问题 在您的代码中看到的是重量,您以错误的方式使用重量。如果您在一个小部件中使用权重将无效,至少您必须使用两个小部件在它们之间赋予适当的权重,并在其父级中使用权重总和才能正常工作。例如,请参阅下面您修改的代码:-

                <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginLeft="16dp"
                    android:gravity="center_vertical"
                    android:orientation="horizontal"
                    android:weightSum="3">
                
                    <TextView
                        android:id="@+id/name"
                        android:layout_width="0dp"
                        android:layout_height="wrap_content"
                        android:layout_marginRight="8dp"
                        android:layout_weight="1"
                        android:ellipsize="end"
                        android:text="qwertyuioopasdfsd" />
                
                    <LinearLayout
                        android:id="@+id/code_container"
                        android:layout_width="0dp"
                        android:layout_height="wrap_content"
                        android:layout_gravity="center_vertical"
                        android:layout_weight="2"
                        android:background="@drawable/background_code"
                        android:orientation="vertical">
                
                        <TextView
                            android:id="@+id/code"
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:ellipsize="end"
                            android:paddingBottom="1dp"
                            android:paddingLeft="7dp"
                            android:paddingRight="7dp"
                            android:paddingTop="1dp" />
                
                    </LinearLayout>
                
                </LinearLayout>
                

                【讨论】:

                  猜你喜欢
                  • 2018-09-18
                  • 2020-06-04
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2020-12-05
                  • 2016-01-21
                  • 2017-08-12
                  相关资源
                  最近更新 更多