I'm trying to do a shake animation. It works well when I do it in XML, like this:
<set xmlns:android="http://schemas.android.com/apk/res/android">
<rotate
android:duration="50"
android:fromDegrees="-0.02"
android:pivotX="50%"
android:pivotY="50%"
android:repeatCount="30"
android:repeatMode="reverse"
android:interpolator="#android:anim/linear_interpolator"
android:toDegrees="0.02" />
<translate
android:fromXDelta="-0.02"
android:toXDelta="0.02"
android:repeatCount="30"
android:repeatMode="reverse"
android:interpolator="#android:anim/linear_interpolator"
android:duration="50" />
</set>
But I want to do it using ObjectAnimator, so I'll be able to use AnimatorSet to play this animation at the same time of other animations I've done.
I tried to do it like that:
val pvhR = PropertyValuesHolder.ofFloat(View.ROTATION, -0.05f, 0.05f)
val pvhT = PropertyValuesHolder.ofFloat(View.TRANSLATION_X, -0.05f, 0.05f)
val rotate = ObjectAnimator.ofPropertyValuesHolder(imageView, pvhR). apply {
duration = 50
repeatCount = 20
repeatMode = ValueAnimator.REVERSE
}
val translate = ObjectAnimator.ofPropertyValuesHolder(imageView, pvhT).apply {
duration = 50
repeatCount = 20
repeatMode = ValueAnimator.REVERSE
}
But the translate animation makes my imageView disappear. I had problems with rotation animations before (when I tried to do rotation animation, the imageView changed its position, but I fixed it by using .ofPropertyValuesHolder, instead of using .ofFloat). Probably because I'm using a custom library that implements a zoomable/pinchable layout.
Now I'm trying to do this translate animation, but it doesn't works even with .ofPropertyValuesHolderz. It's working only with XML, but as I said, I can't put it inside an AnimatorSet().
Try this code
val set = AnimatorSet()
val rotation = ObjectAnimator.ofFloat(imageViewMain, "rotation", -0.05f, 0.05f).apply {
duration = 50
repeatCount = 20
repeatMode = ValueAnimator.REVERSE
}
val translation = ObjectAnimator.ofFloat(imageViewMain, "translationX", -0.05f, 0.05f).apply {
duration = 50
repeatCount = 20
repeatMode = ValueAnimator.REVERSE
}
set.playTogether(rotation, translation)
Related
Here is my code to translate imageview in X direction(from left to right).
final Animation animTrans = new TranslateAnimation(0f, 1f, 0f, 0f);
animTrans.setRepeatCount(0);
animTrans.setDuration(200);
animTrans.setFillAfter(true);
circleImage.startAnimation(animTrans);
When it starts to translate the image is displaced in y axis. Here is a picture of what happpens.
But when i use an xml to translate everything works fine. What is the problem with my code ?
Xml:
<translate
android:fromXDelta="0%p"
android:toXDelta="80%p"
android:duration="200"
android:fillAfter="true"
/>
I can't understand why, but
circleImage.animate().
translationXBy(width).
setDuration(200).
start();
solved my problem. I think maybe its a problem with final Animation animTrans = new TranslateAnimation(0f, 1f, 0f, 0f);. Those xdelta, ydelta values may be the reason.
I am writing a launcher.
How do I start an activity to show the Marshmallow animation?
I have looked at the AOSP Launcher3 source and only found it to use this:
if (useLaunchAnimation) {
ActivityOptions opts = Utilities.isLmpOrAbove() ?
ActivityOptions.makeCustomAnimation(this, R.anim.task_open_enter, R.anim.no_anim) :
ActivityOptions.makeScaleUpAnimation(v, 0, 0, v.getMeasuredWidth(), v.getMeasuredHeight());
optsBundle = opts.toBundle();
}
Which then get fed into
startActivity(context, optsBundle);
Where the task_open_enter animation looks like this:
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:background="#ff000000"
android:shareInterpolator="false"
android:zAdjustment="top">
<alpha
android:duration="167"
android:fillAfter="true"
android:fillBefore="true"
android:fillEnabled="true"
android:fromAlpha="0"
android:interpolator="#interpolator/decelerate_quart"
android:startOffset="0"
android:toAlpha="1.0" />
<translate
android:duration="417"
android:fillAfter="true"
android:fillBefore="true"
android:fillEnabled="true"
android:fromYDelta="110%"
android:interpolator="#interpolator/decelerate_quint"
android:startOffset="0"
android:toYDelta="0" />
</set>
Unfortunately these are not the same as the Marshmallow animation I am looking for, as shown below:
(gif is slowed down 3 times)
I found the answer in the launcher source code here:
Bundle optsBundle = null;
ActivityOptions opts = null;
if (Utilities.ATLEAST_MARSHMALLOW) {
int left = 0, top = 0;
int width = v.getMeasuredWidth(), height = v.getMeasuredHeight();
// if (v instanceof TextView) {
// // Launch from center of icon, not entire view
// Drawable icon = Workspace.getTextViewIcon((TextView) v);
// if (icon != null) {
// Rect bounds = icon.getBounds();
// left = (width - bounds.width()) / 2;
// top = v.getPaddingTop();
// width = bounds.width();
// height = bounds.height();
// }
// }
opts = ActivityOptions.makeClipRevealAnimation(v, left, top, width, height);
} else if (!Utilities.ATLEAST_LOLLIPOP) {
// Below L, we use a scale up animation
opts = ActivityOptions.makeScaleUpAnimation(v, 0, 0, v.getMeasuredWidth(), v.getMeasuredHeight());
} else if (Utilities.ATLEAST_LOLLIPOP_MR1) {
// On L devices, we use the device default slide-up transition.
// On L MR1 devices, we use a custom version of the slide-up transition which
// doesn't have the delay present in the device default.
opts = ActivityOptions.makeCustomAnimation(context, R.anim.task_open_enter, R.anim.no_anim);
}
optsBundle = opts != null ? opts.toBundle() : null;
context.startActivity(intent, optsBundle);
I would like to create a ObjectAnimator programmatically that represents this xml -
<objectAnimator
android:duration="#integer/eye_anim_duration"
android:interpolator="#android:interpolator/anticipate_overshoot"
android:propertyName="pathData"
android:repeatCount="0"
android:repeatMode="restart"
android:valueFrom="#string/big"
android:valueTo="#string/small"
android:valueType="pathType"
/>
In similar fashion as I can create a ObjectAnimator for color change -
ValueAnimator animator = ObjectAnimator.ofInt(vector, "fillColor", getResources().getColor(R.color.light_green), getResources().getColor(R.color.red));
animator.setDuration(3000);
Using PathAnimatorInflater class and rewriting the setupAnimatorForPath to accept Strings instead TypedArray it is possible. Here is a method that uses this -
private ObjectAnimator getAnimator(AnimatedVectorDrawable vector, int in, int out, int delay) {
ObjectAnimator animator = new ObjectAnimator();
animator.setTarget(vector);
animator.setPropertyName("pathData");
TypeEvaluator evaluator = PathAnimatorInflater.setupAnimatorForPath(animator, getResources().getString(in), getResources().getString(out));
if (evaluator == null) {
return null;
}
animator.setEvaluator(evaluator);
animator.setRepeatMode(ValueAnimator.REVERSE);
animator.setDuration(3000);
animator.setStartDelay(delay);
return animator;
}
But to use this with AnimatedVectorDrawable it needs some changes in Vector classes also.
I wanna make animation like domino effect.
but when i use animation xml and handler with for loop, it works together at the same time.
how can i make the domino effect animation such as start the animation for next button(tue) 100 milliseconds after the first button(mon) started?
my hanlder method here
private void startAnimation(final ArrayList<View> vArr)
{
mon = (Button)findViewById(R.id.mon);
tue = (Button)findViewById(R.id.tue);
wed = (Button)findViewById(R.id.wed);
thu = (Button)findViewById(R.id.thu);
fri = (Button)findViewById(R.id.fri);
sat = (Button)findViewById(R.id.sat);
sun = (Button)findViewById(R.id.sun);
final Button[] weekDayList = {mon, tue, wed, thu, fri, sat, sun};
Handler handler = new Handler()
{
#Override
public void handleMessage(Message msg)
{
super.handleMessage(msg);
switch (msg.what)
{
case 3:
for (int i = 0; i < weekDayList.length; i++)
weekDayList[i].startAnimation(buttonDown);
sendEmptyMessageDelayed(4, 100);
break;
case 4:
break;
}
}
};
handler.sendEmptyMessage(3);
}
my animation xml here
<set xmlns:android="http://schemas.android.com/apk/res/android">
<scale
android:fromXScale="1.0"
android:toXScale="1.0"
android:fromYScale="1.0"
android:toYScale="0.0"
android:pivotX="50%"
android:pivotY="100%"
android:duration="500" />
</set>
You can use ObjectAnimator (since API 11) to do this and have the animations play sequentially by adding them all to an AnimatorSet.
public void dominoAnimations() {
ObjectAnimator monAnimation = ObjectAnimator.ofFloat(mon, "scaleY", 0f);
ObjectAnimator tueAnimation = ObjectAnimator.ofFloat(tue, "scaleY", 0f);
ObjectAnimator wedAnimation = ObjectAnimator.ofFloat(wed, "scaleY", 0f);
ObjectAnimator thuAnimation = ObjectAnimator.ofFloat(thu, "scaleY", 0f);
ObjectAnimator friAnimation = ObjectAnimator.ofFloat(fri, "scaleY", 0f);
ObjectAnimator satAnimation = ObjectAnimator.ofFloat(sat, "scaleY", 0f);
ObjectAnimator sunAnimation = ObjectAnimator.ofFloat(sun, "scaleY", 0f);
AnimatorSet dominoes = new AnimatorSet();
dominoes.playSequentially(
monAnimation, tueAnimation, wedAnimation,
thuAnimation, friAnimation, satAnimation, sunAnimation
);
dominoes.setDuration(500);
dominoes.start();
}
Set AnimationListener to the animations. Use onAnimationEnd() to start the next animation.
So Id like to rotate a handful of views all at the same time, all using the same rotation specs. The issue is that for some reason the rotation acts differently for the second element. Apparently this has to do with the animation object actually changing state in between those two lines of code. Obviously I could just create a seperate Animation object and apply it, but I feel like there is an easier way (I have about 15 views)
Rotates only the first view correctly:
Animation rotateAnim = AnimationUtils.loadAnimation(this, R.anim.rotationtoportrait);
target.startAnimation(rotateAnim);
lightBtn.startAnimation(rotateAnim);
Rotates both correctly
Animation rotateAnim = AnimationUtils.loadAnimation(this, R.anim.rotationtoportrait);
Animation rotateAnim2 = AnimationUtils.loadAnimation(this, R.anim.rotationtoportrait);
target.startAnimation(rotateAnim);
lightBtn.startAnimation(rotateAnim2);
XML:
<?xml version="1.0" encoding="utf-8"?>
<rotate
xmlns:android="http://schemas.android.com/apk/res/android"
android:fromDegrees="-90"
android:toDegrees="0"
android:pivotX="50%"
android:pivotY="50%"
android:duration="500" android:fillAfter="true">
Anyone have any ideas?
Do it like this:
ObjectAnimator anim = ObjectAnimator.ofFloat(view, "y", 100f);
arrayListObjectAnimators.add(anim);
ObjectAnimator anim1 = ObjectAnimator.ofFloat(view, "x", 0f);
arrayListObjectAnimators.add(anim1);
ObjectAnimator[] objectAnimators = arrayListObjectAnimators.toArray(new ObjectAnimator[arrayListObjectAnimators.size()]);
AnimatorSet animSetXY = new AnimatorSet();
animSetXY.playTogether(objectAnimators);
animSetXY.duration(1000);
animSetXY.start();
So I guess this just isn't possible, so I created a helper method to just apply the same animation to a list of views:
public void doRotations(ArrayList<View> views, int start, int end, int xprop, float xscale, int yprop, float yscale, int duration, Boolean fillAfter){
for(int i = 0; i < views.size(); i++){
RotateAnimation temp = new RotateAnimation(start, end, xprop, xscale, yprop, yscale);
temp.setDuration(duration);
temp.setFillAfter(fillAfter);
views.get(i).startAnimation(temp);
}
}
Definitely a hack, but I guess thats all I'm able to do right now
I was able to do this in Kotlin by programmatically creating one AnimatorSet.
1. Create an ArrayList of the views you want to animate
var viewList = arrayListOf(view1,view2,view3)
2. Loop through the ArrayList and create a growing AnimatorSet
var ix = 0
var anim = AnimatorSet()
var viewList = arrayListOf(view1,view2,view3)
viewList.forEach{
// Initiate the animator set with one ObjectAnimator
if(ix == 0){
anim = AnimatorSet().apply {
play(ObjectAnimator.ofFloat(it, "rotation", 0F, 360F))
}
}
// Add one ObjectAnimator at a time to the growing AnimatorSet
else{
var currentAnim = ObjectAnimator.ofFloat(it,"rotation",0F,360F)
anim = AnimatorSet().apply {
play(anim).with(currentAnim)
}
}
ix++
}
3. Start the animation
button.setOnClickListener {
AnimatorSet().apply {
play(anim)
start()
}