How to set animation on recyclerview - android

I am creating an app using new API 21 (Lollipop) i am setting animation slide_in_left on recyclerview to animate cardview 1 by 1 but when items appear they will come together and i want them to slide_in_left 1 by 1 and 2nd i am animating toolbar bar to hide/show its working but problem is when i scroll up and when recyclerview stops then it will hide and same case when scroll down, i want to hide/show toolbar when scroll starts anyone help me. Thanks in advance
belowis Myadapter class for recyclerview animation
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
private ArrayList<PersonData> peopleDataSet;
private Context context;
View view;
public static class MyViewHolder extends RecyclerView.ViewHolder {
TextView textViewName;
TextView textViewEmail;
ImageView imageViewIcon;
public MyViewHolder(View itemView) {
super(itemView);
this.textViewName = (TextView) itemView.findViewById(R.id.textViewName);
this.textViewEmail = (TextView) itemView.findViewById(R.id.textViewEmail);
this.imageViewIcon = (ImageView) itemView.findViewById(R.id.imageView);
}
}
public MyAdapter(ArrayList<PersonData> people,Context context) {
this.peopleDataSet = people;
this.context=context;
}
#Override
public MyViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.cards_layout, parent, false);
view.setOnClickListener(MainActivity.myOnClickListener);
setAnimation(view);
MyViewHolder myViewHolder = new MyViewHolder(view);
return myViewHolder;
}
#Override
public void onBindViewHolder(final MyViewHolder holder, final int listPosition) {
TextView textViewName = holder.textViewName;
TextView textViewEmail = holder.textViewEmail;
ImageView imageView = holder.imageViewIcon;
textViewName.setText(peopleDataSet.get(listPosition).getName());
textViewEmail.setText(peopleDataSet.get(listPosition).getEmail());
imageView.setImageResource(peopleDataSet.get(listPosition).getImage());
}
#Override
public int getItemCount() {
return peopleDataSet.size();
}
/**
* Here is the key method to apply the animation
*/
private void setAnimation(View viewToAnimate)
{
// If the bound view wasn't previously displayed on screen, it's animated
Animation animation = AnimationUtils.loadAnimation(context, android.R.anim.slide_in_left);
viewToAnimate.startAnimation(animation);
}
below is toolbar code
#SuppressLint("NewApi") #Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
if (dy > 0) {
toolbar.animate()
.translationY(-toolbar.getBottom())
.setInterpolator(new AccelerateInterpolator())
.start();
} else {
toolbar.animate()
.translationY(0)
.setInterpolator(new AccelerateInterpolator())
.start();
}

