问题
I have two text views side by side in my RecyclerView item. One of the views has layout_weight="1" to keep another view visible if the view is wide.
The problem is that the the view sizes are not updating when the views got recycled, so to textView id:name width is not correct when i scroll the list.
Should I somehow force the view to update the layout?
My RecyclerView item:
<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>
Im using RecyclerView v. 23.4.0.
Update
1. I have tried to call my RecyclerView item layout root requestLayout at the end of the adapter onBindViewHolder().
This does not any have affect, my views are not updated.
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
...
viewHolder.mLayoutRoot.requestLayout();
}
2. Maybe the problem is that the view is not yet completely drawn when I call requestLayout? So I tried to set GlobalLayoutListener and call requestLayout there. I think this helps little, but still if I have enough items, in my list not all views are updated. Owerall in my list I have around max 10 items.
ViewTreeObserver vto = viewHolder.mLayoutRoot.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
viewHolder.mLayoutRoot.requestLayout();
}
});
3. I tried to use TableLayout with shrinkColumns property to approach similar layout, but the result is same. Name text view widht is not always correctly set. Also, it looks like requestLayout has no affect at all (or am I calling it in wrong place?)
4. (28/8/2016)
I stripped down my code to bare minimum to reproduce this.
I managed to fix this using setIsRecyclable(false), like dosssik suggests, but I think it is not very good solution in terms of performance?
Here is my full list item:
<?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>
My adapter code:
I also tried to call invalidate(), but can not see any affect.
@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
}
Expected behaviour:
+----------------------------------------+
|[Name][Code] |
+----------------------------------------+
|[Name qwertyuiopasdfghjklzxcvb...][Code]|
+----------------------------------------+
This is how works right now if I scroll list up/down. So the layout is not updated and code is not alignet right to name. Also name is not taking always enough space.
+----------------------------------------+
|[Name][Code] |
+----------------------------------------+
|[Name qwer...] [Code] |
+----------------------------------------+
|[Name qwertyuiopa][Code] |
+----------------------------------------+
|[Name qwe] [Code] |
+----------------------------------------+
回答1:
This xml works for me without using setIsRecyclable(false);
In some android versions (like kitkat) the code: android:ellipsize="end"
only works with android:singleLine="true"
, so add this to the first TextView "@+id/name" and it will works!
<?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>
The result:
回答2:
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>
回答3:
The RecyclerView
will reuse ViewHolder
objects so the layouts of those will be inflated once and then reused through the lifetime of the list. Try calling requestLayout
on your root LinearLayout
.
Usage of forceLayout(), requestLayout() and invalidate()
回答4:
You need to invalidate the two TextView
views, and make sure you are doing it after you set the new text value to them:
@Override
public void onBindViewHolder(ItemHolder holder, int position)
{
...
holder.nameTextView.invalidate();
holder.codeTextView.invalidate();
}
As an alternative, if you don't mind the code view aligning to the right, you could replace the LinearLayout
for a RelativeLayout
and set the proper constraints between the name view and the code container:
<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>
回答5:
I finally got the right answer. As my test, you can just add two lines in you method onBindViewHolder
, as below:
@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
}
Hope it help you.
回答6:
you should add weight to inner linear layout also then it will be
<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>
try this will work for you
回答7:
I have had layout refresh problems in a chat-like Activity using RecyclerView: I've called a method from onBindViewHolder to adapt any chat message's margins, below a code snippet: .. RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) this.mItemView.getLayoutParams(); lp.setMargins(0,0,50,0);
回答8:
I had very simillar problem with recyclerView. Sometimes it works not properly, when items recycling, unfortunatly.
for me helped turning off recyclable.
setIsRecyclable(false);
you can do it in ViewHolder's constuctor like this:
class Holder extends RecyclerView.ViewHolder {
public Holder(View itemView) {
super(itemView);
setIsRecyclable(false);
}
}
Sure, that not awesome solution, but it's the only solution, that helped me.
Hope it will help you.
回答9:
try this i am not use editor but it will auto-correct if any problem with 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>
the above will work
if you still insist of updating layout, always know if the update you need is measurement then you need to call View.invalidate()
along with View.requestLayout()
回答10:
Try using this, I had same issue, i resolved it using this :
@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);
}
Set layout params in onCreateViewHolder like above.
回答11:
setting weight on just one of them is wrong, because when the code
is long the name
will get disappeared.
I don't know what are you trying to achieve, but even with a simple ListView and a simple layout like below for its items they will never get disappeared and I think they look better:
<?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>
回答12:
I think you are expecting Out put simile like this
in the top container LinearLayout change "wrap_content" to "match_parent"
check this tutorial http://wiki.workassis.com/android-recyclerview-example/
回答13:
I share my solution which is not need to use
setIsRecyclable(false)
Use below custom TextView at your xml.
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();
}
}
回答14:
Well, I am not getting exact problem what you are facing...but one problem i can see in your code is weight, you are using weight in a wrong way. weight will not effective if you use in one widget, at-least you have to use two widget to give proper weight between them, and use weight sum in their parent to work properly. for example see below your modified code:-
<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>
来源:https://stackoverflow.com/questions/39027193/recyclerview-item-layout-weight-sizes-not-updating