I want to animate the change in the padding of a view. The resting place of the translation animation is the same as the padding I want to apply.
TranslateAnimation moveleft = new TranslateAnimation(Animation.ABSOLUTE, 0.0f,
Animation.ABSOLUTE, PADDING, Animation.ABSOLUTE,
0.0f, Animation.ABSOLUTE, 0.0f);
moveLeft.setDuration(500);
moveLeft.setFillAfter(true);
This starts the view's animation then sets the padding. This doesn't exactly work because it cause a graphical glitch.
v.startAnimation(moveleft);
v.setPadding(PADDING, 0, 0,0);
Use ValueAnimator, its really simple and unclutter
say,
we have to change right padding to _20dp where as left, top and bottom padding are _6dp, _6dp and 0 respectively.
ofInt() is varagrs type. the value we have to animate is pass in it as KeyValue pair (arg1 = current value, arg2 = target value,............)
Here we go,
ValueAnimator animator = ValueAnimator.ofInt(view.getPaddingRight(), _20dp);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator valueAnimator){
view.setPadding(_6dp, _6dp, (Integer) valueAnimator.getAnimatedValue(), 0);
}
});
animator.setDuration(200);
animator.start();
Instead of setting your padding right away, why not try an animation listener to set the padding after the animation has completed?
v.setAnimationListener(new Animation.AnimationListener() {
...
#Override
public void onAnimationEnd(){
v.setPadding(PADDING, 0, 0,0);
}
...
});
v.startAnimation(moveleft);
Here is how to animate setPadding in Kotlin:
private fun setPaddingWithAnimation() {
val paddingDp: Int = 20
val density: Float = requireActivity().getResources().getDisplayMetrics().density
val paddingPixel: Int = (paddingDp * density).toInt()
val animator = ValueAnimator.ofInt(recyclerItems.paddingRight, paddingPixel)
animator.addUpdateListener { valueAnimator -> binding.recyclerHealthTips.recyclerHealthTips.setPadding(
paddingPixel, 0, valueAnimator.animatedValue as Int, 0) }
animator.duration = 500
animator.start()
}
Related
Is it possible to control the width animation for a view that has wrap_content? Right now I have a TextView with a barrier to its right side. I have another background View that aligns to this barrier. When the TextView changes text it just flashes to the new width but I want to animate the width with an animator where I can control the timing, interpolator etc. I know that I can set something like animateLayoutChanges but the effect is not customisable.
You can use ObjectAnimator along with AnimatorSet for the animation.
Here is an extension in kotlin for the view on which you want to apply the animation.
inline fun View.animateWidth(duration: Long = 1000, startDelay: Long = 0) {
val scaleX = ObjectAnimator.ofFloat(this, "scaleX", /*Change width*/)
//val scaleY = ObjectAnimator.ofFloat(this, "scaleY", /*Change width*/)
val set = AnimatorSet()
//set.playTogether(scaleX, scaleY)
set.playTogether(scaleX)
set.duration = duration
set.startDelay = startDelay
set.interpolator = LinearInterpolator()
set.addAnimatorListener(
onStart = { this.isClickable = false },
onEnd = { this.isClickable = true }
)
set.start()
}
I have a case where I have 2 edittext email and password. When any one gets focus say email the hint moves up and becomes the heading. In this animation i translate the textview(hint) up and at the same time scale it down to 0.8f.
At the same time the second textview say password looses focus, if its still empty i want to reverse the animation and put back the heading as hint.
To sum up hint moves up as heading and gets back to hint position if the edittext remains empty.
I have two different animation set for both the editText views, they work well individually but on parallel execution one of them doesnt show up instead the hint becomes invisible.
My code is as follows :
email.setOnFocusChangeListener(new View.OnFocusChangeListener() {
#Override
public void onFocusChange(View view, boolean b) {
if(b && emailAnimated==false){
Log.d("email1", "called");
AnimationSet animSet = new AnimationSet(true);
animSet.setFillAfter(true);
animSet.setFillEnabled(true);
animSet.setDuration(800);
TranslateAnimation translate = new TranslateAnimation(0, 0, 0, -60);
animSet.addAnimation(translate);
ScaleAnimation scale = new ScaleAnimation(1f, 0.8f, 1f, 0.8f);
animSet.addAnimation(scale);
emailText.startAnimation(animSet);
emailAnimated=true;
}else if(emailAnimated==true)
{
Log.d("email2", "called");
AnimationSet animSet = new AnimationSet(true);
animSet.setFillAfter(true);
animSet.setFillEnabled(true);
animSet.setDuration(800);
TranslateAnimation translate = new TranslateAnimation(0, 0, 0, -60);
animSet.addAnimation(translate);
ScaleAnimation scale = new ScaleAnimation(1f, 0.8f, 1f, 0.8f);
animSet.addAnimation(scale);
//Reverse the animation if losses focus
animSet.setInterpolator(new ReverseInterpolator());
emailText.startAnimation(animSet);
emailAnimated=false;
}
}
});
password.setOnFocusChangeListener(new View.OnFocusChangeListener() {
#Override
public void onFocusChange(View view, boolean b) {
if(b && passwordAnimated==false){
Log.d("pass1", "called");
AnimationSet animSet = new AnimationSet(true);
animSet.setFillAfter(true);
animSet.setFillEnabled(true);
animSet.setDuration(800);
TranslateAnimation translate = new TranslateAnimation(0, 0, 0, -60);
animSet.addAnimation(translate);
ScaleAnimation scale = new ScaleAnimation(1f, 0.8f, 1f, 0.8f);
animSet.addAnimation(scale);
passwordText.startAnimation(animSet);
passwordAnimated=true;
}
else if(passwordAnimated==true)
{
Log.d("pass2", "called");
AnimationSet animSet = new AnimationSet(true);
animSet.setFillAfter(true);
animSet.setFillEnabled(true);
animSet.setDuration(800);
TranslateAnimation translate = new TranslateAnimation(0, 0, 0, -60);
animSet.addAnimation(translate);
ScaleAnimation scale = new ScaleAnimation(1f, 0.8f, 1f, 0.8f);
animSet.addAnimation(scale);
animSet.setInterpolator(new ReverseInterpolator());
passwordText.startAnimation(animSet);
passwordAnimated=false;
}
}
});
Just to make it more understandable, else if condition of email will be called whenever if condition of password is true and vies versa and thats how 2 animation sets are called.
You can try to set the layer type of the two EditTexts to hardware while animating.
animSet.setAnimationListener(new AnimationListener() {
#Override
public void onAnimationStart( Animation animation ) {
emailText.setLayerType(View.LAYER_TYPE_HARDWARE, null);
}
#Override
public void onAnimationRepeat( Animation animation ) {
}
#Override
public void onAnimationEnd( Animation animation ) {
emailText.setLayerType(View.LAYER_TYPE_NONE, null);
}
});
I have two views in different layouts I want to move one to another. What's wrong with my code? Y animation plays wrong. First view is located in fragment's layout, second in status bar
...
int p1[] = new int[2];
int p2[] = new int[2];
viewOne.getLocationInWindow(p1);
viewTwo.getLocationInWindow(p2);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet
.play(ObjectAnimator.ofFloat(expandedImageView, "X", p1[0], p2[0] - p1[0]))
.with(ObjectAnimator.ofFloat(expandedImageView, "Y", p1[1], p2[1] - p1[1]))
.with(ObjectAnimator.ofFloat(expandedImageView, View.SCALE_X, startScale))
.with(ObjectAnimator.ofFloat(expandedImageView, View.SCALE_Y, startScale));
I have a another solution for you:(move viewOne to viewTwo)
TranslateAnimation animation = new TranslateAnimation(0, viewTwo.getX()-viewOne.getX(),0 , viewTwo.getY()-viewOne.getY());
animation.setRepeatMode(0);
animation.setDuration(3000);
animation.setFillAfter(true);
viewOne.startAnimation(animation);
Try this
private fun moveView(viewToBeMoved: View, targetView: View) {
val targetX: Float =
targetView.x + targetView.width / 2 - viewToBeMoved.width / 2
val targetY: Float =
targetView.y + targetView.height / 2 - viewToBeMoved.height / 2
viewToBeMoved.animate()
.x(targetX)
.y(targetY)
.setDuration(2000)
.withEndAction {
targetView.visibility = View.GONE
}
.start()
}
I have simple animation that moves the object in Y axis. I want change the behavior for the REVERSE.
//My Code:
ImageView iv = ... //my view
ObjectAnimator oa = ObjectAnimator.ofFloat(iv, "y", 300);
oa.setDuration(100);
oa.setRepeatCount(1);
oa.setRepeatMode(ValueAnimator.REVERSE);
oa.start();
You cannot change the behavior of the reverse mode. Instead, you will need to create an AnimatorSet and play them sequentially.
ImageView iv = ... //my view
ObjectAnimator oa = ObjectAnimator.ofFloat(iv, "y", 300);
oa.setDuration(100);
ObjectAnimator oa2 = ObjectAnimator.ofFloat(/* code here */)
// Add any other code for oa2
AnimatorSet set = new AnimatorSet();
set.playSequentially(oa, oa2);
set.start()
I am changing the left margin of an image view in the following manner :
ViewGroup.MarginLayoutParams layoutParams = (MarginLayoutParams) image.getLayoutParams ();
layoutParams.leftMargin = VALUE;
image.setLayoutParams ( layoutParams );
I would like the change in margin to apply with animation. Any clues ?
What I tried :
ObjectAnimator objectAnimator = ObjectAnimator.ofFloat ( image , "x" , VALUE);
objectAnimator.start();
This works perfectly, as the image is moved to the specified X value with animation, HOWEVER the value of layoutParams.leftMargin remains unchanged !! So I cannot use this method, because if I try to change the value of layoutParams.leftMargin to 100 after using the objectAnimator with the value 100, the value applied is not correct ( 200 is applied instead of 100, the effect if the objectAnimator remains eventhough I am setting the left margin in the following manner :
layoutParams.leftMargin = 100;
Use Animation class, not ObjectAnimator.
final int newLeftMargin = <some value>;
Animation a = new Animation() {
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
LayoutParams params = yourView.getLayoutParams();
params.leftMargin = (int)(newLeftMargin * interpolatedTime);
yourView.setLayoutParams(params);
}
};
a.setDuration(500); // in ms
yourView.startAnimation(a);
Please note that you should use correct LayoutParams class i.e. if your view is the child of LinearLayout then params should be LinearLayout.LayoutParams
I came by this question, but I couldn't use it because I want to animate the margin from a negative value to 0, so I used valueAnimater base on user1991679 answer:
final View animatedView = view.findViewById(R.id.animatedView);
final LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) animatedView.getLayoutParams();
ValueAnimator animator = ValueAnimator.ofInt(params.bottomMargin, 0);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator valueAnimator)
{
params.bottomMargin = (Integer) valueAnimator.getAnimatedValue();
animatedView.requestLayout();
}
});
animator.setDuration(300);
animator.start();
You must change LinearLayout.LayoutParams according to animatedView container.
Also you can use nineoldandroids for older version that don't have ValueAnimator.
The answer from user1991679 is great, but if you need to interpolate a margin from any other value but 0, you need to use it in your calculations:
ViewGroup.MarginLayoutParams params = (MarginLayoutParams) mBottomLayout.getLayoutParams();
final int bottomMarginStart = params.bottomMargin; // your start value
final int bottomMarginEnd = <your value>; // where to animate to
Animation a = new Animation() {
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
ViewGroup.MarginLayoutParams params = (MarginLayoutParams) mBottomLayout.getLayoutParams();
// interpolate the proper value
params.bottomMargin = bottomMarginStart + (int) ((bottomMarginEnd - bottomMarginStart) * interpolatedTime);
mBottomLayout.setLayoutParams(params);
}
};
a.setDuration(300);
mBottomLayout.startAnimation(a);
In my case I needed to animate an "enter the screen" animation, coming from "-48dp" to 0. Without the start value, the animation is always 0, thus jumping, not animating the view. The solution was to interpolate the offset and add it to the original value.
Nothing worked for me like I wanted so...
I needed to create ToggleMenu from -80 dp (oldLeftMargin) to 0dp.
Same with bottomMargin, etc.
Now it works:
final int oldLeftMargin = (int) getResources().getDimension(R.dimen.left_menu_button_margin_left);
Animation a = new Animation() {
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) llMeniuToggle.getLayoutParams();
params.leftMargin = oldLeftMargin + (int) ((0 - oldLeftMargin) * interpolatedTime);
llMeniuToggle.setLayoutParams(params);
}
};
a.setDuration(500);
llMeniuToggle.startAnimation(a);
You can use the following
image.animate().setDuration(durationIn).translationXBy(offsetFloat).start();
You can also add .setInterpolator(new BounceInterpolator()) to change the look of the animation.