Unfortunately, RecyclerView does not support animation while scrolling with RecyclerView.ItemAnimator.
If you want to achieve one by one animation of RecyclerView items, you should animate each viewHolder.itemView inside onBindView method. Keeping track of last animated position is important to avoid animation when scrolling down, unless that is not your intention though.
Here is simple alpha animation I did:
public interface RecyclerViewItemOnScrollAnimator {
public void onAnimateViewHolder(RecyclerView.ViewHolder viewHolder, int position);
public void onPrepareToAnimateViewHolder(RecyclerView.ViewHolder viewHolder);
}
Here is implementation of the Animator (sort of, couldn't come up with better name)
public class RecyclerViewScrollAnimator implements RecyclerViewItemOnScrollAnimator{
private final LinearLayoutManager mLayoutManager;
private final RecyclerView mRecyclerView;
private final Interpolator mInterpolator;
int mLastAnimatedPosition = RecyclerView.NO_POSITION;
public RecyclerViewScrollAnimator(RecyclerView recyclerView) {
mLayoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
mRecyclerView = recyclerView;
mInterpolator = InterpolatorUtil.loadInterpolator(recyclerView.getContext());
}
#Override
public void onAnimateViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
onPrepareToAnimateViewHolder(viewHolder);
if(shouldAnimateOrPrepare(position)){
ViewCompat.animate(viewHolder.itemView)
.alpha(1)
.setInterpolator(mInterpolator)
.setDuration(300).start();
}
mLastAnimatedPosition = position;
}
private boolean shouldAnimateOrPrepare(int position) {
return mLastAnimatedPosition == RecyclerView.NO_POSITION || mLastAnimatedPosition < position;
}
#Override
public void onPrepareToAnimateViewHolder(RecyclerView.ViewHolder viewHolder) {
// do some stuff if needed before animation
if(shouldAnimateOrPrepare(position)){
ViewCompat.setAlpha(viewHolder.itemView, 0);
}
}
//clear animation if needed
private void cancelAnimationAt(int position) {
RecyclerView.ViewHolder v = mRecyclerView.findViewHolderForPosition(position);
if(!isVisible(position) || v != null){
if(v != null) {
Log.i("cancelAnimationAt", " canceling "+position);
//cancelAnimation(v.itemView);
}
}
}
private void cancelAnimation(View v) {
if(v != null) {
v.clearAnimation();
v.setAnimation(null);
ViewCompat.animate(v).cancel();
}
}
private boolean isVisible(int position){
return position >= mLayoutManager.findFirstVisibleItemPosition() && position <= mLayoutManager.findLastVisibleItemPosition();
}
}
And here is how you use it in your RecyclerView.Adapter
#Override
public void onBindViewHolder(RecyclerView.ViewHolder vh, int position) {
if(getItemViewType(position) == VIEW_TYPE_ITEM) {
bindItem(vh, position-1); // position only if no header otherwise position-1
mRecyclerViewAnimator.onAnimateViewHolder(vh, position);
}
else return; // don't bind the header
}
Now you should modify the code inside onAnimateViewHolder() to use your animation resource. That's trivial.
On your Toolbar toggling, I suggest you to use this library. It has really handy scroll callbacks for your purpose.

Related

Setup span in StaggeredGridLayoutManager

I need to create a view like the following (this is an iOS screenshot since I've already created it).
Each blue box is part of a linear layout of a RecyclerView, while each green item is part of a staggered layout.
I'm able to create that structure. The problem is that I cannot make each gray box half of the yellow box. Using the StaggeredGridLayoutManager I'm not able to control the span (size) of the boxes that appear for the inner recycler view. Only a GridLayoutManager has the chance to set a span size lookup.
The desiderata is to calculate the boxes based on the with of the container. For example: if the screen is 99, the yellow box should be 66 while the gray 33.
Do you have any suggestions on how to achieve this?
Here a snippet of the code I'm using:
public class BoxViewAdapter extends RecyclerView.Adapter<BoxViewAdapter.BoxViewHolder> {
private List<Box> mValues;
private Context mContext;
protected BoxViewAdapter.ItemListener mListener;
public BoxViewAdapter(Context context, List<Box> values, BoxViewAdapter.ItemListener itemListener) {
mValues = values;
mContext = context;
mListener = itemListener;
}
#Override
public BoxViewAdapter.BoxViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(mContext).inflate(R.layout.box_view_item, parent, false);
return new BoxViewAdapter.BoxViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull BoxViewAdapter.BoxViewHolder holder, int position) {
holder.bind(mValues.get(position));
}
#Override
public int getItemCount() {
return mValues.size();
}
public interface ItemListener {
void onItemClick();
}
public class BoxViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
public Box item;
private RecyclerView recyclerView;
public BoxViewHolder(View v) {
super(v);
v.setOnClickListener(this);
StaggeredGridLayoutManager staggeredGridLayoutManager = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.HORIZONTAL);
recyclerView = v.findViewById(R.id.cardRecyclerView);
recyclerView.setLayoutManager(staggeredGridLayoutManager);
}
public void bind(Box item) {
this.item = item;
recyclerView.setAdapter(new CardViewAdapter(itemView.getContext(), item.cards, null));
}
#Override
public void onClick(View view) {
if (mListener != null) {
mListener.onItemClick();
}
}
}
}
In the meantime, I've resolved this way.
In onBindViewHolder of CardViewAdapter I simply set LayoutParams for itemView. Since the item view is the rendered inside a RecyclerView, StaggeredGridLayoutManager knows how to deal with it.
#Override
public void onBindViewHolder(#NonNull CardViewHolder viewHolder, int position) {
if (mUseLayoutParameters) {
int singleScreenWidth = DisplayUtils.getScreenWidth() / 3;
Card card = mValues.get(position);
if (card.isDoubleSized) {
singleScreenWidth = singleScreenWidth * 2;
}
StaggeredGridLayoutManager.LayoutParams layoutParams = new StaggeredGridLayoutManager.LayoutParams(singleScreenWidth, ViewGroup.LayoutParams.MATCH_PARENT);
viewHolder.itemView.setLayoutParams(layoutParams);
}
viewHolder.bind(mValues.get(position));
}

