something weird happens while scrolling RecyclerView too fast - android

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.

Related

RecyclerView problems when scrolling

I have an expandable RecyclerView, when the data is loaded in the Recyclerview everything seems good but when I scroll to the bodem, the data on the items above were muted to. In a normale recycler view I can solve this problem by bind everything in my OnBindViewHolder but now its harder.
this is my code for the expandable adapter:
public class CalendarAdapter extends ExpandableRecyclerViewAdapter<CalendarHeaderViewHolder, CalendarItemViewHolder> {
private Context ctx;
public CalendarAdapter(List<? extends ExpandableGroup> group, Context ctx){
super(group);
this.ctx = ctx;
}
#Override
public CalendarHeaderViewHolder onCreateGroupViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.group_view_holder, parent, false);
return new CalendarHeaderViewHolder(view);
}
#Override
public CalendarItemViewHolder onCreateChildViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.calendar_item, parent, false);
return new CalendarItemViewHolder(view,ctx);
}
#Override
public void onBindChildViewHolder(CalendarItemViewHolder holder, int flatPosition, ExpandableGroup group, int childIndex) {
final CalenderItem calenderItem = ((CalenderHeader) group).getItems().get(childIndex);
holder.onBind(calenderItem, group);
}
#Override
public void onBindGroupViewHolder(CalendarHeaderViewHolder holder, int flatPosition, ExpandableGroup group) {
holder.setGroupName(group);
}
}
And i bind my Holder in CalendarItemViewHolder
public class CalendarItemViewHolder extends ChildViewHolder {
private TextView tvDescription;
private TextView tvSubDescription;
private TextView tvDate;
private RelativeLayout ivIcon;
private CalenderItem item;
private LinearLayout llItemCal;
private onInteractionListener listener;
private Context ctx;
public CalendarItemViewHolder(View itemView, Context ctx){
super(itemView);
tvDescription = (TextView) itemView.findViewById(R.id.tvDescription);
tvSubDescription = (TextView)itemView.findViewById(R.id.tvSubDescription);
ivIcon = (RelativeLayout) itemView.findViewById(R.id.ivIcon);
tvDate = (TextView) itemView.findViewById(R.id.tvDate);
llItemCal = (LinearLayout) itemView.findViewById(R.id.llItemCal);
this.ctx = ctx;
listener = (onInteractionListener) ctx;
}
public void onBind(CalenderItem calenderItem, ExpandableGroup group){
this.item = calenderItem;
llItemCal.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//MOVE TO DETAIL SCREEN
}
});
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
tvDescription.setText(Html.fromHtml(calenderItem.getActivitie().getTitle(), Html.FROM_HTML_MODE_COMPACT));
} else {
tvDescription.setText(Html.fromHtml(calenderItem.getActivitie().getTitle()));
}
//HERE IS A PROBLEM WHEN SCROLLING
if(calenderItem.getCalType()== null || calenderItem.getCalType().equals("")){
tvSubDescription.setVisibility(View.GONE);
}else{
tvSubDescription.setText(calenderItem.getCalType());
}
//HERE IS ALSO A PROBLEM WHEN SCROLLING
if (calenderItem.getDate() != null){
tvDate.setText(calenderItem.getDate());
tvDate.setVisibility(View.VISIBLE);
}
//HERE EVERYTHING SEEMS OKE
Drawable background = ctx.getResources().getDrawable(R.drawable.circle);
switch (item.getType()){
case PROTHESE:
background.setColorFilter(ContextCompat.getColor(ctx, R.color.blue_grey_200), PorterDuff.Mode.SRC_IN);
ivIcon.setBackground(background);
break;
case NOTHING:
background.setColorFilter(ContextCompat.getColor(ctx, R.color.blue_grey_400), PorterDuff.Mode.SRC_IN);
ivIcon.setBackground(background);
break;
case DANGER:
background.setColorFilter(ContextCompat.getColor(ctx, R.color.blue_grey_800), PorterDuff.Mode.SRC_IN);
ivIcon.setBackground(background);
break;
}
}
public interface onInteractionListener {
public void moveToDetailFragment(CalenderItem calItem);
}
}
When I scroll, the tvDate is showed randomly in my list
where do I need to write this code to solve this problem?
Please try with below code.
//HERE IS ALSO A PROBLEM WHEN SCROLLING
if (calenderItem.getDate() != null){
tvDate.setText(calenderItem.getDate());
tvDate.setVisibility(View.VISIBLE);
}else {
tvDate.setVisibility(View.GONE);
}
Must be like that :
//HERE IS A PROBLEM WHEN SCROLLING
if(calenderItem.getCalType()== null || calenderItem.getCalType().equals("")){
tvSubDescription.setVisibility(View.GONE);
}else{
tvSubDescription.setText(calenderItem.getCalType());
tvSubDescription.setVisibility(View.VISIBLE);
}
//HERE IS ALSO A PROBLEM WHEN SCROLLING
if (calenderItem.getDate() != null){
tvDate.setText(calenderItem.getDate());
tvDate.setVisibility(View.VISIBLE);
} else {
tvDate.setVisibility(View.GONE);
}

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.

