Android Studio 1.5
Device Samsung 4.4.2
I am trying to animate items loaded from a ArrayList into a recyclerview. I when the dropdown arrow is clicked the items should animate (decelerate) when expanded and should animate when collapsed. However, currently the list items just appears.
Code that calls the setAnimation
#Override
public void onBindChildViewHolder(ChatChildViewHolder childViewHolder, int position, Object childListItem) {
ChatChildTitles chatChildTitles = (ChatChildTitles)childListItem;
childViewHolder.tvChildTitle.setText(chatChildTitles.getTitle());
setAnimation(childViewHolder.cvChildRooms, position);
}
Code for setting the animation
private void setAnimation(CardView viewToAnimate, int position) {
Animation animation = AnimationUtils.loadAnimation(mContext, android.R.anim.fade_in);
animation.setInterpolator(mContext, android.R.anim.decelerate_interpolator);
viewToAnimate.startAnimation(animation);
}
Here is a couple of screenshots:
In the collapsed state
After the arrow has been clicked expland the list
This is my layout I am using that represents the rows that will be displayed in the recyclerView:
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView
android:id="#+id/cvChildRooms"
xmlns:card="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
card:cardBackgroundColor="#color/child_header_lighter_grey"
card:contentPadding="4dp"
card:cardPreventCornerOverlap="true">
<de.hdodenhof.circleimageview.CircleImageView
android:id="#+id/profile_image"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_gravity="center_vertical|start"
android:src="#drawable/photorace"/>
<TextView
android:id="#+id/tvChildTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|center"
android:text="Coffee Latte Room"
android:fontFamily="sans-serif-light"
android:textSize="16sp"
android:textColor="#android:color/black"/>
</android.support.v7.widget.CardView>
I have a function that should start the animation.
private void setAnimation(CardView viewToAnimate, int position) {
Animation animation = AnimationUtils.loadAnimation(mContext, android.R.anim.decelerate_interpolator);
viewToAnimate.startAnimation(animation);
}
I have tested using the following that works ok with slide_in_left. However, I don't want them to slide in from the left
Animation animation = AnimationUtils.loadAnimation(mContext, android.R.anim.slide_in_left);
viewToAnimate.startAnimation(animation);
Many thanks for any suggestions,
If you want to use a decelerate interpolator you need to set it AS an interpolator, not as the animator:
private void setAnimation(CardView viewToAnimate, int position) {
Animation animation = AnimationUtils.loadAnimation(mContext, android.R.anim.fade_in); //change this with your desidered (or custom) animation
animation.setInterpolator(mContext, android.R.anim.decelerate_interpolator);
viewToAnimate.startAnimation(animation);
}
UPDATE
You said that you are using com.bignerdranch.android:expandablerecyclerview:2.0.3.
From the official docs of the library, it's clearly state how to create expand/collapse animations:
You can also create your own animations for expansion by overriding
ParentViewHolder#onExpansionToggled(boolean), which will be called for
you when the itemView is expanded or collapsed.
I suggest you to take a look at the official example of the library.
You can't use decelerate_interpolator because it's not an animation, it is an interpolator:
An interpolator defines the rate of change of an animation. This
allows the basic animation effects (alpha, scale, translate, rotate)
to be accelerated, decelerated, repeated, etc.
Reference:
http://developer.android.com/reference/android/view/animation/Interpolator.html
As you can see the XML that describing them are completely different:
Source of decelerate_interpolator.xml:
<decelerateInterpolator />
Source of slide_in_left.xml:
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate android:fromXDelta="-50%p" android:toXDelta="0"
android:duration="#android:integer/config_mediumAnimTime"/>
<alpha android:fromAlpha="0.0" android:toAlpha="1.0"
android:duration="#android:integer/config_mediumAnimTime" />
</set>
To animate the list as they expand and collapse, consider using an ItemAnimator from android.
You will need to set up a custom itemAnimator, something similar to the one in the link below:
https://gist.github.com/ademar111190/dc988c8d899dae0193f7
Set the itemAnimator in the method runPendingAnimations to your decelerate interpolator.
#Override
public void runPendingAnimations() {
if (!mViewHolders.isEmpty()) {
int animationDuration = 300;
AnimatorSet animator;
View target;
for (final RecyclerView.ViewHolder viewHolder : mViewHolders) {
target = viewHolder.itemView;
target.setPivotX(target.getMeasuredWidth() / 2);
target.setPivotY(target.getMeasuredHeight() / 2);
animator = new AnimatorSet();
animator.playTogether(
ObjectAnimator.ofFloat(target, "translationX", -target.getMeasuredWidth(), 0.0f),
ObjectAnimator.ofFloat(target, "alpha", target.getAlpha(), 1.0f)
);
animator.setTarget(target);
animator.setDuration(animationDuration);
animator.setInterpolator(new DecelerateInterpolator());
animator.setStartDelay((animationDuration * viewHolder.getPosition()) / 10);
animator.addListener(new AnimatorListener() {
#Override
public void onAnimationEnd(Animator animation) {
mViewHolders.remove(viewHolder);
}
});
animator.start();
}
}
}
Then you will need to set the itemAnimator to the recycler view.
recyclerView.setItemAnimator(new MyItemAnimator());
You can use below code for do that.
private void hideViews() {
recyclerView.animate().translationY(-recyclerView.getHeight()).setInterpolator(new AccelerateInterpolator(2));
FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mFabButton.getLayoutParams();
int fabBottomMargin = lp.bottomMargin;
mFabButton.animate().translationY(mFabButton.getHeight()+fabBottomMargin).setInterpolator(new AccelerateInterpolator(2)).start();
}
private void showViews() {
recyclerView.animate().translationY(0).setInterpolator(new DecelerateInterpolator(2));
mFabButton.animate().translationY(0).setInterpolator(new DecelerateInterpolator(2)).start();
}
You can call this method onClick of your Button.
Hope it will help you.
Related
I am trying to write an animation like in WhatsApp Call screen. But I don't know what is the true way to achieve this.
To achieve this animation I am starting trying with fadein and fadeout animation. These are my set methods for fade in and out animations.
private Animation setAnimFadeOut(int startOff,int duration){
Animation animFadeOut;
animFadeOut = new AlphaAnimation(1, 0);
animFadeOut.setInterpolator(new AccelerateInterpolator());
animFadeOut.setStartOffset(startOff);
animFadeOut.setDuration(duration);
return animFadeOut;
}
private Animation setAnimFadeIn(int startOff,int duration){
Animation animFadeIn;
animFadeIn = new AlphaAnimation(0, 1);
animFadeIn.setInterpolator(new AccelerateInterpolator());
animFadeIn.setStartOffset(startOff);
animFadeIn.setDuration(duration);
return animFadeIn;
}
and for every animations animationlisteners onAnimationEnd method triggers animation for restart. fadeIn animation starts fadeOut animation and fadeOut starts fadeIn animation.
right1FadeOut.setAnimationListener(new Animation.AnimationListener() {
#Override
public void onAnimationEnd(Animation animation) {
right1.startAnimation(right1FadeIn);
Log.i(TAG, "onAnimationEnd: 1 outEnd");
}
});
right1FadeIn.setAnimationListener(new Animation.AnimationListener() {
Override
public void onAnimationEnd(Animation animation) {
right1.startAnimation(right1FadeOut);
Log.i(TAG, "onAnimationEnd: 1 inEnd");
}
});
Initialization
int startOff = 0;
int diff = 100;
int duration = 600;
final Animation right1FadeOut = setAnimFadeOut(startOff,duration);
final Animation right1FadeIn = setAnimFadeIn(0,duration);
final Animation right2FadeOut = setAnimFadeOut(startOff+diff,duration+diff);
final Animation right2FadeIn = setAnimFadeIn(0,duration);
final Animation right3FadeOut = setAnimFadeOut(startOff+diff*2,duration+diff*2);
final Animation right3FadeIn = setAnimFadeIn(0,duration);
I am starting animation calling fadeout for every button and it did not work as I expected. How can I achieve animation like WhatsApp?
right1.startAnimation(right1FadeOut);
right2.startAnimation(right2FadeOut);
right3.startAnimation(right3FadeOut);
this is the result.
I would first use Animator objects instead of Animation, then i can use AnimatorSet to control all animators as a group. (aka: order)
For example:
activity XML:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/txt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clickable="true"
android:focusable="true"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<ImageView
android:id="#+id/img1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:alpha="0"
android:src="#drawable/ic_launcher_foreground" />
<ImageView
android:id="#+id/img2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:alpha="0"
android:src="#drawable/ic_launcher_foreground" />
<ImageView
android:id="#+id/img3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:alpha="0"
android:src="#drawable/ic_launcher_foreground" />
<ImageView
android:id="#+id/img4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:alpha="0"
android:src="#drawable/ic_launcher_foreground" />
</LinearLayout>
</android.support.constraint.ConstraintLayout>
Activity Class:
Java:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
View[] images = {findViewById(R.id.img1), findViewById(R.id.img2), findViewById(R.id.img3), findViewById(R.id.img4),}; //array of views that we want to animate
//we will have 2 animator foreach view, fade in & fade out
//prepare animators - creating array of animators & instantiating Object animators
ArrayList<ObjectAnimator> anims = new ArrayList<>(images.length * 2);
for (View v : images) anims.add(ObjectAnimator.ofFloat(v, View.ALPHA, 0f, 1f).setDuration(80)); //fade in animator
for (View v : images) anims.add(ObjectAnimator.ofFloat(v, View.ALPHA, 1f, 0f).setDuration(80)); //fade out animator
final AnimatorSet set = new AnimatorSet(); //create Animator set object
//if we want to repeat the animations then we set listener to start again in 'onAnimationEnd' method
set.addListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
set.start(); //repeat animator set indefinitely
}
});
set.setStartDelay(600); //set delay every time we start the chain of animations
for (int i = 0; i < anims.size() - 1; i++) set.play(anims.get(i)).before(anims.get(i + 1)); //put all animations in set by order (from first to last)
findViewById(R.id.txt).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) { //start the animations on click
set.start();
}
});
}
}
Kotlin:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val images = arrayOf(img1, img2, img3, img4) //array of views that we want to animate
//we will have 2 animator foreach view, fade in & fade out
//prepare animators - creating array of animators & instantiating Object animators
val anims = ArrayList<ObjectAnimator>(images.size * 2)
for (v in images) anims.add(ObjectAnimator.ofFloat(v, View.ALPHA, 0f, 1f).setDuration(80)) //fade in animator
for (v in images) anims.add(ObjectAnimator.ofFloat(v, View.ALPHA, 1f, 0f).setDuration(80)) //fade out animator
val set = AnimatorSet() //create Animator set object
//if we want to repeat the animations then we set listener to start again in 'onAnimationEnd' method
set.addListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator?) = set.start() //repeat animator set indefinitely
})
set.startDelay = 600 //set delay every time we start the chain of animations
for (i in 0 until anims.size - 1) set.play(anims[i]).before(anims[i + 1]) //put all animations in set by order (from first to last)
txt.setOnClickListener { set.start() } //start the animations on click
}
}
Try to start your subsequent animations in your AnimationListeners onAnimationStart method with an increasing delay.
arrow1FadeIn.setAnimationListener( new Animation.AnimationListener() {
#Override
public void onAnimationStart( Animation animation )
{
arrow2.startAnimation( arrow2FadeIn );
}
#Override
public void onAnimationEnd( Animation animation )
{
arrow1.startAnimation( arrow1FadeOut );
}
#Override
public void onAnimationRepeat( Animation animation )
{
}
} );
arrow1FadeOut.setAnimationListener( new Animation.AnimationListener()
{
#Override
public void onAnimationStart( Animation animation )
{
}
#Override
public void onAnimationEnd( Animation animation )
{
arrow1.startAnimation( arrow1FadeIn );
}
#Override
public void onAnimationRepeat( Animation animation )
{
}
} );
And your animations like
final Animation arrow1FadeIn = setAnimFadeIn( startOff, duration );
final Animation arrow1FadeOut = setAnimFadeOut( startOff, duration );
final Animation arrow2FadeIn = setAnimFadeIn( diff, duration );
final Animation arrow2FadeOut = setAnimFadeOut( startOff, duration );
final Animation arrow3FadeIn = setAnimFadeIn( diff*2, duration );
final Animation arrow3FadeOut = setAnimFadeOut( startOff, duration );
You may need to twiddle a bit when starting all over again but this way, they should be in sync. Just start the first fadeIn Animation with
arrow1.startAnimation( arrow1FadeIn );
I suggest you use Facebook Rebound library.
It support Spring animation like facebook has. It also has cool feature called SpringChain, which automatically play a sequence of animation using Spring physics from start to end. You can custom how you want to animate the View (scale, alpha, translate ...)
I am trying to slid a view from the bottom of the screen and collapse another view a little everything will fit correctly.
I have managed to do that with the attribute animateLayoutChanges and 2 simple xml animations slid_up.xml and slid_down.xml.
My problem is that the animation that happens to ViewPager from the attribute animateLayoutChanges isn't smooth.
Is there a way to fix that?
slid_up.xml
<translate
android:duration="1000"
android:fromYDelta="100%"
android:toYDelta="0" />
</set>
slid_down.xml
<translate
android:duration="1000"
android:fromYDelta="0"
android:toYDelta="100%" />
</set>
P.S. I have tried to create custom animators as Height animators but it messes with the original height of the view.
HeightResizeAnimation
public class HeightResizeAnimation extends Animation {
private View mView;
private float mToHeight;
private float mFromHeight;
private int duration = 300;
public HeightResizeAnimation(View v, float offset) {
mView = v;
mToHeight = v.getHeight() + offset;
mFromHeight = v.getHeight();
setDuration(duration);
}
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
float newHeight = (mToHeight - mFromHeight) * interpolatedTime + mFromHeight;
mView.getLayoutParams().height = (int) newHeight;
mView.requestLayout();
}
}
after the animation the height will no longer be as match_parent that was before the animation.
Update
Here is the animation that happens now
You can see that the fab animation to bottom isn't smooth also for the viewpager that the fab is child
It seems like you just want to show a snackbar + move the FAB along with it as it shows?
To do so you could use a combination of Snackbar and CoordinatorLayout from the Android Support Library: http://www.androidhive.info/2015/09/android-material-design-snackbar-example/
In your xml layout add the following in the View:
android:visibility="gone"
android:alpha="0"
android:id="#+id/text_view_liked"
I am asumming the view to be TextView.
On the click of ImageView liked animate the text_view_liked with the following:
final TextView textViewLiked = (TextView) findViewById(R.id.text_view_get_liked);
final FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
PropertyValuesHolder pvhYTextViewLiked = PropertyValuesHolder
.ofFloat(View.Y,
textViewLiked.getBottom(),
(textViewLiked.getBottom() - textViewLiked.getHeight()));
PropertyValuesHolder pvhAlphaTextViewLiked = PropertyValuesHolder
.ofFloat(View.ALPHA, 0f, 1f);
ObjectAnimator objectAnimator = ObjectAnimator.ofPropertyValuesHolder(textViewLiked,
pvhYTextViewLiked,
pvhAlphaTextViewLiked);
ObjectAnimator objectAnimatorFab = ObjectAnimator.ofFloat(fab, View.Y, fab.getY(),
fab.getY() - textViewLiked.getHeight());
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.play(objectAnimator).with(objectAnimatorFab);
animatorSet.addListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationStart(Animator animation) {
super.onAnimationStart(animation);
textViewLiked.setVisibility(View.VISIBLE);
}
#Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
PropertyValuesHolder pvhYTextViewLiked = PropertyValuesHolder
.ofFloat(View.Y,
textViewLiked.getTop(),
(textViewLiked.getTop() + textViewLiked.getHeight()));
PropertyValuesHolder pvhAlphaTextViewLiked = PropertyValuesHolder
.ofFloat(View.ALPHA, 1f, 0f);
ObjectAnimator objectAnimator = ObjectAnimator.ofPropertyValuesHolder(textViewLiked,
pvhYTextViewLiked,
pvhAlphaTextViewLiked);
ObjectAnimator objectAnimatorFab = ObjectAnimator.ofFloat(fab, View.Y, fab.getY(),
fab.getY() + textViewLiked.getHeight());
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.play(objectAnimator).with(objectAnimatorFab);
animatorSet.addListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
textViewLiked.setVisibility(View.GONE);
}
});
animatorSet.start();
}
}, 1000);
}
});
You can adjust the duration accordingly
new Handler is created just to automate the process of progress indicator is finished. You can just call on the finish of progress indicator.
After all the solution was to use the android:clipChildren="false"
As can be seen on the gif, the FAB is getting cropped while it is going down to the original position. So after I have used the android:clipChildren="false" on the parent of the FAB viewPager then the FAB isn't getting cropped anymore.
I'm having a recyclerView with several views and one is the animation view which has an animation applied to it. Once the view is out of the screen the animation is no longer active, even though the animation still exists.
The data:
rotate_around_center_point.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false" >
<rotate
android:duration="2500"
android:interpolator="#android:anim/linear_interpolator"
android:pivotX="50%"
android:pivotY="50%"
android:repeatCount="infinite"
android:repeatMode="restart"
android:toDegrees="360" />
</set>
Applying animation:
animation = AnimationUtils.loadAnimation(this.getContext(),
R.anim.rotate_around_center_point);
loadingRotatingCircleIV.startAnimation(animation);
I could not find any way to catch an event when the animation is interrupted so I'm able to restart the animation once it was out of the screen.
RecyclerView is incorrectly stopping animation on the view when view goes out from screen only if you apply View Animation to that view. If you will use Property Animation everything works properly. So the following solution will work:
ObjectAnimator animator = ObjectAnimator.ofFloat(loadingRotatingCircleIV, "rotation", 0.0f, 360.0f);
animator.setDuration(1000L);
animator.setRepeatCount(ObjectAnimator.INFINITE);
animator.setInterpolator(new LinearInterpolator());
animator.start();
If you want to use a ViewAnimation for a RecyclerView item, you have to restore animation in overriden method of RecyclerView - onViewAttachedToWindow(ItemViewHolder holder), like:
#Override
public void onViewAttachedToWindow(ItemViewHolder holder) {
super.onViewAttachedToWindow(holder);
if (holder.animated)
{
Animation anim = new ScaleAnimation(0.8f, 1.2f,
0.8f, 1.2f,
Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f);
anim.setFillAfter(true);
anim.setInterpolator(new BounceInterpolator());
anim.setDuration(1100);
anim.setRepeatCount(Animation.INFINITE);
anim.setRepeatMode(Animation.REVERSE);
holder.animatedView.startAnimation(anim);
}
}
I'm doing the exact same thing right now, and having the exact same problem. So the question is how to restart the animation once the view is back on screen.
I got the problem 99% resolved just by calling "loadingRotatingCircleIV.startAnimation(animation);" inside onBindViewHolder every time it gets called for my animated view's position.
But there's just a tiny little problem left. If you scroll down by just a little, so the view gets off screen, but it's view holder doesn't get recycled, when you scroll back up, onBindViewHolder is obviously not called again, but the animation is stopped.. So I'm still trying to fix that one..
So, my current solution:
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
...
if (position=="animated_view_position") view.startAnimation(animation);
...
}
If somebody has a better solution, please help.
create **HashMap<Integer, Boolean>** for saving each items animation loaded status
construct (){
for(int c = 0; c < adapterDataList.size(); c++){
//Here is create default status of animation loaded status used Boolean to saving
}
}
//Call Start Animation with onViewAttachedToWindow
#Override
public void onViewAttachedToWindow(ItemViewHolder holder) {
super.onViewAttachedToWindow(holder);
// get HashMap of each items Animation status like this
if(!HashMap.get(position)){ //FALSE means animation not loaded yet
//Here should call start Animation method;
} else {
//Here call no animation method, like setImageDrawable etc...
}
//create an AnimationListener for each item, when an animation is End, Listener should call a method to change HashMap above which you just created, like this **HashMap.put(position, Boolean.valueOf(true))** //TRUE means animation loaded
}
//to get position value as Integer use holder.getLayoutPosition()
How can I animate the RecyclerView Items when there are appearing?
The default item animator only animates when a data is added or removed after the recycler data has been set.
How can this be achieved?
EDIT :
According to the ItemAnimator documentation :
This class defines the animations that take place on items as changes are made to the adapter.
So unless you add your items one by one to your RecyclerView and refresh the view at each iteration, I don't think ItemAnimator is the solution to your need.
Here is how you can animate the RecyclerView items when they appear using a CustomAdapter :
public class CustomAdapter extends RecyclerView.Adapter<CustomAdapter.ViewHolder>
{
private Context context;
// The items to display in your RecyclerView
private ArrayList<String> items;
// Allows to remember the last item shown on screen
private int lastPosition = -1;
public static class ViewHolder extends RecyclerView.ViewHolder
{
TextView text;
// You need to retrieve the container (ie the root ViewGroup from your custom_item_layout)
// It's the view that will be animated
FrameLayout container;
public ViewHolder(View itemView)
{
super(itemView);
container = (FrameLayout) itemView.findViewById(R.id.item_layout_container);
text = (TextView) itemView.findViewById(R.id.item_layout_text);
}
}
public CustomAdapter(ArrayList<String> items, Context context)
{
this.items = items;
this.context = context;
}
#Override
public CustomAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
{
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.custom_item_layout, parent, false);
return new ViewHolder(v);
}
#Override
public void onBindViewHolder(ViewHolder holder, int position)
{
holder.text.setText(items.get(position));
// Here you apply the animation when the view is bound
setAnimation(holder.itemView, position);
}
/**
* Here is the key method to apply the animation
*/
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;
}
}
}
And your custom_item_layout would look like this :
<FrameLayout
android:id="#+id/item_layout_container"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="#+id/item_layout_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceListItemSmall"
android:gravity="center_vertical"
android:minHeight="?android:attr/listPreferredItemHeightSmall"/>
</FrameLayout>
For more information about CustomAdapters and RecyclerView, refer to this training on the official documentation.
Problems on fast scroll
Using this method could cause problems with fast scrolling. The view could be reused while the animation is been happening. In order to avoid that is recommendable to clear the animation when is detached.
#Override
public void onViewDetachedFromWindow(final RecyclerView.ViewHolder holder)
{
((CustomViewHolder)holder).clearAnimation();
}
On CustomViewHolder:
public void clearAnimation()
{
mRootLayout.clearAnimation();
}
Old answer :
Give a look at Gabriele Mariotti's repo, I'm pretty sure you'll find what you need. He provides simple ItemAnimators for the RecyclerView, such as SlideInItemAnimator or SlideScaleItemAnimator.
Made Simple with XML only
Visit Gist Link
res/anim/layout_animation.xml
<?xml version="1.0" encoding="utf-8"?>
<layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
android:animation="#anim/item_animation_fall_down"
android:animationOrder="normal"
android:delay="15%" />
res/anim/item_animation_fall_down.xml
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="500">
<translate
android:fromYDelta="-20%"
android:toYDelta="0"
android:interpolator="#android:anim/decelerate_interpolator"
/>
<alpha
android:fromAlpha="0"
android:toAlpha="1"
android:interpolator="#android:anim/decelerate_interpolator"
/>
<scale
android:fromXScale="105%"
android:fromYScale="105%"
android:toXScale="100%"
android:toYScale="100%"
android:pivotX="50%"
android:pivotY="50%"
android:interpolator="#android:anim/decelerate_interpolator"
/>
</set>
Use in layouts and recylcerview like:
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layoutAnimation="#anim/layout_animation"
app:layout_behavior="#string/appbar_scrolling_view_behavior" />
I animated fading in of Recyclerview items when they first appear as shown in the code below. Perhaps this will be of use to someone.
private final static int FADE_DURATION = 1000; //FADE_DURATION in milliseconds
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.getTextView().setText("some text");
// Set the view to fade in
setFadeAnimation(holder.itemView);
}
private void setFadeAnimation(View view) {
AlphaAnimation anim = new AlphaAnimation(0.0f, 1.0f);
anim.setDuration(FADE_DURATION);
view.startAnimation(anim);
}
You can also replace setFadeAnimation() with the following setScaleAnimation() to animate appearance of items by scaling them from a point:
private void setScaleAnimation(View view) {
ScaleAnimation anim = new ScaleAnimation(0.0f, 1.0f, 0.0f, 1.0f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
anim.setDuration(FADE_DURATION);
view.startAnimation(anim);
}
The code above has some warts in so far as when you scroll the RecyclerView items always fade or scale. If you wish you can add code to just allow the animation to happen when the fragment or activity containing the RecyclerView is first created (e.g. get the system time on creation and only allow animation for the first FADE_DURATION milliseconds).
I created animation from pbm's answer with little modification to make the aninmation run only once
in the other word the Animation appear with you scroll down only
private int lastPosition = -1;
private void setAnimation(View viewToAnimate, int position) {
// If the bound view wasn't previously displayed on screen, it's animated
if (position > lastPosition) {
ScaleAnimation anim = new ScaleAnimation(0.0f, 1.0f, 0.0f, 1.0f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
anim.setDuration(new Random().nextInt(501));//to make duration random number between [0,501)
viewToAnimate.startAnimation(anim);
lastPosition = position;
}
}
and in onBindViewHolder call the function
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.getTextView().setText("some text");
// call Animation function
setAnimation(holder.itemView, position);
}
You can add a android:layoutAnimation="#anim/rv_item_animation" attribute to RecyclerView like this:
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layoutAnimation="#anim/layout_animation_fall_down"
/>
thanks for the excellent article here:
https://proandroiddev.com/enter-animation-using-recyclerview-and-layoutanimation-part-1-list-75a874a5d213
A good place to start is this:
https://github.com/wasabeef/recyclerview-animators/blob/master/animators/src/main/java/jp/wasabeef/recyclerview/adapters/AnimationAdapter.java
You don't even need the full library, that class is enough.
Then if you just implement your Adapter class giving an animator like this:
#Override
protected Animator[] getAnimators(View view) {
return new Animator[]{
ObjectAnimator.ofFloat(view, "translationY", view.getMeasuredHeight(), 0)
};
}
#Override
public long getItemId(final int position) {
return getWrappedAdapter().getItemId(position);
}
you'll see items appearing from the bottom as they scroll, also avoiding the problem with the fast scroll.
Animating items in the recyclerview when they are binded in the adapter might not be the best idea as that can cause the items in the recyclerview to animate at different speeds. In my case, the item at the end of the recyclerview animate to their position quicker then the ones at the top as the ones at the top have further to travel so it made it look untidy.
The original code that I used to animate each item into the recyclerview can be found here:
http://frogermcs.github.io/Instagram-with-Material-Design-concept-is-getting-real/
But I will copy and paste the code in case the link breaks.
STEP 1: Set this inside your onCreate method so that you ensure the animation only run once:
if (savedInstanceState == null) {
pendingIntroAnimation = true;
}
STEP 2: You will need to put this code into the method where you want to start the animation:
if (pendingIntroAnimation) {
pendingIntroAnimation = false;
startIntroAnimation();
}
In the link, the writer is animating the toolbar icons, so he put it inside this method:
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_main, menu);
inboxMenuItem = menu.findItem(R.id.action_inbox);
inboxMenuItem.setActionView(R.layout.menu_item_view);
if (pendingIntroAnimation) {
pendingIntroAnimation = false;
startIntroAnimation();
}
return true;
}
STEP 3: Now write the logic for the startIntroAnimation():
private static final int ANIM_DURATION_TOOLBAR = 300;
private void startIntroAnimation() {
btnCreate.setTranslationY(2 * getResources().getDimensionPixelOffset(R.dimen.btn_fab_size));
int actionbarSize = Utils.dpToPx(56);
toolbar.setTranslationY(-actionbarSize);
ivLogo.setTranslationY(-actionbarSize);
inboxMenuItem.getActionView().setTranslationY(-actionbarSize);
toolbar.animate()
.translationY(0)
.setDuration(ANIM_DURATION_TOOLBAR)
.setStartDelay(300);
ivLogo.animate()
.translationY(0)
.setDuration(ANIM_DURATION_TOOLBAR)
.setStartDelay(400);
inboxMenuItem.getActionView().animate()
.translationY(0)
.setDuration(ANIM_DURATION_TOOLBAR)
.setStartDelay(500)
.setListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
startContentAnimation();
}
})
.start();
}
My preferred alternative:
I would rather animate the whole recyclerview instead of the items inside the recyclerview.
STEP 1 and 2 remains the same.
In STEP 3, as soon as your API call returns with your data, I would start the animation.
private void startIntroAnimation() {
recyclerview.setTranslationY(latestPostRecyclerview.getHeight());
recyclerview.setAlpha(0f);
recyclerview.animate()
.translationY(0)
.setDuration(400)
.alpha(1f)
.setInterpolator(new AccelerateDecelerateInterpolator())
.start();
}
This would animate your entire recyclerview so that it flys in from the bottom of the screen.
Create this method into your recyclerview Adapter
private void setZoomInAnimation(View view) {
Animation zoomIn = AnimationUtils.loadAnimation(context, R.anim.zoomin);// animation file
view.startAnimation(zoomIn);
}
And finally add this line of code in onBindViewHolder
setZoomInAnimation(holder.itemView);
In 2019,
I would suggest putting all the item animations into the ItemAnimator.
Let's start with declaring the animator in the recycler-view:
with(view.recycler_view) {
adapter = Adapter()
itemAnimator = CustomAnimator()
}
Declare the custom animator then,
class CustomAnimator() : DefaultItemAnimator() {
override fun animateAppearance(
holder: RecyclerView.ViewHolder,
preInfo: ItemHolderInfo?,
postInfo: ItemHolderInfo): Boolean{} // declare what happens when a item appears on the recycler view
override fun animatePersistence(
holder: RecyclerView.ViewHolder,
preInfo: ItemHolderInfo,
postInfo: ItemHolderInfo): Boolean {} // declare animation for items that persist in a recycler view even when the items change
}
Similar to the ones above there is one for disappearance animateDisappearance, for add animateAdd, for change animateChange and move animateMove.
One important point would be to call the correct animation-dispatchers inside them.
Just extends your Adapter like below
public class RankingAdapter extends AnimatedRecyclerView<RankingAdapter.ViewHolder>
And add super method to onBindViewHolder
#Override
public void onBindViewHolder(ViewHolder holder, final int position) {
super.onBindViewHolder(holder, position);
It's automate way to create animated adapter like "Basheer AL-MOMANI"
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.ScaleAnimation;
import java.util.Random;
/**
* Created by eliaszkubala on 24.02.2017.
*/
public class AnimatedRecyclerView<T extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<T> {
#Override
public T onCreateViewHolder(ViewGroup parent, int viewType) {
return null;
}
#Override
public void onBindViewHolder(T holder, int position) {
setAnimation(holder.itemView, position);
}
#Override
public int getItemCount() {
return 0;
}
protected int mLastPosition = -1;
protected void setAnimation(View viewToAnimate, int position) {
if (position > mLastPosition) {
ScaleAnimation anim = new ScaleAnimation(0.0f, 1.0f, 0.0f, 1.0f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
anim.setDuration(new Random().nextInt(501));//to make duration random number between [0,501)
viewToAnimate.startAnimation(anim);
mLastPosition = position;
}
}
}
I think, is better to use it like this: (in RecyclerView adapter override just a one method)
override fun onViewAttachedToWindow(holder: ViewHolder) {
super.onViewAttachedToWindow(holder)
setBindAnimation(holder)
}
If You want every attach animation there in RV.
Using this code I only have a fade in, I'm looking for how to add a fade out as well. I've added another xml called "fadeout" but I can't integrate it in my code.
ImageView imageView = (ImageView)findViewById(R.id.imageView);
Animation fadeInAnimation = AnimationUtils.loadAnimation(this, R.anim.fadein);
imageView.startAnimation(fadeInAnimation);
button1.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
imageView.startAnimation(fadeInAnimation);
}
}
fadein.xml
<?xml version="1.0" encoding="UTF-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<alpha android:fromAlpha="0.0" android:toAlpha="1.0"
android:interpolator="#android:anim/accelerate_interpolator"
android:duration="Your Duration(in milisecond)"
android:repeatCount="infinite"/>
</set>
Here is my solution. It uses AnimatorSet. AnimationSet library was too buggy to get working. This provides seamless and infinite transitions between fade in and out.
public static void setAlphaAnimation(View v) {
ObjectAnimator fadeOut = ObjectAnimator.ofFloat(v, "alpha", 1f, .3f);
fadeOut.setDuration(2000);
ObjectAnimator fadeIn = ObjectAnimator.ofFloat(v, "alpha", .3f, 1f);
fadeIn.setDuration(2000);
final AnimatorSet mAnimationSet = new AnimatorSet();
mAnimationSet.play(fadeIn).after(fadeOut);
mAnimationSet.addListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
mAnimationSet.start();
}
});
mAnimationSet.start();
}
This is Good Example for Fade In and Fade Out Animation with Alpha Effect
Animate Fade In Fade Out
UPDATED :
check this answer may this help you
I´ve been working in Kotlin (recommend to everyone), so the syntax might be a bit off.
What I do is to simply to call:
v.animate().alpha(0f).duration = 200
I think that, in Java, it would be the following.:
v.animate().alpha(0f).setDuration(200);
Try:
private void hide(View v, int duration) {
v.animate().alpha(0f).setDuration(duration);
}
private void show(View v, int duration) {
v.animate().alpha(1f).setDuration(duration);
}
According to the documentation AnimationSet
Represents a group of Animations that should be played together. The
transformation of each individual animation are composed together into
a single transform. If AnimationSet sets any properties that its
children also set (for example, duration or fillBefore), the values of
AnimationSet override the child values
AnimationSet mAnimationSet = new AnimationSet(false); //false means don't share interpolators
Pass true if all of the animations in this set should use the
interpolator associated with this AnimationSet. Pass false if each
animation should use its own interpolator.
ImageView imageView= (ImageView)findViewById(R.id.imageView);
Animation fadeInAnimation = AnimationUtils.loadAnimation(this, R.anim.fade_in);
Animation fadeOutAnimation = AnimationUtils.loadAnimation(this, R.anim.fade_out);
mAnimationSet.addAnimation(fadeInAnimation);
mAnimationSet.addAnimation(fadeOutAnimation);
imageView.startAnimation(mAnimationSet);
I hope this will help you.
we can simply use:
public void animStart(View view) {
if(count==0){
Log.d("count", String.valueOf(count));
i1.animate().alpha(0f).setDuration(2000);
i2.animate().alpha(1f).setDuration(2000);
count =1;
}
else if(count==1){
Log.d("count", String.valueOf(count));
count =0;
i2.animate().alpha(0f).setDuration(2000);
i1.animate().alpha(1f).setDuration(2000);
}
}
where i1 and i2 are defined in the onCreateView() as:
i1 = (ImageView)findViewById(R.id.firstImage);
i2 = (ImageView)findViewById(R.id.secondImage);
count is a class variable initilaized to 0.
The XML file is :
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<ImageView
android:id="#+id/secondImage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="animStart"
android:src="#drawable/second" />
<ImageView
android:id="#+id/firstImage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="animStart"
android:src="#drawable/first" />
</RelativeLayout>
#drawable/first and #drawable/second are the images in the drawable folder in res.
You can do this also in kotlin like this:
contentView.apply {
// Set the content view to 0% opacity but visible, so that it is visible
// (but fully transparent) during the animation.
alpha = 0f
visibility = View.VISIBLE
// Animate the content view to 100% opacity, and clear any animation
// listener set on the view.
animate()
.alpha(1f)
.setDuration(resources.getInteger(android.R.integer.config_mediumAnimTime).toLong())
.setListener(null)
}
read more about this in android docs
Please try the below code for repeated fade-out/fade-in animation
AlphaAnimation anim = new AlphaAnimation(1.0f, 0.3f);
anim.setRepeatCount(Animation.INFINITE);
anim.setRepeatMode(Animation.REVERSE);
anim.setDuration(300);
view.setAnimation(anim); // to start animation
view.setAnimation(null); // to stop animation
FOR FADE add this first line with your animation's object.
.animate().alpha(1).setDuration(2000);
FOR EXAMPLE
Today, I got stuck with this problem. This worked for me:
#JvmStatic
fun setFadeInFadeOutAnimation(v: View?) {
val fadeIn = ObjectAnimator.ofFloat(v, "alpha", 0.0f, 1f)
fadeIn.duration = 300
val fadeOut = ObjectAnimator.ofFloat(v, "alpha", 1f, 0.0f)
fadeOut.duration = 2000
fadeIn.addListener(object : AnimatorListenerAdapter(){
override fun onAnimationEnd(animation: Animator?) {
fadeOut.start()
}
})
fadeIn.start()
}