How to change RecyclerView item color on Swipe?

I'm trying to make recyclerView item change color on swipe so it's will be highlited because on swipe i open an AlertDialog so the person will recognize which one item he is changing.
But my main issue is that when i try to change the background color by using
viewHolder.itemView.setBackgroundColor(Color.parseColor("#cc0000"));
Nothing is heppening, here is my full code from the builder of recyclerView
public void buildTopRecyclerView(){
mRecyclerViewTOP = findViewById(R.id.listView);
mRecyclerViewTOP.setHasFixedSize(true);
RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(this);
exampleAdapter = new ExampleAdapter(itemCassas);
mRecyclerViewTOP.setLayoutManager(mLayoutManager);
mRecyclerViewTOP.setAdapter(exampleAdapter);
// onSwipe();
new ItemTouchHelper(new ItemTouchHelper.SimpleCallback(0,ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) {
#Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
return false;
}
#Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
int position = viewHolder.getAdapterPosition();
if (direction == ItemTouchHelper.RIGHT) {
customAllertDelete(position);
viewHolder.itemView.setBackgroundColor(Color.parseColor("#cc0000"));
exampleAdapter.notifyDataSetChanged();
}
if (direction == ItemTouchHelper.LEFT) {
customAllertQuantity(position);
}
}
}).attachToRecyclerView(mRecyclerViewTOP);
}
And here is also the Adapter code if it could be usefull
public class ExampleAdapter extends RecyclerView.Adapter<ExampleAdapter.ExampleViewHolder> {
private ArrayList<ItemCassa> mExampleList;
#NonNull
#Override
public ExampleViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.recyclerlist_item,parent,false);
return new ExampleViewHolder(v);
}
ExampleAdapter(ArrayList<ItemCassa> exampleList){
mExampleList = exampleList;
}
#Override
public void onBindViewHolder(#NonNull ExampleViewHolder holder, int position) {
ItemCassa item = mExampleList.get(position);
holder.desc.setText(item.getBtnName());
holder.qta.setText(String.valueOf(item.getQuant()));
holder.imp.setText(String.valueOf((new DecimalFormat("#0.00").format(item.getPrice()))));
if(position % 2 == 0 ){
holder.itemView.setBackgroundColor(Color.parseColor("#C0C0C0"));
holder.desc.setBackgroundColor(Color.parseColor("#C0C0C0"));
holder.qta.setBackgroundColor(Color.parseColor("#C0C0C0"));
holder.imp.setBackgroundColor(Color.parseColor("#C0C0C0"));
}else if(position % 2 == 1){
holder.itemView.setBackgroundColor(Color.parseColor("#D3D3D3"));
holder.desc.setBackgroundColor(Color.parseColor("#D3D3D3"));
holder.qta.setBackgroundColor(Color.parseColor("#D3D3D3"));
holder.imp.setBackgroundColor(Color.parseColor("#D3D3D3"));
}
}
#Override
public int getItemCount() {
return mExampleList.size();
}
public static class ExampleViewHolder extends RecyclerView.ViewHolder {
public TextView desc;
public TextView qta;
public TextView imp;
ExampleViewHolder(View itemView) {
super(itemView);
desc = itemView.findViewById(R.id.Desc);
qta = itemView.findViewById(R.id.Qta);
imp = itemView.findViewById(R.id.Imp);
}
}
public void removeItem(int position) {
mExampleList.remove(position);
notifyItemRemoved(position);
}
}
Here is the gif that "explain" a bit what i'm trying to do, as you can see on swipe it's open an AlertDialog and turn back the selected item, but now i would color that selected item background until the user make a choose in the AlertDialog
Try using like this:
First of all in your onSwiped method call your adapter's method and pass the required parameters.
#Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int swipeDir) {
int swipedPosition = viewHolder.getAdapterPosition();
YourAdapter adapter = (YourAdapter) rlCartList.getAdapter();
adapter.remove(swipedPosition); // I was removing items so you
can change the name of method as you like
}
Now in your adapter do something like this:
public void remove(int position) {
YourModel item = cartItems.get(position);
if (cartItems.contains(item)) {
ViewHolder.tvItemName.setBackgroundColor(mContext.getResources().getColor(R.color.grey));
notifyDataSetChanged();
}
}
One more thing is you will need to make your textView or the view you are assigning the background to Static. And I have tested this code it's running fine in my project the background of textView changes on swipe.
Tell me if you need further assistance. And also one more thing you can use your alert dialog in adapter too :)