Add more button inside recyclerView

What I'm trying to do is to have a recycler view with a button in it. When the layout that has the recycler is initialized it will only have the button and once is clicked an item will be added(the first item) and the button(or imageview, whatever it has a + on it) pushed to the right. I have the recyclerView and it's working(by working I mean that I have some photos that are being displayed in the right order), but I can't get the button to display. I found various links sort of similar to this, such as How to add fixed Button in RecyclerView Adapter?, but I can't figure it out.
Here is the Adapter:
public class SelectPhotoAdapter extends
RecyclerView.Adapter<SelectPhotoHolder> {// Recyclerview will extend to
// recyclerview adapter
private ArrayList<Data_Model> arrayList;
private Context context;
private boolean hasLoadButton = true;
private final int IMAGES = 0;
private final int LOAD_MORE = 1;
public SelectPhotoAdapter (Context context,
ArrayList<Data_Model> arrayList) {
this.context = context;
this.arrayList = arrayList;
}
public boolean isHasLoadButton() {
return hasLoadButton;
}
public void setHasLoadButton(boolean hasLoadButton) {
this.hasLoadButton = hasLoadButton;
notifyDataSetChanged();
}
#Override
public int getItemCount() {
if (hasLoadButton) {
return arrayList.size() + 1;
} else {
return arrayList.size();
}
}
#Override
public int getItemViewType(int position) {
if (position < getItemCount()) {
return IMAGES;
} else {
return LOAD_MORE;
}
}
// #Override
// public int getItemCount() {
// return (null != arrayList ? arrayList.size() : 0);
//
// }
#Override
public void onBindViewHolder(SelectPhotoHolder holder, int position) {
SelectPhotoHolder mainHolder = holder;// holder
if(position >= getItemCount()) {
mainHolder.addPhoto.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
//dostuff
}
});
} else {
final Data_Model model = arrayList.get(position);
Bitmap image = BitmapFactory.decodeResource(context.getResources(),
model.getImagePath());// This will convert drawbale image into
mainHolder.imageview.setImageBitmap(image);
}
}
#Override
public SelectPhotoHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if(viewType == IMAGES) {
return new SelectPhotoHolder((LayoutInflater.from(parent.getContext()).inflate(R.layout.selectphotolist, parent, false)),IMAGES);
} else if (viewType == LOAD_MORE) {
return new SelectPhotoHolder((LayoutInflater.from(parent.getContext()).inflate(R.layout.addphoto, parent, false)),LOAD_MORE);
} else {
return null;
}
}
}
If I use the getItemCount() which is commented I get the photos, but no button and if I use the uncommented getItemCount() which I took from the link provided it will crash. I also haven't figured out where to use isHasLoadButton() or setHasLoadButton methods. Could anyone point me in the right direction ?
If you need me to post anymore files let me know. Thank you.
The file which uses the recyclerView:
public class SelectPhotoDialogFragment extends DialogFragment {
private RecyclerView mRecyclerView;
private SelectPhotoAdapter adapter;
// this method create view for your Dialog
public static final Integer[] IMAGES= {R.drawable.first,R.drawable.second,R.drawable.third,R.drawable.picc};
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
Dialog dialog = super.onCreateDialog(savedInstanceState);
// request a window without the title
dialog.getWindow().requestFeature(Window.FEATURE_NO_TITLE);
return dialog;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
//inflate layout with recycler view
View v = inflater.inflate(R.layout.select_photo, container, false);
mRecyclerView = (RecyclerView) v.findViewById(R.id.recycler_view);
int recyclerHeight = mRecyclerView.getHeight();
mRecyclerView.setHasFixedSize(true);
mRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity(), LinearLayoutManager.HORIZONTAL, false));
ArrayList<Data_Model> arrayList = new ArrayList<>();
for (int i = 0; i < IMAGES.length; i++) {
arrayList.add(new Data_Model(IMAGES[i]));
}
adapter = new SelectPhotoAdapter(getActivity(), arrayList);
mRecyclerView.setAdapter(adapter);// set adapter on recyclerview
adapter.notifyDataSetChanged();// Notify the adapter
return v;
}
}
To explain again: The only thing that I'm seeing are the images(first,second,third,picc) and no button after, but they are displayed correctly. What I would like to happen is to have only the button as an item, and when I click on it to have a picture inserted before and it moving to the right(as you can see I have a horizontal orientation). This is what I've been trying to do with 2 ViewTypes, but I haven't figured it out.
The Holder:
public class SelectPhotoHolder extends RecyclerView.ViewHolder {
// View holder for gridview recycler view as we used in listview
private final int IMAGES = 0;
private final int LOAD_MORE = 1;
public ImageView imageview;
public Button addPhoto;
public SelectPhotoHolder(View view,int ViewType) {
super(view);
// Find all views ids
if(ViewType == IMAGES) {
this.imageview = (ImageView) view
.findViewById(R.id.selectPhoto);
}
else
this.addPhoto = (Button) view.findViewById(R.id.load_more);
}
}
new Holder output:
01-31 14:12:47.131 24072-24072/name.company.newapp E/test-exist: IMAGESfalse
01-31 14:12:47.141 24072-24072/name.company.newapp E/test-exist: IMAGESfalse
01-31 14:12:47.141 24072-24072/name.company.newapp E/test-exist: IMAGESfalse
01-31 14:12:47.151 24072-24072/name.company.newapp E/test-exist: IMAGESfalse
01-31 14:12:47.231 24072-24072/name.company.newapp E/test-exist: IMAGESfalse
#Override
public int getItemViewType(int position) {
if (position < getItemCount()-1) {
return IMAGES;
} else {
return LOAD_MORE;
}
}
public SelectPhotoHolder(View view,int ViewType) {
super(view);
// Find all views ids
if(ViewType == IMAGES) {
this.imageview = (ImageView) view
.findViewById(R.id.selectPhoto);
Log.e("test-exist", "IMAGES" + (imageview == null));
}else{
this.addPhoto = (Button) view.findViewById(R.id.load_more);
Log.e("test-exist", "LOAD_MORE" + (addPhoto == null));
}
}
#Override
public int getItemCount() {
if (hasLoadButton) {
return arrayList==null? 1 :arrayList.size() + 1;
} else {
return arrayList==null? 0 :arrayList.size();
}
}
if(position >= (arrayList==null? 0 :arrayList.size())) {
try{
mainHolder.addPhoto.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
//dostuff
}
});
}catch(Throwable e){
Log.e("test-erorr","current position:"+position);
}
} else {
final Data_Model model = arrayList.get(position);
Bitmap image = BitmapFactory.decodeResource(context.getResources(),
model.getImagePath());// This will convert drawbale image into
mainHolder.imageview.setImageBitmap(image);
}
Here's a sample, that's why i meant in " sample - example ", modify it to your own requirements, This "sample" display Recyclerview items with images, below the recyclerview there's a button, when pressed it adds a new image, you can modify the place - animation, Here's the Item.java :
public class Item {
private int Pic;
public Item(String Title, int Pic, int Color) {
setPic(Pic);
}
public int getPic() {
return Pic;
}
public void setPic(int photo) {
this.Pic= photo;
}
}
Adapter :
public class Adapter extends RecyclerView.Adapter<RecyclerViewHolders>{
private List<Item> itemList;
public Context context;
public Adapter(Context context, List<Item> itemList) {
this.itemList = itemList;
this.context = context;
}
#Override
public RecyclerViewHolders onCreateViewHolder(ViewGroup parent, int viewType) {
View layoutView = LayoutInflater.from(parent.getContext()).inflate(R.layout.your_item, null);
return new RecyclerViewHolders(layoutView);
}
#Override
public void onBindViewHolder(RecyclerViewHolders holder, int position) {
holder.Pic.setImageResource(itemList.get(position).getPic());
}
#Override
public int getItemCount() {
return this.itemList.size();
}
}
It's all normal to now, in your MainActivity or where your Adapter initialized
Adapter rcAdapter;
public static List<Item> Items;
public static Button button;
In your onCreate:
initListViews();
Now the initListViews method :
public void initListViews()
{
List<Item> rowListItem = getAllItemList();
LinearLayoutManager yourlayout= new LinearLayoutManager(this);
RecyclerView RcView = (RecyclerView)findViewById(R.id.recycler_view);
button = (Button)findViewById(R.id.button);
RcView.setHasFixedSize(true);
RcView.setLayoutManager(yourlayout);
rcAdapter = new RecyclerViewAdapter(this, rowListItem);
RcView.setAdapter(rcAdapter);
}
private List<Item> getAllItemList(){
Items.add(new Item(YourImageResources));
Items.add(new Item(YourImageResources));
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
AllItems.add(new Item(YourImageResources));
}
});
}
If you want a specific number of items to be added :
int PressedTimes = 0;
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(PressedTimes < 7)
{
AllItems.add(new Item(YourImageResources));
PressedTimes=+1;
}
if(PressedTimes == 7)
{
button.setVisibility(View.GONE);
}
}
});

