Android recyclerview view holder's internal view's animations - android

I am using an expandable recycler view and I am trying to make a rotation animation on an ImageView that is a child of the ViewHolder. I set a click listener on the a certain button and in there I call the animateIconExpansion method that performs the animation:
private void animateIconExpansion(ImageView expandIcon, boolean isExpanded) {
ObjectAnimator animator = ObjectAnimator.ofFloat(this.expandIcon, "rotation", rotationEnd - 180f, rotationEnd);
animator.setDuration(1000);
animator.start();
}
The problem is that the animation doesn't work properly. I tried using the animate API introduced in 3.0 and it didn't work. I also tried the RotateAnimation and it didn't work:
RotateAnimation rotateAnim = new RotateAnimation(rotationEnd - 180f, rotationEnd,
RotateAnimation.RELATIVE_TO_SELF, 0.5f,
RotateAnimation.RELATIVE_TO_SELF, 0.5f);
rotateAnim.setDuration(200);
rotateAnim.setFillBefore(true);
expandIcon.startAnimation(rotateAnim);
What am I doing wrong?

So the problem I was having is that I was making the animation in Adapter. Which was stated here as a wrong approach.
What we want to do in this case is to create a class that extends DefaultItemAnimator, override the animateChange method and place your custom animation there.
Now the problem with that is that you don't know the state of your view holder. Sometimes you want to animate the holder when a button is clicked. To solve that you can use flags in your view holder, that you will have a reference to in the animateChange method.
Then you'd want to call recyclerView.setItemAnimator(animater) and pass in an instance of your class.

Related

How to properly reuse Animation in Android?

I'm trying to reuse AlphaAnimation object in a method of an Activity for every new MyView added every second to myLayout:
private AlphaAnimation showAnimation = new AlphaAnimation(0, 1);
private void addViewAndAnimate() {
MyView view = new MyView();
myLayout.addView(view);
showAnimation.setDuration(durationTime);
view.startAnimation(showAnimation);
}
but after I call addViewAndAnimate() the animation is not applied for latest added MyView only, but gets repeated for all MyViews added earlier.
Are view objects somehow bound to an animation? If so is there a way to detach them?
To remove animation from the View, use View.clearAnimation();
So you need to store somewhere a view, to which the animation is applied before, and clear it before applying it to new view. Also you can try to create a new Animation instance for each view.
Or start animation in Handler.post(). This shouldstart animation in the current view for sure

recyclerview animation not working with objectanimator