Recyclerview onCreateViewHolder called for every item

I have a RecyclerView inside a NestedScrollView that show some data downloaded asynchronously. The problem is that there is a significant lag when the items are initilized. After some tests I found out that the problem is that onCreateViewHolder is called for every item and it took some time to inflate the layout. This is my adapter:
public class EpisodeAdapter extends RecyclerView.Adapter<EpisodeAdapter.ViewHolder> {
private static final String TAG = "EpisodeAdapter";
private static final int NO_POSITION = -1;
private static final int EXPAND = 1;
private static final int COLLAPSE = 2;
private SparseArray<Episode> episodes;
private OnItemClickListener<Episode> downloadClickListener;
private OnItemClickListener<Episode> playClickListener;
private RecyclerView recyclerView;
private final EpisodeAnimator episodeAnimator;
private final Transition expandCollapse;
private int expandedPosition = NO_POSITION;
public EpisodeAdapter() {
episodes = new SparseArray<>();
episodeAnimator = new EpisodeAnimator();
expandCollapse = new AutoTransition();
}
//Called when first loading items
public void swapEpisodes(SparseArray<Episode> newEpisodes){
final int previousSize = episodes.size();
episodes = newEpisodes;
expandedPosition = NO_POSITION;
Log.e(TAG, "Swap called");
if(previousSize == 0) {
notifyItemRangeInserted(0, episodes.size());
}
else {
notifyItemRangeChanged(0, Math.max(previousSize, episodes.size()));
}
}
//Called when downloading other information, this seems to work fine without delay
public void setEpisodesDetails(final List<TmdbEpisode> episodeList){
for (TmdbEpisode episode : episodeList){
final int position = episodes.indexOfKey(episode.getNumber());
notifyItemChanged(position, episode);
}
}
#Override
public EpisodeAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
Log.e(TAG, "Start createViewHolder");
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_episode, parent, false);
ViewHolder viewHolder = new ViewHolder(view);
viewHolder.downloadButton.setOnClickListener(v -> {
if(downloadClickListener != null)
downloadClickListener.onItemClick(v, episodes.valueAt(viewHolder.getAdapterPosition()));
});
viewHolder.playButton.setOnClickListener(v -> {
if(playClickListener != null)
playClickListener.onItemClick(v, episodes.valueAt(viewHolder.getAdapterPosition()));
});
viewHolder.itemView.setOnClickListener(v -> {
final int position = viewHolder.getAdapterPosition();
if(position == NO_POSITION) return;
TransitionManager.beginDelayedTransition(recyclerView, expandCollapse);
episodeAnimator.setAnimateMoves(false);
//Collapse any currently expanded items
if(expandedPosition != NO_POSITION){
notifyItemChanged(expandedPosition, COLLAPSE);
}
//Expand clicked item
if(expandedPosition != position){
expandedPosition = position;
notifyItemChanged(position, EXPAND);
}
else {
expandedPosition = NO_POSITION;
}
});
Log.e(TAG, "Finish createViewHolder");
return viewHolder;
}
#Override
public void onBindViewHolder(EpisodeAdapter.ViewHolder holder, int itemPosition) {
Log.e(TAG, "Start");
holder.number.setText(String.valueOf(episodes.keyAt(itemPosition)));
holder.details.setVisibility(View.GONE);
holder.itemView.setActivated(false);
Log.e(TAG, "Finish");
}
#Override
public void onBindViewHolder(ViewHolder holder, int position, List<Object> payloads) {
Log.e(TAG, "Start payloads");
if(payloads.contains(EXPAND) || payloads.contains(COLLAPSE)){
setExpanded(holder, position == expandedPosition);
}
else if(!payloads.isEmpty() && payloads.get(0) instanceof TmdbEpisode){
TmdbEpisode episode = (TmdbEpisode) payloads.get(0);
holder.title.setText(episode.getName());
holder.details.setText(episode.getOverview());
}
else {
onBindViewHolder(holder, position);
}
Log.e(TAG, "Finish payloads");
}
private void setExpanded(ViewHolder holder, boolean isExpanded) {
holder.itemView.setActivated(isExpanded);
holder.details.setVisibility((isExpanded) ? View.VISIBLE : View.GONE);
}
public void setPlayClickListener(OnItemClickListener<Episode> onItemClickListener){
playClickListener = onItemClickListener;
}
public void setDownloadClickListener(OnItemClickListener<Episode> onItemClickListener){
downloadClickListener = onItemClickListener;
}
#Override
public int getItemCount() {
return episodes.size();
}
static class ViewHolder extends RecyclerView.ViewHolder {
View itemView;
TextView number;
FadeTextSwitcher title;
ImageButton downloadButton;
FloatingActionButton playButton;
TextView details;
ViewHolder(View itemView) {
super(itemView);
Log.e(TAG, "Start constructor");
this.itemView = itemView;
number = itemView.findViewById(R.id.number);
title = itemView.findViewById(R.id.title);
downloadButton = itemView.findViewById(R.id.download_button);
playButton = itemView.findViewById(R.id.play_button);
details = itemView.findViewById(R.id.details);
Log.e(TAG, "Finish constructor");
}
}
#Override
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
this.recyclerView = recyclerView;
this.recyclerView.setItemAnimator(episodeAnimator);
expandCollapse.setDuration(recyclerView.getContext().getResources().getInteger(R.integer.episode_expand_collapse_duration));
expandCollapse.setInterpolator(AnimationUtils.loadInterpolator(this.recyclerView.getContext(), android.R.interpolator.fast_out_slow_in));
expandCollapse.addListener(new Transition.TransitionListener() {
#Override
public void onTransitionStart(android.transition.Transition transition) {
EpisodeAdapter.this.recyclerView.setOnTouchListener((v, event) -> true);
}
#Override
public void onTransitionEnd(android.transition.Transition transition) {
episodeAnimator.setAnimateMoves(true);
EpisodeAdapter.this.recyclerView.setOnTouchListener(null);
}
#Override
public void onTransitionCancel(android.transition.Transition transition) {}
#Override
public void onTransitionPause(android.transition.Transition transition) {}
#Override
public void onTransitionResume(android.transition.Transition transition) {}
});
}
static class EpisodeAnimator extends SlideInItemAnimator {
private boolean animateMoves = false;
EpisodeAnimator() {
super();
}
void setAnimateMoves(boolean animateMoves) {
this.animateMoves = animateMoves;
}
#Override
public boolean animateMove(RecyclerView.ViewHolder holder, int fromX, int fromY, int toX, int toY) {
if (!animateMoves) {
dispatchMoveFinished(holder);
return false;
}
return super.animateMove(holder, fromX, fromY, toX, toY);
}
}
}
Is there a way to force reusing the same ViewHolder for every items? So onCreateViewHolder will be called once.
I've also set nestedScrollingEnabled="false" in the recyclerview.
I have a RecyclerView inside a NestedScrollView
I'm going to guess that your <RecyclerView> tag has its height defined as wrap_content. If it does, that means that you're inflating a layout resource (and creating a ViewHolder object) for every single item in your data set; potentially thousands of layout inflations and object creations.
The recycling behavior of RecyclerView only works when the height of the recyclerview is smaller than the height needed to display its children. It's normal for a recyclerview to create a small double-digit number of ViewHolder instances (usually however many items you can see on screen at once plus a few to optimize views just off-screen), but this depends on the fact that your recyclerview's size is constrained by the screen size (i.e. you're using match_parent or a fixed size).
In the case of a RecyclerView with wrap_content height inside a NestedScrollView, the user won't be able to see all of the items at a single time, but the Android framework only knows that you have a recyclerview large enough to hold every single item in your data set and so it has to create a viewholder for every single item.
You'll have to figure out a way to rework your layout hierarchy so that you can use some limited height for your RecyclerView.

Android RecyclerView StaggeredGrid items change position when scrolling top

In my Staggered Grid with 2 columns I use the effect of infinite list to automatically load elements 10 by 10, everything works fine except when I scroll up. The first two elements change their position. It is like the first two items jump from one column to another.
As I read, the adapter must save the state of each element. But, I do not know how to do it.
This is the main code that I use to display the StaggeredGrid functionality
public class SpacesItemDecoration extends RecyclerView.ItemDecoration {
private int space;
public SpacesItemDecoration(int space) {
this.space = space;
}
#Override
public void getItemOffsets(Rect outRect, View view,
RecyclerView parent, RecyclerView.State state) {
outRect.left = space;
outRect.right = space;
outRect.bottom = space+1;
// Add top margin only for the first item to avoid double space between items
if(parent.getChildLayoutPosition(view) == 0)
outRect.top = space;
}
}
public class SolventViewHolders extends RecyclerView.ViewHolder implements View.OnClickListener {
public TextView vh_name;
public TextView vh_price;
public ImageView vh_image;
public SolventViewHolders(View v) {
super(v);
v.setOnClickListener(this);
vh_image=(ImageView)v.findViewById(R.id.thumbnail);
vh_name= (TextView) v.findViewById(R.id.title);
vh_price= (TextView) v.findViewById(R.id.subtitle);
}
public void sendImage(String url) {
Picasso.with(ctx)
.load(url)
.into(vh_image);
}
#Override
public void onClick(View view) {
int position = getPosition();
if (user_id != null) {
openDetail(MyLogs.get(position).getId_item(), MyLogs.get(position).getItemName(), MyLogs.get(position).getPrice(), MyLogs.get(position).getDescrip(), MyLogs.get(position).getId_store(), MyLogs.get(position).getId_store());
}
}
}
public class SolventRecyclerViewAdapter extends RecyclerView.Adapter<SolventViewHolders> {
private List<productItem> itemList;
private Context context;
public SolventRecyclerViewAdapter(Context context, List<productItem> itemList) {
this.itemList = itemList;
this.context = context;
}
#Override
public SolventViewHolders onCreateViewHolder(ViewGroup parent, int viewType) {
View layoutView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_mosaico_defoult, null);
SolventViewHolders rcv = new SolventViewHolders(layoutView);
return rcv;
}
#Override
public void onBindViewHolder(SolventViewHolders holder, int position) {
holder.vh_name.setText(itemList.get(position).getName());
holder.vh_price.setText("CLP:"+itemList.get(position).getPrice());
}
#Override
public int getItemCount() {
return this.itemList.size();
}
}
#Edit 1:
This is how I instanciate the adapter
recyclerView = (RecyclerView)view.findViewById(R.id.recycler_view);
recyclerView.setHasFixedSize(true);
gaggeredGridLayoutManager = new StaggeredGridLayoutManager(2, 1);
recyclerView.setLayoutManager(gaggeredGridLayoutManager);
recyclerView.addItemDecoration(new SpacesItemDecoration(2));
populateList(currentPage);
rcAdapter= new SolventRecyclerViewAdapter(ctx, MyLogs);
recyclerView.setAdapter(rcAdapter);
The populate method happens as an asyntask response that comes in json format:
...
for(int i=0; i < lengthJsonArr; i++)
{
JSONObject jsonChildNode = jsonMainNode.getJSONObject(i);
...
MyLogs.add("name,price,etc");
rcAdapter.notifyDataSetChanged();
}
And this is how i hold the onScroll:
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
visibleItemCount = gaggeredGridLayoutManager.getChildCount();
totalItemCount = gaggeredGridLayoutManager.getItemCount();
int[] firstVisibleItems = null;
firstVisibleItems = gaggeredGridLayoutManager.findFirstVisibleItemPositions(firstVisibleItems);
if (firstVisibleItems != null && firstVisibleItems.length > 0) {
pastVisiblesItems = firstVisibleItems[0];
}
if (loading) {
if ((visibleItemCount + pastVisiblesItems) >= totalItemCount) {
loading= false;
Log.d("tag", "LOAD NEXT ITEM");
currentPage = currentPage + 1;
populateList(currentPage);
}
}
}
});
I appreciate any help
This is a feature. Reordering and repositioning view for a seamless tream of items.
Use setGapStrategy(int gapStrategy)
Sets the gap handling strategy for StaggeredGridLayoutManager. If the gapStrategy parameter is different than the current strategy, calling this method will trigger a layout request.
You will probably want GAP_HANDLING_NONE.
Does not do anything to hide gaps.
This happens because the holder dose not recognize the width and height of the Image view when you scroll up and down. It clears the upper view when you scroll down and vice versa.
Use like this :
#Override
public void onBindViewHolder(ViewHolder viewHolder, int position) {
MyViewHolder vh = (MyViewHolder) viewHolder;
ImageModel item = imageModels.get(position);
RelativeLayout.LayoutParams rlp = (RelativeLayout.LayoutParams)vh.imageView.getLayoutParams();
float ratio = item.getHeight() / item.getWidth();
rlp.height = (int) (rlp.width * ratio);
vh.imageView.setLayoutParams(rlp);
vh.positionTextView.setText("pos: " + position);
vh.imageView.setRatio(item.getRatio());
Picasso.with(mContext).load(item.getUrl()).
placeholder(PlaceHolderDrawableHelper.getBackgroundDrawable(position)).
into(vh.imageView);
}
For clear see this link: Picasso/Glide-RecyclerView-StaggeredGridLayoutManager

