I can't combine two TranslateAnimations, but if i run each animation separatly I have the expected results (works fine). My only problem is when trying to combine both TranslateAnimations in one AnimationSet, in this case no animation is rendered, and after the 4000 ms the image is rendered on top left of the parent view.
This is how I am trying to run both animations in a set:
private void animateView(int width, int height, View view) {
AnimationSet animationSet = new AnimationSet(true);
int startX1 = -(width / 2);
int startY1 = getView().getHeight();
int endX1 = getView().getWidth() / 3;
int endY1 = getView().getHeight() - (getView().getHeight() / 3);
int endX2 = getView().getWidth() - width / 2;
int endY2 = 0;
TranslateAnimation anim1 = new TranslateAnimation(startX1, endX1, startY1, endY1);
anim1.setDuration(2000);
animationSet.addAnimation(anim1);
TranslateAnimation anim2 = new TranslateAnimation(endX1, endX2, endY1, endY2);
anim2.setDuration(2000);
anim2.setStartOffset(2000);
animationSet.addAnimation(anim2);
view.startAnimation(animationSet);
}
Related
I know how to use CircularReveal to reveal a view, so I'm looking for a way to do something like "CircularHide". In other words, I want to invisible a view by a circular animation (increasing radius) after making it visible. How can I do that?
I've written this to reveal:
private void startCircularReveal() {
RelativeLayout changeableLayout = findViewById(R.id.layoutChangeable);
int centerX = (likeButton.getRight() + likeButton.getLeft()) / 2;
int centerY = (likeButton.getBottom() + likeButton.getTop()) / 2;
float endRadius = (float) Math.hypot(changeableLayout.getWidth(), changeableLayout.getHeight());
changeableLayout.setVisibility(View.VISIBLE);
Animator revealAnimator = ViewAnimationUtils.createCircularReveal(changeableLayout,
centerX, centerY, 0, endRadius);
revealAnimator.setDuration(200).start();
}
I'm trying to scale view from start rectangle (e.g. defined by another view) to it's final position.
I tried to use the following code to setup animations which looks straight forward:
float scaleX = 0f;
float scaleY = 0f;
Rect startRect = new Rect(10, 10, 100, 100); // taken from real view position with getLocationOnScreen
final Collection<Animator> animators = new ArrayList<>();
if (animatedView.getMeasuredHeight() != 0) {
scaleX = (float)startRect.width() / animatedView.getMeasuredWidth();
}
if (animatedView.getMeasuredHeight() != 0) {
scaleY = (float)startRect.height() / animatedView.getMeasuredHeight();
}
animatedView.getLocationInWindow(location);
animatedView.setPivotX(startRect.left);
animatedView.setPivotY(startRect.top);
animatedView.setScaleX(scaleX);
animatedView.setScaleY(scaleY);
animators.add(ObjectAnimator.ofFloat(animatedView, View.SCALE_X, 1.0f).setDuration(1000));
animators.add(ObjectAnimator.ofFloat(animatedView, View.SCALE_Y, 1.0f).setDuration(1000));
The animatedView is child of RelativeLayout (layout parameters set to below some title view of layout) and measured width and height and location are valid values at the moment of animation setup.
Depending on startRect I observe different animations - sometimes animated view get displayed below or above startRect.
Seems RectEvaluator is one of possible solutions, but it's available only from API 18.
What is the proper way to animate view from start rectangle position to final (not modified one)?
As per comments on the question, it's possible to copy RectEvaluator code from Android source, and then apply the following logic:
RectViewAnimator mRectAnimator;
/**
* Creates animator which can be played. From some start position
* to final (real position).
* From final position to start position can be achieved using reverse interpolation.
*/
private Collection<Animator> createMoveAnimators(View targetView, Rect startRect) {
final Collection<Animator> animators = new ArrayList<>();
final int[] location = new int[2];
targetView.getLocationOnScreen(location);
final Rect finalRect = new Rect(location[0], location[1],
location[0] + targetView.getMeasuredWidth(),
location[1] + targetView.getMeasuredHeight());
// Must keep this reference during animations, since Animator keeps only WeakReference to it's targets.
mRectAnimator = appendRectEvaluatorAnimation(animators, targetView, 500, startRect, finalRect);
return animators;
}
private RectViewAnimator appendRectEvaluatorAnimation(final Collection<Animator> animators, final View view, final int duration,
final Rect startRect, final Rect finalRect) {
final float scaleX = (float) startRect.width() / finalRect.width();
final float scaleY = (float) startRect.height() / finalRect.height();
view.setTranslationY(startRect.top - (finalRect.top + (1 - scaleY) * finalRect.height() / 2));
view.setTranslationX(startRect.left - (finalRect.left + (1 - scaleX) * finalRect.width() / 2));
view.setScaleX(scaleX);
view.setScaleY(scaleY);
final RectViewAnimator rectViewAnimator = new RectViewAnimator(view, finalRect);
final Animator animator = ObjectAnimator.ofObject(rectViewAnimator, RectViewAnimator.RECT,
new RectEvaluator(), startRect, finalRect);
animators.add(animator);
return rectViewAnimator;
}
private static class RectViewAnimator {
static final String RECT = "rect";
private final View target;
private final Rect finalRect;
RectViewAnimator(final View target, final Rect finalRect) {
this.target = target;
this.finalRect = finalRect;
}
#Keep
public void setRect(final Rect r) {
final float scaleX = (float)r.width() / finalRect.width();
final float scaleY = (float)r.height() / finalRect.height();
target.setScaleX(scaleX);
target.setScaleY(scaleY);
target.setTranslationX(r.left - (finalRect.left + (1 - scaleX) * finalRect.width() / 2));
target.setTranslationY(r.top - (finalRect.top + (1 - scaleY) * finalRect.height() / 2));
}
}
How to implement this animation using curvedmotion because documentation does not have a good example.
https://www.google.com/design/spec/animation/meaningful-transitions.html#meaningful-transitions-hierarchical-timing
As you can see, the path is basically a quarter of an oval. The parametric equation of an oval is like this:
x = a * cos(t)
y = b * cos(t)
See http://www.mathopenref.com/coordparamellipse.html
You have three parameters. Two of them (a, b) are the width and the height of the bounding box of your quarter-oval. The third one can be taken from animation interpolator. To get the right quarter, you need a value from range [PI*3/2, PI*2].
final View fab;
final int startX = fab.getTranslationX(),startY = fab.getTranslationY();
final int endX = 100,endY = 0;
ValueAnimator animator = ValueAnimator.ofFloat((float)(Math.PI*3/2),(float)(Math.PI*2));
animator.setInterpolator(new DecelerateInterpolator());
animator.setDuration(500);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
float t = (float) valueAnimator.getAnimatedValue();
float a = endX-startX;
float b = endY-startY;
fab.setTranslationX((float) (a*Math.cos(t)));
fab.setTranslationY((float) (b*Math.sin(t)));
}
});
animator.start();
I tried to implement createCircularReveal() in FloatingButton. But the animation is too fast. How to increase the duration of the Animation. I tried setDuration(milli-seconds), But its not working.
I follow developer.android.com, Defining Custom Animations
Here my code:
int cx = (fabBtn.getLeft() + fabBtn.getRight()) / 2;
int cy = (fabBtn.getTop() + fabBtn.getBottom()) / 2;
int finalRadius = Math.max(fabBtn.getWidth(), fabBtn.getHeight());
Animator anim = ViewAnimationUtils.createCircularReveal(fabBtn, cx, cy, 2, finalRadius);
anim.setDuration(2000);
fabBtn.setVisibility(View.VISIBLE);
anim.start();
I am also facing the issue as I mentioned and I am able to resolve this issue by getting cx and cy the center position for image view (In your case I think it is button).
So get cx and cy using this:
int cx = (fabBtn.getWidth()) / 2;
int cy = (fabBtn.getHeight()) / 2;
Instead of this::
int cx = (fabBtn.getLeft() + fabBtn.getRight()) / 2;
int cy = (fabBtn.getTop() + fabBtn.getBottom()) / 2;
You might be calling getWidth() and getHeight() too soon.
So you'll have to use a getViewTreeObserver()
Be sure to add a duration to anim.setDuration(time) and set the initial visibility of the view to INVISIBLE
Here the code:
public void checker() {
myView.getViewTreeObserver().addOnPreDrawListener(
new ViewTreeObserver.OnPreDrawListener() {
public boolean onPreDraw() {
int finalHeight = myView.getMeasuredHeight();
int finalWidth = myView.getMeasuredWidth();
// Do your work here
myView.getViewTreeObserver().removeOnPreDrawListener(this);
cx = finalHeight / 2;
cy = finalWidth / 2;
finalRadius = Math.max(finalHeight, finalWidth);
anim = ViewAnimationUtils.createCircularReveal(myView, cx, cy, 0, finalRadius);
myView.setVisibility(View.VISIBLE);
anim.setDuration(3000);
anim.start();
return true;
}
});
}
I thought I had the same problem, but it seems that, for some reason, a duration of just 2000 milliseconds is not enough for the animation to show. When I set the duration to 3000, I saw a beautiful circle animation.
A small delay of 1 second also helped
// get the center for the clipping circle
int cx = myView.getWidth() / 2;
int cy = myView.getHeight() / 2;
// get the final radius for the clipping circle
int finalRadius = Math.max(myView.getWidth(), myView.getHeight());
// create the animator for this view (the start radius is zero)
Animator anim = ViewAnimationUtils.createCircularReveal(myView, cx, cy, 0, finalRadius);
anim.setStartDelay(1000);
// make the view visible when the animation starts
anim.addListener(new AnimatorListenerAdapter()
{
#Override
public void onAnimationStart(Animator animation)
{
super.onAnimationStart(animation);
myView.setVisibility(View.VISIBLE);
}
});
// start the animation
anim.start();
How can I get current position of thumb in pixel for SeekBar?
Currently I am trying to get the position on the bases of calculation.
Screen width, Seek Bar width, current seek progress and all. But I am not able to get exact position out of it.
Do any one have any idea?
Coding for same is as below.
I want to drow one component on top of SeekBar thumb.
translation = calculateTranslation(this.deviceScreenWidth, seekBarWidth, seekBar1T.getMax(), Integer.valueOf(ConstantData.seekbar_fifth.toString()), topComponentWidth, 0);
if (currentapiVersion < android.os.Build.VERSION_CODES.HONEYCOMB){
TranslateAnimation anim = new TranslateAnimation(translation - 1,
translation, 0, 0);
anim.setFillAfter(true);
anim.setDuration(0);
edtTextRodUpDown.startAnimation(anim);
}else{
edtTextRodUpDown.setTranslationX(translation);
}
and calculate Translation like
private float calculateTranslation(int screenWidth, int seekBarWidth, int seekMaxProgress, int currentProgress, int componentWidth, int extraDifference){
double calculatedPosition = 0f;
calculatedPosition = (double)((double)(seekBarWidth-((screenWidth-seekBarWidth))) / (double)seekMaxProgress) * (double)currentProgress;
calculatedPosition = calculatedPosition - (double)(componentWidth/2);
calculatedPosition = calculatedPosition + extraDifference;
Double d = new Double(calculatedPosition);
if(d < 0){
return 0.0f;
}else if(d + componentWidth > screenWidth){
return screenWidth-componentWidth;
}else{
return d.floatValue();
}
}
I did this:
int width = seekBar.getWidth()
- seekBar.getPaddingLeft()
- seekBar.getPaddingRight();
int thumbPos = seekBar.getPaddingLeft()
+ width
* seekBar.getProgress()
/ seekBar.getMax();
You can use the getBounds() to get this info.
This sample code below is for instance aligning a TextView with the seekbar thumb position.
TextView sliderIndicator = ...
Rect bounds = seekBar.getThumb().getBounds();
sliderIndicator.setTranslationX(seekBar.getLeft() + bounds.left);
The seekbar thumb has a thumb offset, which is basically a slight shift to the right. So you have to make sure that you take this into account as well:
int thumbPos = seekbar.getMeasuredWidth() * seekbar.getProgress() / seekbar.getMax() - seekbar.getThumbOffset();