StaggeredGridLayoutManager and moving items

人盡茶涼 提交于 2019-11-26 15:32:34

问题


I have created a very simple project, displaying 28 images with StaggeredGridLayoutManager by recyclerview. but as I scroll the recyclerview it moves items for example from left to right or swap the column of left and right.

codes:

import java.util.ArrayList;
import java.util.List;

import android.app.Activity;
import android.os.Bundle;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.StaggeredGridLayoutManager;


public class MainActivity extends Activity {


    String mImageDir;
    private RecyclerView mRecyclerView;
    private StaggeredGridLayoutManager mLayoutManager;



    MyRecyclerAdapter myRecyclerAdapter;
    List<ImageModel> mImageList;

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);   
        mRecyclerView = (RecyclerView)findViewById(R.id.recyclerview_rootview);
        mLayoutManager = new StaggeredGridLayoutManager(2,StaggeredGridLayoutManager.VERTICAL);     
        mLayoutManager.setGapStrategy(StaggeredGridLayoutManager.GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS);        
        mRecyclerView.setLayoutManager(mLayoutManager);
        mRecyclerView.setHasFixedSize(false);
        mImageList = new ArrayList<ImageModel>();
        for (int i = 1; i < 29 ; i++) {
            ImageModel img = new ImageModel();
            img.setTitle("Image No " + i);
            int drawableResourceId = this.getResources().getIdentifier("image"+String.valueOf(i), "drawable", this.getPackageName());
            img.setResId(drawableResourceId);
            mImageList.add(img);

        }
        myRecyclerAdapter = new MyRecyclerAdapter(MainActivity.this,mImageList);        
        mRecyclerView.setAdapter(myRecyclerAdapter);

    }

} 

And the adapter:

public class MyRecyclerAdapter extends RecyclerView.Adapter<MyRecyclerAdapter.ViewHolder> {


    private List<ImageModel> mItems;
    Context mContext;

    public MyRecyclerAdapter(Context context,List<ImageModel> objects) {
        mContext = context;
        mItems = objects;

    }

    static class ViewHolder extends RecyclerView.ViewHolder{
        public  ImageView mImageView;
        public  TextView mTextView;
        public View rootView;
        public ViewHolder(View itemView) {
            super(itemView);
            rootView = itemView;
            mImageView =(ImageView)itemView.findViewById(R.id.image);
            mTextView =(TextView)itemView.findViewById(R.id.title);
        }
    }

    @Override
    public int getItemCount() {

        return mItems.size();
    }

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

        ImageModel item = mItems.get(position); 
        Picasso.with(mContext).load(item.getResId()).into(holder.mImageView);
        holder.mTextView.setText(item.getTitle());
    }


    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int arg1) {
        LayoutInflater inflater =    
                (LayoutInflater) mContext.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
        View convertView = inflater.inflate(R.layout.item, parent, false);
        return new ViewHolder(convertView);

    }

}

and a sample moving item:

http://i.imgur.com/FUapm2K.gif?1

if you play (scroll up and down) you can discover more interesting animation :-)

How to prevent that and having stable layout like an ordinary listview?

Edit

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

    ImageModel item = mItems.get(position);
    RelativeLayout.LayoutParams rlp = (RelativeLayout.LayoutParams)holder.mImageView.getLayoutParams();
    float ratio = item.getHeight()/item.getWidth();
    rlp.height = (int)(rlp.width * ratio);
    holder.mImageView.setLayoutParams(rlp);
    Picasso.with(mContext).load(item.getResId()).into(holder.mImageView);
    holder.mTextView.setText(item.getTitle());
}

回答1:


This is happening because SGLM does not keep any w/h information about the views. So each time a View is rebound, it gets the place holder size first and then the final size when the image is loaded.

Loading the actual image w/ different size (than place holder) triggers a new layout request, and during that layout request, SGLM detects that there will be gaps in the UI (or some item w/ higher position appears below an item w/ lower position) thus re-orders items w/ an animation.

You can avoid it by setting your place holder image to the dimensions of the actual image. If you don't have it ahead of time, you can save them after the image is loaded for the first-time and use it in the next onBind call.




回答2:


What worked for me was to disable all animation on the recycle view when using StaggeredGridLayoutManager.

mRecyclerView.setItemAnimator(null);

You can create your own animator if you only want to restrict moving animations and keep the add and remove item animations.




回答3:


You can try this

    StaggeredGridLayoutManager manager = new StaggeredGridLayoutManager(2, OrientationHelper.VERTICAL);
    manager.setGapStrategy(StaggeredGridLayoutManager.GAP_HANDLING_NONE);
    mRecyclerView.setLayoutManager(manager);

after you set this, you'll find there is a blank at the top when you scroll to the top. continue to set this

    mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
        @Override
        public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
            super.onScrollStateChanged(recyclerView, newState);
            ((StaggeredGridLayoutManager)recyclerView.getLayoutManager()).invalidateSpanAssignments();
        }
    });

It's work for me, I get this way somewhere. I hope it can help you!




回答4:


Change this line

mLayoutManager.setGapStrategy(
        StaggeredGridLayoutManager.GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS); 

to this line

mLayoutManager.setGapStrategy(
        StaggeredGridLayoutManager.GAP_HANDLING_NONE);           



回答5:


Add the following line at the end of the method: holder.mImageView.requestLayout();

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

This fixed the issue for me.




回答6:


Maybe this is a more efficient way:

mBrandRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
        @Override
        public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
            if(newState == RecyclerView.SCROLL_STATE_IDLE){
                mBrandRecyclerView.invalidateItemDecorations();
            }
        }
    });

For more information: https://github.com/ibosong/CommonItemDecoration




回答7:


Set your recyclerview's height fixed and item height and width wrap-content for staggered-layout-manager




回答8:


First set gap strategy like following code :

mLayoutManager = new StaggeredGridLayoutManager(SPAN_COUNT, StaggeredGridLayoutManager.VERTICAL);
                            mLayoutManager.setGapStrategy(StaggeredGridLayoutManager.GAP_HANDLING_NONE);

and then add your item to mItems and then use:

mAdapter.notifyItemInserted(mItems.size() - 1);

this method is better than using:

mAdapter.notifyDataSetChanged();


来源:https://stackoverflow.com/questions/27636999/staggeredgridlayoutmanager-and-moving-items

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