something weird happens while scrolling RecyclerView too fast

I'm using RecyclerView for a long list and when i scroll too fast, something weird happens. There are two problems.
First one is the one in the image below. One of items (like red bordered one in the image) sticks on some random part of the screen and blocks other items. It disappears when the real version of that item becomes visible on the screen.
Another problem about scrolling this long RecyclerView too fast is sticking onClick effect. Some of items become visible like someone is pressing on them.
Are these problems about my adapter or are they common problems about RecyclerView? Here is my adapter:
public class SimpleListItemRecyclerAdapter extends RecyclerView.Adapter<SimpleListItemRecyclerAdapter.ListItemViewHolder> {
Context context;
ArrayList<RecyclerItemModel> list;
OnRecyclerViewItemClickListener onRecyclerViewItemClickListener;
private int lastPosition = -1;
private boolean isClickable;
public SimpleListItemRecyclerAdapter(ArrayList<RecyclerItemModel> list, _FragmentTemplate fragment) {
this.list = list;
this.context = fragment.getActivity();
try {
this.onRecyclerViewItemClickListener = (OnRecyclerViewItemClickListener) fragment;
isClickable = true;
} catch (ClassCastException e) {
isClickable = false;
}
setHasStableIds(true);
}
#Override
public ListItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext()).
inflate(R.layout.item_sub_main_list_single_text,
parent,
false);
return new ListItemViewHolder(itemView, onRecyclerViewItemClickListener, isClickable, list.get(0).getHeight());
}
#Override
public void onBindViewHolder(ListItemViewHolder holder, int position) {
final RecyclerItemModel recyclerItem = list.get(position);
if (recyclerItem.isBackgroundColorSpecified())
holder.itemView.setBackgroundResource(recyclerItem.getBackgroundColorResource());
else {
final int[] backgroundColors = Preferences.backgroundSelectorsGrey;
holder.itemView.setBackgroundResource(backgroundColors[position % backgroundColors.length]);
}
holder.textMain.setText(recyclerItem.getTextMain());
if (recyclerItem.isImageRightAvailable()) {
if (recyclerItem.isProgressBarAvailable())
if (recyclerItem.shouldShowDoneImage())
setDrawableFromSVG(holder.imageRight, recyclerItem.getImageRightResourceDone());
else
setDrawableFromSVG(holder.imageRight, recyclerItem.getImageRightResource());
} else
holder.imageRight.setVisibility(View.GONE);
if (recyclerItem.isTextMainBottomAvailable())
holder.textMainBottom.setText(recyclerItem.getTextMainBottom());
else
holder.textMainBottom.setVisibility(View.GONE);
if (recyclerItem.isTextRightTopAvailable())
holder.textRightTop.setText(recyclerItem.getTextRightTop());
else
holder.textRightTop.setVisibility(View.GONE);
if (recyclerItem.isTextRightBottomAvailable())
holder.textRightBottom.setText(recyclerItem.getTextRightBottom());
else
holder.textRightBottom.setVisibility(View.GONE);
if (recyclerItem.isProgressBarAvailable())
holder.progressBar.setProgress(recyclerItem.getProgress());
else
holder.progressBar.setVisibility(View.GONE);
setAnimation(holder.itemView, position);
}
#Override
public long getItemId(int position) {
return list.get(position).hashCode();
}
#Override
public int getItemCount() {
return list.size();
}
private void setAnimation(View viewToAnimate, int position) {
if (position > lastPosition) {
Animation animation = AnimationUtils.loadAnimation(context, R.anim.appear);
viewToAnimate.startAnimation(animation);
lastPosition = position;
}
}
public Drawable setDrawableFromSVG(ImageView imageView, int resource) {
SVG svg = new SVGBuilder()
.readFromResource(context.getResources(), resource)
.build();
imageView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
imageView.setImageDrawable(svg.getDrawable());
return svg.getDrawable();
}
public final static class ListItemViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
OnRecyclerViewItemClickListener onRecyclerViewItemClickListener;
TextView textMain, textMainBottom, textRightTop, textRightBottom;
ImageView imageRight;
ProgressBar progressBar;
View itemView;
public ListItemViewHolder(View itemView, OnRecyclerViewItemClickListener onRecyclerViewItemClickListener,
boolean isClickable, int height) {
super(itemView);
this.itemView = itemView;
this.onRecyclerViewItemClickListener = onRecyclerViewItemClickListener;
textMain = (TextView) itemView.findViewById(R.id.list_item_text_main);
imageRight = (ImageView) itemView.findViewById(R.id.list_item_image_right);
textRightTop = (TextView) itemView.findViewById(R.id.list_item_text_right_top);
textRightBottom = (TextView) itemView.findViewById(R.id.list_item_text_right_bottom);
textMainBottom = (TextView) itemView.findViewById(R.id.list_item_text_main_bottom);
progressBar = (ProgressBar) itemView.findViewById(R.id.list_item_progress_bar);
switch (height) {
case RecyclerItemModel.HEIGHT_FULL:
itemView.setLayoutParams(new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, Preferences.squareLength));
break;
case RecyclerItemModel.HEIGHT_HALF:
itemView.setLayoutParams(new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, Preferences.squareLength / 2));
}
if (isClickable)
itemView.setOnClickListener(this);
}
#Override
public void onClick(View v) {
onRecyclerViewItemClickListener.onRecyclerViewItemClick(getPosition());
}
}
}
Here is what resolved my problem:
Animating somehow causes a problem on RecyclerView. So the solution for me was removing the following line:
setAnimation(holder.itemView, position);
I didn't try to add animation again but if you really need it, here is something that can be useful: How to animate RecyclerView items when they appear.

Categories

Resources