I am trying to move a needle image on meter with the help of RotateAnimation.
I am using the following code
mNeedleAnimation = new RotateAnimation(mTransformedDegree, mTransformedDegree + degreeToRotate,x, y);
mTransformedDegree += degreeToRotate;
mNeedleAnimation.setAnimationListener(this);
mNeedleAnimation.setDuration(200);
mNeedleAnimation.setFillEnabled(true);
mNeedleAnimation.setFillAfter(true);
mNeedle.startAnimation(mNeedleAnimation);
Problem with this code is that needle jumps directly from 1 value to other. But i want the needle movement to be smooth. I also tried to use the setInterPolator as linear for the animation but it didn't worked as expected.
Can anyone help me for the same?
Updating the needle view code
final ViewTreeObserver viewTreeObserver = mNeedle.getViewTreeObserver();
viewTreeObserver.addOnPreDrawListener(new OnPreDrawListener() {
#Override
public boolean onPreDraw()
{
mNeedleHeight= mNeedle.getHeight();
mNeedleWidth = mNeedle.getWidth();
return true;
}
});
You can use animation interporlators and instantiate any sub-class and set it to the animation.
mNeedleAnimation.setInterpolator(new AccelerateDecelerateInterpolator());
I am doing this :
rotanim = new RotateAnimation(
(float) (((currenspeed) * 360) / 180),
(float) (((nextspeed) * 360) / 180),
(float) imgNeedle.getWidth() / 2,
(float) imgNeedle.getHeight());
rotanim.reset();
rotanim.setDuration(400);
rotanim.setFillAfter(true);
imgNeedle.startAnimation(rotanim);
and it works good for me..
Related
I am wanting to hide an extended fab when it is clicked using an Animation. I want to scale the button down from original height and width to 0/gone. It disappears in the corner but the animation is laggy in the middle.
private fun animateHideScoreFab(){
val animX = ValueAnimator.ofInt(editScoreFab.measuredWidth, 0).apply {
duration = 1000
}
val animY = ValueAnimator.ofInt(editScoreFab.measuredHeight, 0).apply {
duration = 1000
}
animX.addUpdateListener {
val value = it.animatedValue as Int
editScoreFab.layoutParams.width = value
editScoreFab.requestLayout()
}
animY.addUpdateListener {
val value = it.animatedValue as Int
editScoreFab.layoutParams.height = value
}
AnimatorSet().apply {
this.interpolator = AccelerateDecelerateInterpolator()
play(animX).with(animY)
start()
}
}
Result:
Using AccelerateDecelerateInterpolator https://developer.android.com/reference/android/view/animation/AccelerateDecelerateInterpolator says
An interpolator where the rate of change starts and ends slowly but accelerates through the middle.
So in the middle the rate of change is faster than the start which could make it look laggy
Try another Interpolator like LinearInterpolator https://developer.android.com/reference/android/view/animation/LinearInterpolator.html which has a constant rate of change.
Update
For AccelerateDecelerateInterpolator if you look at the table in https://developer.android.com/guide/topics/graphics/prop-animation.html#interpolators between 400ms and 600ms the the value jumps 45.5% of the distance you and animating
The other factor of smoothness is not to use an Int but use a Float e.g. ValueAnimator.ofFloat so it has intermediate steps,
Update2
Re-laying out an item is expensive as it has to be measured and redrawn.
It should be faster and smoother just to scale the already drawn image as this is usually done by the GPU and thus faster and smoother. Also scaling a view takes a Float
example of scaling a Fab to top right onClick (Note using ObjectAnimator as it is simpler to implement)
Sorry in Java not Kotlin
FloatingActionButton fab = findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
AnimatorSet animationSet = new AnimatorSet();
// Set point to scale around to top right
view.setPivotY(0);
view.setPivotX(view.getMeasuredWidth());
ObjectAnimator scaleY = ObjectAnimator.ofFloat(view,"scaleY", 1f, 0f);
scaleY.setDuration(1000);
// Optional as AccelerateDecelerateInterpolator is the default
scaleY.setInterpolator(new AccelerateDecelerateInterpolator());
ObjectAnimator scaleX = ObjectAnimator.ofFloat(view,"scaleX", 1f, 0f);
scaleX.setDuration(1000);
// Optional as AccelerateDecelerateInterpolator is the default
scaleX.setInterpolator(new AccelerateDecelerateInterpolator());
animationSet.playTogether(scaleX, scaleY);
animationSet.start();
}
});
There's a scaleX and scaleY property in Views that you can interpolate from 1 to 0, this will give the effect of the View shrinking and not the container.
Second, for a scale animation work to the top if your parent layout params doesn't do for you, you have to set the pivot to the rightXtop corner with: view.pivotX = view.width; view.pivotY = 0
Also, if you using propertyListener and updating it yourself you can use only one ValueAnimator and do the calc yourself plus consider replacing editScoreFab.requestLayout() with editScoreFab.invalidate()
After few hours of trying I'm looking for some hints on how to add snap-scroll mechanism to MPAndroid. Basically I want the 5 visible bars to align so they are fully visible and centered. I now imported the library source code because it looks like there's no other way to change the code in computeScroll (BarLineChartTouchListener).
Edit:
To clarify - I'm showing around 20 bars but chart is zoomed so user can scroll horizontally. What bothers me it is not getting aligned automatically so first visible bar might be clipped in half. I'm looking for snapping effect where it will round the position to the nearest multiplication of the bar width, leaving 5 fully visible bars.
I ended up adding the following function in BarLineChartBase.java. I know it's far from elegant, but seems to do the job. It's limited to targetApi > 11, because of the ValueAnimator. For lower API (which I don't cater for) you might need to have a look at nineoldandroids or some other animation loop technique.
#TargetApi(Build.VERSION_CODES.HONEYCOMB)
public void alignX() {
int count = this.getValueCount();
int xIndex = this.getLowestVisibleXIndex() + Math.round( (this.getHighestVisibleXIndex() - this.getLowestVisibleXIndex()) / 2.0f );
float xsInView = this.getXAxis().getValues().size() / this.getViewPortHandler().getScaleX();
Transformer mTrans = this.getTransformer(YAxis.AxisDependency.LEFT);
float[] pts = new float[] { xIndex - xsInView / 2f, 0 };
mTrans.pointValuesToPixel(pts);
final Matrix save = new Matrix();
save.set(this.getViewPortHandler().getMatrixTouch());
final float x = pts[0] - this.getViewPortHandler().offsetLeft();
final int frames = 20;
ValueAnimator valueAnimator = new ValueAnimator().ofInt(0, frames);
valueAnimator.setDuration(500);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
int prev = -1;
#Override
public void onAnimationUpdate(ValueAnimator animation) {
if( (int) animation.getAnimatedValue() > prev ) {
save.postTranslate( -x / (float)frames, 0);
BarLineChartBase.this.getViewPortHandler().refresh(save, BarLineChartBase.this, true);
}
prev = (int) animation.getAnimatedValue();
}
});
valueAnimator.start();
}
I trigger it at the end of computeScroll function in BarLineChartTouchListener.
I kept names of variables as I copied code from functions like MoveViewJob, ViewPortHandler etc. Since it's only aligning in x axis - I removed Y axis calculations and used zeros instead. Any optimizations welcome, especially from the author #PhilippJahoda.
I am trying to animate an ImageView using TranslateAnimation from it's current position (0,Yscreensize/2) to the other side of the screen (Xscreensize,imageview.getY()) but I can't manage it to work. here is my code:
dart = (ImageView) findViewById(R.id.dart);
Display disp = getWindowManager().getDefaultDisplay();
Point poi = new Point();
disp.getSize(poi);
sizex = poi.x;
sizey = poi.y;
ViewTreeObserver vto = dart.getViewTreeObserver();
vto.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
public boolean onPreDraw() {
dart.getViewTreeObserver().removeOnPreDrawListener(this);
finalHeight = dart.getMeasuredHeight();
dart.setY(sizey / 2 - finalHeight);
return true;
}
}); // till now - got screen size and set dart imageview to Y middle.
Now when I am trying to use the animation:
TranslateAnimation animation = new TranslateAnimation(dart.getX(), sizex, dart.getY(), dart.getY());
animation.setDuration(10000);
dart.startAnimation(animation); // from current x to screen size, from currenty to currenty.
this will not work and the dart just dissappear. what can I do?
Make the variable for the animation a class variable (to ensure it won't be garbage collected to soon). In order to make the ImageView stop its movement before it leaves the screen, you need an additional class variable
float finalWidth;
Then change your OnPreDrawListener like this:
vto.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener()
{
public boolean onPreDraw()
{
dart.getViewTreeObserver().removeOnPreDrawListener(this);
float finalHeight = dart.getMeasuredHeight();
// if you want the view to stop before it leaves the screen
float finalWidth = dart.getMeasuredWidth();
animation = new TranslateAnimation(0, sizex - finalWidth, 0, 0);
animation.setDuration(10000);
// use this if you want the changes to persist
// animation.setFillAfter(true);
dart.setY(sizey / 2 - finalHeight);
return true;
}
});
I used a button to call 'startAnimation()'. The only problem when testing with an emulator is that the ImageView keeps running until it reaches the emulator window egde. So it disappears below the hardware controls region on the right side. In order to make it stop earlier, I tested with
Rect myRect = new Rect();
dart.getWindowVisibleDisplayFrame(myRect);
sizex = myRect.width() * 0.9f;
sizey = myRect.height();
which almost did the trick. It's just an approximation, all right, but getting the exact dimensions of the display - taking into account paddings or insets - seems to be difficult. At least below API 20.
Hope this helps anyway :)
I've got a Custom ViewGroup class and an image I've added to it. When the screen is tapped I want to animate the added image to travel across the screen in a wavelike pattern. What I have now is below but though the image moves in a wavelike pattern it jumps around too quickly and is a blur. How can I slow it down to move in a steady wave?
ValueAnimator animator = ValueAnimator.ofFloat(0, 1); // values from 0 to 1
if(animateImage) {
incrementalValue = 0f;
animator.setDuration(4000);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
float value = ((Float) (animation.getAnimatedValue()))
.floatValue();
float amplitude = 100f;
mImage.setTranslationX(incrementalValue);
mImage.setTranslationY((float) (amplitude * Math.sin((incrementalValue) * Math.PI)));
incrementalValue += 5f;
}
});
animator.setTarget(mImage);
animator.start();
}
You declare a float value in onAnimationUpdate and never use it again. The calcualtion for the image translation is mImage.setTranslationY((float) (amplitude * Math.sin((incrementalValue) * Math.PI)));. If you insert the values and incrementalValue ( = 0) doesn't change, this calculation will always return 0 because sin(0) = 0 and one 0 in a multiplication makes the product 0. You should insert animator.getAnimatedValue for incrementalValue. Besides, i suggest you change the calculation to sin(animator.getAnimatedValue()*PI*2) this makes the wavelength of the sine 1 and the image will move up and down once per repetition of the animation and it won't jump when the aimation restarts.
Note you can still multiply with amplitude to make the image move a certain distance.
I'm currently trying to animate the position of a button using a TranslateAnimation. I've read in multiple places that the way to make the actual button move as well as the drawable is to change its layout after the animation. I've done that, but I've run into two problems/solutions:
Either I add setFillAfter( true ). This is nice because the drawable persists after the animation, but when the layout is changed, the drawable is offset from where it should be by the translation distance, while the blank frame of the button is now where it should be.
Add setFillAfter( false ). Doing this and then setting the layout after the animation works like it should, but the icon will flash, I assume this is the delay between the ending of the animation and the refresh of the screen with the new layout parameters. I'm currently using this code, but the flash is unacceptable, so I'd like to find a solution to fix it.
Here's my code currently:
final View aniView = v; // v is some view
TranslateAnimation ani = new TranslateAnimation(0,
240 - v.getLeft() - v.getWidth() / 2,
0,
240 - v.getTop() - v.getHeight() / 2 );
ani.setDuration( 500 );
ani.setFillAfter( false );
ani.setAnimationListener( new AnimationListener() {
public void onAnimationEnd( Animation a ) {
aniView.setTag( new Boolean( true ) );
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams( aniView.getWidth(),
aniView.getHeight() );
params.leftMargin = 240 - aniView.getWidth() / 2;
params.topMargin = 240 - aniView.getHeight() / 2;
aniView.setLayoutParams(params);
}
public void onAnimationStart( Animation a ) {}
public void onAnimationRepeat( Animation a ) {}
});
v.animationStart( ani );
I've figured out the solution to my own question, and will post the code for anyone who comes across the same problem. Basically the idea is to set the layout params before hand and essentially reverse the animation. Here's the idea:
TranslateAnimation ani = new TranslateAnimation(v.getLeft() + v.getWidth() / 2 - 240 ,
0,
v.getTop() + v.getHeight() / 2 - 240,
0);
ani.setDuration( 500 );
ani.setFillAfter( true );
ani.setDuration( 1000 );
ani.setFillAfter( true );
ani.setAnimationListener( new AnimationListener() {
public void onAnimationEnd( Animation a ) {
aniView.setTag( new Boolean( true ) );
}
public void onAnimationStart( Animation a ) {}
public void onAnimationRepeat( Animation a ) {}
});
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams( v.getWidth(),
v.getHeight() );
params.leftMargin = 240 - aniView.getWidth() / 2;
params.topMargin = 240 - aniView.getHeight() / 2;
v.setLayoutParams(params);
v.startAnimation( set );
Completing the awesome answer from Pete, I had the same problem trying to animate a full activity layout (the flashing in the end of the animation), but I used view.translateX instead of Layout Params... And this solution is also applicable to my case.