notifyItemChanged() make the RecyclerView scroll and jump to UP

我只是一个虾纸丫 提交于 2020-07-01 12:00:55

问题


i have a RecycleView with an adapter that show a list of servers and the user must select one server.

when i call notifyItemChanged(previousPosition) inside the onClick() method to make the old server unselected and the new server selected, that's make the RecycleView list jump to up exactly in the middle of list.

and this problem happen just when i click on one of the last 2 or 3 servers inside the RecycleView list

here is the code of my RecyclerView.Adapter :

public class ServerAdapter extends RecyclerView.Adapter<ServerAdapter.ServerViewHolder> {

    private List<Server> listServers = new ArrayList<>();
    private int[] icons = new int[]{R.drawable.server1,R.drawable.server2,R.drawable.server3,R.drawable.server4,R.drawable.server5,R.drawable.server6,R.drawable.offline};
    private int selected = 0;
    private int previousSelected = 0;

    public ServerAdapter(List<Server> listServers){
        this.listServers = listServers;
    }

    @Override
    public ServerViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.server_relative_layout,parent,false);
        return new ServerViewHolder(view);
    }

    @Override
    public void onBindViewHolder(final ServerViewHolder holder, final int position) {
        if(position == selected){
            holder.getBackground().setSelected(true);
        }else{
            holder.getBackground().setSelected(false);
        }
        holder.getBackground().setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(position != selected){
                    previousSelected = selected;
                    selected = position;
                    holder.getBackground().setSelected(true);
                    notifyItemChanged(previousSelected);
                }
            }
        });
        holder.getImageServer().setImageResource(icons[position%6]);
        holder.getTextNameServer().setText(listServers.get(position).getName());
        holder.getTextConnected().setText(listServers.get(position).getUrl());
    }

    @Override
    public int getItemCount() {
        return listServers.size();
    }

    public class ServerViewHolder extends RecyclerView.ViewHolder{
        private ImageView imageServer;
        private TextView textNameServer;
        private TextView textConnected;
        private View background;
        public ServerViewHolder(View itemView) {
            super(itemView);
            imageServer = (ImageView)itemView.findViewById(R.id.imageServer);
            textNameServer = (TextView)itemView.findViewById(R.id.textNameServer);
            textConnected = (TextView)itemView.findViewById(R.id.textConnected);
            background = itemView;
        }

        public ImageView getImageServer() {
            return imageServer;
        }

        public TextView getTextConnected() {
            return textConnected;
        }

        public TextView getTextNameServer() {
            return textNameServer;
        }

        public View getBackground() {
            return background;
        }
    }
}

any solutions to solve this problem ? thanks.

The problem happened exactly when i specify the layout height and do not let it to wrap_content

<android.support.v7.widget.RecyclerView
    android:layout_width="wrap_content"
    android:layout_height="400dp"
    android:id="@+id/serverRecyclerView"
    android:layout_margin="10dp"
/>

or when i put it below something for expample like that :

<android.support.v7.widget.RecyclerView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:id="@+id/serverRecyclerView"
    android:layout_margin="10dp"
    android:layout_below="@+id/image"/>

my code exactly is :

<android.support.v7.widget.RecyclerView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:id="@+id/serverRecyclerView"
    android:layout_margin="10dp"
    android:layout_alignTop="@+id/imageBall"
    android:layout_alignParentRight="true"
    android:layout_alignParentEnd="true"
    android:layout_toRightOf="@+id/camera"
    android:layout_toEndOf="@+id/camera"/>

回答1:


Looks like this is a bug: https://code.google.com/p/android/issues/detail?id=203574

The best workaround seems to be Bart's answer to set the RecyclerView's LinearLayoutManager's AutoMeasure property to false.

  LinearLayoutManager llm = new LinearLayoutManager(context);
  llm.setAutoMeasureEnabled(false);       
  recyclerView.setLayoutManager(llm);

The set FixedSize to true solution had way too many side-effects...

RecyclerView.setHasFixedSize(true)




回答2:


 android:descendantFocusability="blocksDescendants"

android:descendantFocusability="blocksDescendants"

this attr solve my bug




回答3:


I don't know why, but I used:

RecyclerView.setHasFixedSize(true)

This worked for me. I hope it can help.




回答4:


My RecyclerView was inside ConstraintLayout, and I also had such problem and calling setAutoMeasureEnabled(false) of RecyclerView's LayoutManager did not fix the issue for me, furthermore this method is deprecated in 28.0.0 version. What I did is that, I wrapped my RecyclerView with RelativeLayout and now it works like a charm. As mentioned in bugtracker, this "issue" is intented behaviour in LinearLayout and is not going to be fixed. So if it is possible, just wrap your RecyclerView something like this:

<RelativeLayout
    android:id="@+id/container_messages_list"
    android:layout_width="match_parent"
    android:layout_height="0dp"
    android:background="@drawable/chat_back_pattern"
    app:layout_constraintBottom_toTopOf="@+id/bottom_view"
    app:layout_constraintTop_toBottomOf="@+id/toolbar">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/messages_list"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    </android.support.v7.widget.RecyclerView>
</RelativeLayout>



回答5:


RecyclerView.ItemAnimator animator = myRecyclerListView.getItemAnimator();
if (animator instanceof SimpleItemAnimator) {
    ((SimpleItemAnimator)animator).setSupportsChangeAnimations(false);
}



回答6:


for anyone who stumbles upon this issue, try using

yourRecyclerView.notifyItemChanged(int position, Object payload);

This one did the trick for me.

Using

setAutoMeasureEnabled(false);

also worked but in some edge cases recycler view was acting weird. Good luck!




回答7:


I came across the similar problem, just take care of the xml layout file.
Do not use the layout_below , layout_above or others similar properties in RecyclerView or RecyclerView's parent view.
You can use LinearLayout weight , layout_marginBottom or sth to achieve
layout_below or other.




回答8:


The late answer better than nothing, if you're using NestedScrollView as the parent view of RecyclerView you should delete it.




回答9:


I had a similar problem and I tryed all solutions listed above, but noone worked. I was already padding the "Payloads" to "notifyItemChanged(position, payloads)" because I just needed to "upload" a checkbox value so I was passing the value inside "Payloads" without recalling the update of the entire viewholder. This solution worked for all view holders in my recycler view except for the last one (and probably for all "recycled" ones, I mean those who recall the "onBindViewHolder" by "recycling" an existing view). I think using "notifyItemChanged" will works if you have only the recyclerview and I also think that this problem of "auto-scrolling" is raised by nested scroll views & recycler views.

I was in the case exposed by "raed", so "ScroolView -> RecyclerView -> "n" x RecyclerView". I have a scroolview wich contains a recyclerview whose viewholders can contains a recycler views.

Delete the parent ScrollView is a really weird solution and I couldn't use it, so I setted the "onStopNestedScroll" inside the "ScrollView" and not inside the RecyclerView.

Personally I used it programmatically before the code part which calls the "notifyItemChanged" method by doing:

msvContainer.onStopNestedScroll(mRecyclerView);

Where "msvContainer" is my ScrollView which contains the RecyclerView, and "mRecyclerView" is my RecyclerView contained by the ScrollView. This way worked 99% because the first time I call "notifyItemChanged" the view scroll up only for the ScrollView, so it hides a button inside my ScrollView which is below my RecyclerView but it doesn't scroll the RecyclerView items. After the first call "notifyItemChanged" works properly.

I found that calling:

msvContainer.stopNestedScroll();

works too. But i suggest to use the first method with the target view if you have multiple nested scroll views.

Anyway you should call "startNestedScroll" after you ran out of the critical part of re-updating your view holder because the targeted view, so in my case the RecyclerView, won't scroll until you call this method so it won't recycler his view holders too.

(In my case that I have multiple Recycler View inside a parent Recycler View inside a parent Scroll View if I was in need to call "notifyItemChanged" inside the most inner Recycler View i would use the "stopNestedScroll" method for every parent view and then re-activated the scroll after the scroll-critical part)

Hope this is helpful, have a nice coding! Bye



来源:https://stackoverflow.com/questions/36724898/notifyitemchanged-make-the-recyclerview-scroll-and-jump-to-up

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!