enter code hereI want to implement a animation for items in a Recyclerview using ObjectAnimator. I have followed this tutorial
Here is my code for getting animations:
public static void animate(RecyclerView.ViewHolder holder, boolean direction)
{
AnimatorSet animatorSet=new AnimatorSet();
ObjectAnimator animator_Y=ObjectAnimator.ofFloat(holder.itemView,"TranslationY",direction==true ? 100 : -100,0);
animator_Y.setDuration(1000);
ObjectAnimator animator_X=ObjectAnimator.ofFloat(holder.itemView,"TranslationX",-20,20,0);
animator_X.setDuration(1000);
animatorSet.playTogether(animator_X,animator_Y);
animatorSet.start();
Log.e("animation","animation running");
}
I am calling this function from onBindViewHolder(). There is no animation from translationX and translationY. And I am sure that this method is being called everytime I scroll.
I want know that ObjectAnimator is used for Cardview or not. Thank you.`
This is my onBindViewHolder Method
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.textView.setText(data.get(position));
AnimationforAdapters.animate(holder,true);
}
Try the following code:
public static void animate(RecyclerView.ViewHolder holder, boolean direction) {
AnimatorSet animatorSet=new AnimatorSet();
ObjectAnimator animator_Y=ObjectAnimator.ofFloat(holder.itemView,"translationY",direction ? 100 : -100,0);
animator_Y.setDuration(1000);
ObjectAnimator animator_X=ObjectAnimator.ofFloat(holder.itemView,"translationX",-20,20,0);
animator_X.setDuration(1000);
animatorSet.playTogether(animator_X,animator_Y);
animatorSet.start();
Log.e("animation","animation running");
}
As mentioned in the android documentation for ObjectAnimator
This subclass of ValueAnimator provides support for animating properties on target objects. The constructors of this class take parameters to define the target object that will be animated as well as the name of the property that will be animated. Appropriate set/get functions are then determined internally and the animation will call these functions as necessary to animate the property.
So, the problem in your code was that the ObjectAnimator could not find properties TranslationX and TranslationY in your target object that can be animated.
use this its work for me :
public void slideAnimation(RecyclerView.ViewHolder holder, boolean animate) {
ObjectAnimator animatorY = ObjectAnimator.ofFloat(holder.itemView, "translationY", animate ? 40 : -40, 1f);
ObjectAnimator animatorX = ObjectAnimator.ofFloat(holder.itemView, "translationX", animate ? 40 : -40, 1f);
AnimatorSet set = new AnimatorSet();
set.playTogether(animatorX , animatorY);
set.setDuration(1000);
set.start();
}
try by maintaining reference of your row layout parent root xml tag into holder and use that instead of using holder.itemview.
Sorry guys, I have not enabled the animations in my device.Make Window animation scale,Transition animation scale,Animator duration scale to 1x inside the developer options.

Android view animation stops when view out of screen

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()

View moves two times longer than I want in animation

I want to have myView move deltaX, deltaY pixels from current position.
But it moves two times longer than I want.
This is caused by setLayoutParams in onAnimationEnd.
Without that, myView's real positon is not changed.
How can I change myView's real positon and animate normally?
class MyLayout extends RelativeLayout {
Animation animation;
MyView myView;
animation = new TranslateAnimation(0, deltaX, 0, deltaY);
animation.setDuration(100);
animation.setFillAfter(true);
myView.startAnimation(animation);
}
class MyView extends ImageView {
protected void onAnimationEnd() {
super.onAnimationEnd();
LayoutParams params = (LayoutParams)getLayoutParams();
params.leftMargin += deltaX;
params.topMargin += deltaY;
setLayoutParams(params);
}
}
If you want to avoid doing the desired translation of the view twice as you are doing it now, you have to set the "setFillAfter" flag for the animation to false. As the documentation says "When setFillAfter is set to true, the animation transformation is applied after the animation is over."
On the other hand if you leave the "setFillAfter" flag to true, the view should translate to the desired location and no LayoutParams modification is needed in onAnimationEnd method. So it might be that something is blocking the transformation, if you could provide further information about the layout that the view is part of, it would be mostly helpful.
Another thing is that setting the margin of the layout params to move the view is not entirely the best idea. Take it more like a hack. Rather try to set the position of the view directly or try to play with weights in your parent view. That way you will get the same effect on multiple different screens.

Rotating a view in Android

I have a button that I want to put on a 45 degree angle. For some reason I can't get this to work.
Can someone please provide the code to accomplish this?
API 11 added a setRotation() method to all views.
You could create an animation and apply it to your button view. For example:
// Locate view
ImageView diskView = (ImageView) findViewById(R.id.imageView3);
// Create an animation instance
Animation an = new RotateAnimation(0.0f, 360.0f, pivotX, pivotY);
// Set the animation's parameters
an.setDuration(10000); // duration in ms
an.setRepeatCount(0); // -1 = infinite repeated
an.setRepeatMode(Animation.REVERSE); // reverses each repeat
an.setFillAfter(true); // keep rotation after animation
// Aply animation to image view
diskView.setAnimation(an);
Extend the TextView class and override the onDraw() method. Make sure the parent view is large enough to handle the rotated button without clipping it.
#Override
protected void onDraw(Canvas canvas) {
canvas.save();
canvas.rotate(45,<appropriate x pivot value>,<appropriate y pivot value>);
super.onDraw(canvas);
canvas.restore();
}
I just used the simple line in my code and it works :
myCusstomView.setRotation(45);
Hope it works for you.
One line in XML
<View
android:rotation="45"
... />
Applying a rotation animation (without duration, thus no animation effect) is a simpler solution than either calling View.setRotation() or override View.onDraw method.
// substitude deltaDegrees for whatever you want
RotateAnimation rotate = new RotateAnimation(0f, deltaDegrees,
Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
// prevents View from restoring to original direction.
rotate.setFillAfter(true);
someButton.startAnimation(rotate);
Rotating view with rotate() will not affect your view's measured size. As result, rotated view be clipped or not fit into the parent layout. This library fixes it though:
https://github.com/rongi/rotate-layout
Joininig #Rudi's and #Pete's answers. I have created an RotateAnimation that keeps buttons functionality also after rotation.
setRotation() method preserves buttons functionality.
Code Sample:
Animation an = new RotateAnimation(0.0f, 180.0f, mainLayout.getWidth()/2, mainLayout.getHeight()/2);
an.setDuration(1000);
an.setRepeatCount(0);
an.setFillAfter(false); // DO NOT keep rotation after animation
an.setFillEnabled(true); // Make smooth ending of Animation
an.setAnimationListener(new AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {}
#Override
public void onAnimationRepeat(Animation animation) {}
#Override
public void onAnimationEnd(Animation animation) {
mainLayout.setRotation(180.0f); // Make instant rotation when Animation is finished
}
});
mainLayout.startAnimation(an);
mainLayout is a (LinearLayout) field
As mentioned before, the easiest way it to use rotation available since API 11:
android:rotation="90" // in XML layout
view.rotation = 90f // programatically
You can also change pivot of rotation, which is by default set to center of the view. This needs to be changed programatically:
// top left
view.pivotX = 0f
view.pivotY = 0f
// bottom right
view.pivotX = width.toFloat()
view.pivotY = height.toFloat()
...
In Activity's onCreate() or Fragment's onCreateView(...) width and height are equal to 0, because the view wasn't measured yet. You can access it simply by using doOnPreDraw extension from Android KTX, i.e.:
view.apply {
doOnPreDraw {
pivotX = width.toFloat()
pivotY = height.toFloat()
}
}
if you wish to make it dynamically with an animation:
view.animate()
.rotation(180)
.start();
THATS IT
#Ichorus's answer is correct for views, but if you want to draw rotated rectangles or text, you can do the following in your onDraw (or onDispatchDraw) callback for your view:
(note that theta is the angle from the x axis of the desired rotation, pivot is the Point that represents the point around which we want the rectangle to rotate, and horizontalRect is the rect's position "before" it was rotated)
canvas.save();
canvas.rotate(theta, pivot.x, pivot.y);
canvas.drawRect(horizontalRect, paint);
canvas.restore();
fun rotateArrow(view: View): Boolean {
return if (view.rotation == 0F) {
view.animate().setDuration(200).rotation(180F)
true
} else {
view.animate().setDuration(200).rotation(0F)
false
}
}
That's simple,
in Java
your_component.setRotation(15);
or
your_component.setRotation(295.18f);
in XML
<Button android:rotation="15" />

Categories

Resources