After searching on SO, I have this piece of code to animate the appear animation of items in RecyclerView
#Override
public void onBindViewHolder(CourseViewHolder courseViewHolder, final int i) {
courseViewHolder.courseDate.setText(courseList.get(i).year);
courseViewHolder.courseName.setText(courseList.get(i).name);
setAnimation(courseViewHolder.view, i);
courseViewHolder.view.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
mListener.onClick(view, i);
}
});
}
private void setAnimation(View viewToAnimate, int position) {
// If the bound view wasn't previously displayed on screen, it's animated
if (position > lastPosition) {
Animation animation = AnimationUtils.loadAnimation(context, android.R.anim.slide_in_left);
viewToAnimate.startAnimation(animation);
lastPosition = position;
}
}
It kinda works, however the animation of all the items start at the same time. Is this possible to slide the items one by one? I thought about having a Handler to pass the delay after animating an item, but it does not work and I don't know where to put it (the onBindViewHolder still manages to show all the items at once).
Another question, where could I put the code to animate the disappear animation of all items in adapter? something to run when you refresh the RecyclerView in the OnRefreshListener of the SwipeRefreshLayout in your activity.
Related
I have a list view with a custom adapter. Each item has a delete icon which prompts a delete dialog fragment. on deleting the item, I am performing a slide animation and on animation end. the item is deleted from the list and adapter is notified about the deletion like below:
// dialog fragment on clicking "delete"
positive.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
final FoldingCell fView = (FoldingCell) thisItem.getParent().getParent();
// wait for fold to finish then delete item
fView.postDelayed(new Runnable() {
#Override
public void run() {
deleteCell(fView, pos);
}
}, 850);
my adapter is of type FoldingCell so I am folding back the cell before deletion, hence the postDelayed. The deleteCell is the simple animation below:
private void deleteCell(final View v, final int index) {
TranslateAnimation transanim = new TranslateAnimation(0, 800, 0, 0);
transanim.setDuration(700);
transanim.setAnimationListener(new Animation.AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
}
#Override
public void onAnimationEnd(Animation animation) {
items.remove(index);
// update array adapter
adapter.notifyDataSetChanged();
v.setVisibility(View.GONE);
}
#Override
public void onAnimationRepeat(Animation animation) {
}
});
v.startAnimation(transanim);
}
An important not is that I also am using stableIds which might be causing the problem. After the animation deletion occurs, the adapter is deleting two items instead of just the one clicked on. when I disable the animation in the above code, and just write
items.remove(index);
adapter.notifyDataSetChanged();
exactly on delete, the deletion works perfectly even with stable ids (there is just no animation) why is that? The problem seems like its a combination of the animation and stable ids, since if stableids is false, the deletion works with the animation.
I cannot find a good solution for this other than making stable ids false and solve other issues that arise from doing that.
Solved! The reason why with stableids the adapter was deleting an extra fields was that when I override getItemId, I was returning the adapter position itself which, I guess, during the animation when an item is deleted, that same position id is taken by another list item which gets deleted as well. By returning a different itemId that is unique to the list item, this error does not occur. so I changed this:
#Override
public long getItemId(int position) {
return position;
}
to this:
#Override
public long getItemId(int position) {
Item item = items(position);
return item.getId();
}
where items is the arraylist I am passing to the adapter. Item is my class that holds the elements/views of each item and getId() is the getter I have to return the id integer of each Item
I've got a button and a recycler view. The button refreshes the list. I want to animate one of the text views in my recycler view when it gets updated. Not the whole recyclerview, not the whole row - just one view (in every row).
I tried putting the animation in onBindViewHolder. But this starts the animation on scrolling and when i add a list entry:
#Override
public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) {
Animation animation = AnimationUtils.loadAnimation(context, R.anim.rv_animation_clockwise);
((ViewHolderItem)holder).tv.startAnimation(animation);
Then i tried adding a TextChangedListener to my text view. But this has the same effect as putting it straight into onBindViewHolder:
((ViewHolderItem)holder).tv.addTextChangedListener(new TextWatcher() {
#Override
public void afterTextChanged(Editable s) {
My last attempt was using findViewHolderForAdapterPosition. But it just doesn't do anything.
This is the refresh method which my button calls. It's in the RecyclerViewAdapter.
recyclerview is an instance variable which i set in onAttachedToRecyclerView:
RecyclerView recyclerView;
#Override
public void onAttachedToRecyclerView(#NonNull RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
this.recyclerView = recyclerView;
}
void refresh (List<Entry> al){
this.al = al;
notifyDataSetChanged();
((ViewHolderItem)recyclerView.findViewHolderForAdapterPosition(0)).tv.startAnimation(animation);
bump
You should implement your own ItemAnimator and set it to your RecyclerView. There is some useful information here: https://hackmd.io/#nesquena/r1IEQ-jAl?type=view
Thanks, i'll check out ItemAnimator later.
What i ended up doing yesterday was to add in a small delay between my adapter.refresh() and the animation. Like they suggested here. Weirdly this delay seems to be necessary even if notifyDataSetChanged(); is called after the animation. I don't think it's a good solution but it works for now.
final Animation animation = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.rv_animation_clockwise);
recyclerView.postDelayed(new Runnable()
{
#Override
public void run()
{
for (int i = 0; i < al.size(); i++) {
if (recyclerView.findViewHolderForAdapterPosition(i)!=null) {
((RecyclerViewAdapter.ViewHolderItem) recyclerView.findViewHolderForAdapterPosition(i)).tv.startAnimation(animation);
}
}
}
},50);
I want to do the next animation:
When I click on an item of my RecyclerView the items before disappear with a cascade animation.
For example: I Click on my item placed at position 10, then the items from 0 to 9 have to disappear applying a cascade swipe-left animation.
How can do it?
At this moment I tried to apply like this:
ViewHolder methods:
#Override
public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) {
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
swipeAnimation(position);
}
});
lstViewsHolder.add(holder.itemView);
setAnimation(holder.itemView, position);
}
private void swipeAnimation(int position){
int init = 0;
while(init < position){
lstViewsHolder.get(init).startAnimation(AnimationUtils.loadAnimation(context, R.anim.swipe_left));
init++;
}
}
The correct way of doing this is using a custom ItemAnimator. You should try to extend either SimpleItemAnimator or DefaultItemAnimator to apply your animation.
The ItemAnimators respond to data events from the adapter, so you should include logic in your adapter that calls notifyItemRemoved()or notifyItemRangeRemoved() so that the proper animation is triggered.
I think you should iterate over you're array and each time remove that item and then call notifyItemRemoved, you may want to delay each operation so that you're animation look smooth.
mResults.remove(position);
adapter.notifyItemRemoved(position);
I use ExpandablerecyclerAdapater. Just want the screen to move up from align with parentItem clicked.
I altered the code to force collapse all items before I expand new one.
#Override
public void onParentListItemExpanded(int position) {
Object parent = mItemList.get(position);
collapseAllParents(); // Alternatively keep track of the single item that is expanded and explicitly collapse that row (more efficient)
expandParent(((ParentWrapper) parent).getParentListItem());
}
Now I`m doing in main activity
mAdapter.setExpandCollapseListener(new ExpandableRecyclerAdapter.ExpandCollapseListener() {
#Override
public void onListItemExpanded(final int position) {
mLayoutManager.scrollToPositionWithOffset(position, 0);
}
But it doesnt scroll. What I`m doing wrong?
I am working on an android application, I have a list view with 10 items. I need to do the following. When the user clicks a button, I want the list to smooth scroll to item at position 5, so this item is displayed on the top of the list.
I have found 2 methods that can be used for this, but both methods are not working exactly how I need:
listView.setSelection(5) this will scroll to the row and put it on top of the list But without animation
list.smoothScrollToPosition(5) this will scroll the listview untill the row is visible but it will not put it on top (it is at the bottom of the page) and if the row is allready visible it will not scroll as it considers it is visible.
So is there a way to have the same behavior as the setSelection method but with smoothscrolling?
Thank you
I believe smoothScrollToPositionFromTop() does what you want.
There's also one that will take the desired animation duration in milliseconds as an argument.
So is there a way to have the same behavior as the setSelection method
but with smoothscrolling?
You could post a delayed Runnable and create your own smooth scroll effect using ListView.setSelection. Here's an example:
private ListView mListView;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
mListView.post(new PositionScroller(this));
}
private static final class PositionScroller implements Runnable {
private static final int SMOOTH_SCROLL_DURATION = 25;
private int mSelectedPosition;
private final WeakReference<YourParentActivity> mParent;
private PositionScroller(YourParentActivity parent) {
mParent = new WeakReference<YourParentActivity>(parent);
}
#Override
public void run() {
final ListView list = mParent.get().mListView;
if (mSelectedPosition <= 5) {
if (list.postDelayed(this, SMOOTH_SCROLL_DURATION)) {
list.setSelection(mSelectedPosition++);
}
}
}
}