RecyclerView refreshes items only when scrolling down and up

I am making a simple recyclerview with Movie items. When I try adding new items they won't display in the recyclerview right away. For some reason I have to scroll down so the first item is gone, and then up again for the new item to be displayed (it's added at index 0).
The adapter:
public class MovieCardAdapter extends RecyclerView.Adapter<MovieCardAdapter.MovieViewHolder> {
List<Movie> movieList;
public MovieCardAdapter(final List<Movie> movieList) {
this.movieList = movieList;
}
public void addItem(int position, final Movie movie){
movieList.add(position,movie);
notifyItemInserted(position);
}
public void removeItem(final Movie movie){
final int indexOf = movieList.indexOf(movie);
movieList.remove(movie);
notifyItemRemoved(indexOf);
}
public void updateItems(final List<Movie> items){
movieList = items;
notifyDataSetChanged();
}
#Override
public MovieViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
View itemView = LayoutInflater.
from(viewGroup.getContext()).
inflate(R.layout.movie_row, viewGroup, false);
return new MovieViewHolder(itemView);
}
#Override
public long getItemId(int position) {
return super.getItemId(position);
}
#Override
public void onBindViewHolder(final MovieViewHolder movieViewHolder, final int i) {
final Movie movie = movieList.get(i);
String posterUrl = "";
final int posterHeight = 240;
if (!isEmpty(movie.getImdb_id())) {
posterUrl = "http://img.omdbapi.com/?i=" + movie.getImdb_id() + "&apikey="+apiKey+"&h=" + posterHeight;
} else if (!isEmpty(movie.getPoster())) {
posterUrl = movie.getPoster();
}
if (!isEmpty(posterUrl)) {
Picasso.with(movieViewHolder.context)
.load(posterUrl)
.resize(220, 354)
.centerCrop()
.into(movieViewHolder.poster);
}
movieViewHolder.title.setText(movie.getTittel());
movieViewHolder.format.setText(movie.getFormat());
if (movie.getRuntime().isEmpty() || movie.getRuntime().equalsIgnoreCase("0")){
movieViewHolder.runtime.setText("");
}else{
movieViewHolder.runtime.setText(movie.getRuntime()+" mins");
}
if (!movie.getTagline().isEmpty()) {
movieViewHolder.tagline.setText(movie.getTagline());
} else if (!movie.getPlot().isEmpty()) {
movieViewHolder.tagline.setText(movie.getPlot());
}
}
#Override
public int getItemCount() {
return movieList.size();
}
public static class MovieViewHolder extends RecyclerView.ViewHolder {
protected Context context;
protected LinearLayout movieRowLinearLayout;
protected ImageView poster;
protected TextView title;
protected TextView runtime;
protected TextView tagline;
protected TextView format;
public MovieViewHolder(View v) {
super(v);
context = v.getContext();
movieRowLinearLayout = (LinearLayout) v.findViewById(R.id.movieRowLinearLayout);
poster = (ImageView) v.findViewById(R.id.poster);
title = (TextView) v.findViewById(R.id.title);
runtime = (TextView) v.findViewById(R.id.runtime);
tagline = (TextView) v.findViewById(R.id.tagline);
format = (TextView) v.findViewById(R.id.format);
}
}
}
Initializing the adapter:
adapter = new MovieCardAdapter(MovieList.movies);
recyclerView.setAdapter(adapter);
Adding a sample movie:
if (id == R.id.add_movie) {
final Movie movie = new Movie();
movie.setTittel("0TEST");
movie.setFormat("HD-DVD");
MovieList.sortMoviesByTitle();
adapter.addItem(MovieList.movies.indexOf(movie), movie);
return true;
} else if (id == R.id.action_logOut) {
logOut(this);
}
Sry bad english, hope someone understands and know what the problem might be.
I have tried adding from runOnUiThread([...]), but same result.
This is happening because position 0 is above the current visible screen.
Until transcript mode is implemented, when you are adding an item to position N, try calling scrollToPosition(N) if LinearLayoutManager#findFirstCompletelyVisibleItemPosition returns N.
Also, don't make the position parameter final in your onBindViewHolder method. Instead, use ViewHolder.getPosition() if you need it in a callback. When position of the item changes, you will not receive an onBind call unless the item itself is invalidated.
Update
Also your update logic is not correct because you are not clearing values in views.
if (!movie.getTagline().isEmpty()) {
movieViewHolder.tagline.setText(movie.getTagline());
} else if (!movie.getPlot().isEmpty()) {
movieViewHolder.tagline.setText(movie.getPlot());
} else {
movieViewHolder.tagline.setText(null);
}
Same for the poster URL.
layoutManager.scrollToPosition didn't solve the problem for me.
Not a definitive solution, but the following line did the trick in my case:
recyclerView.getLayoutManager().smoothScrollToPosition(recyclerView, null, 0);
Hope it helps...
Adding this solved the problem to me
layoutManager.scrollToPosition

How to set animation on recyclerview

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.

Categories

Resources