I have a RelativeLayout with 4 Button side by side. On certain event I wish to change position / Change margin of all four Button with animation.
I am currently doing it with following code
final RelativeLayout.MarginLayoutParams params1 = (RelativeLayout.MarginLayoutParams) button1.getLayoutParams();
final RelativeLayout.MarginLayoutParams params2 = (RelativeLayout.MarginLayoutParams) button2.getLayoutParams();
final RelativeLayout.MarginLayoutParams params3 = (RelativeLayout.MarginLayoutParams) button3.getLayoutParams();
final RelativeLayout.MarginLayoutParams params4 = (RelativeLayout.MarginLayoutParams) button4.getLayoutParams();
final RelativeLayout.MarginLayoutParams params5 = (RelativeLayout.MarginLayoutParams) button5.getLayoutParams();
ValueAnimator animator1 = ValueAnimator.ofInt(params1.rightMargin, (deviceWidth - availableWithForTabs + spaceForTabs));
ValueAnimator animator2 = ValueAnimator.ofInt(params2.rightMargin, dpToPx(78));
ValueAnimator animator3 = ValueAnimator.ofInt(params3.rightMargin, dpToPx(52));
ValueAnimator animator4 = ValueAnimator.ofInt(params4.rightMargin, dpToPx(26));
ValueAnimator animator5 = ValueAnimator.ofInt(params5.rightMargin, dpToPx(0));
animator5.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
params5.rightMargin = (Integer) valueAnimator.getAnimatedValue();
}
});
animator5.setDuration(150);
animator5.start();
animator4.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
params4.rightMargin = (Integer) valueAnimator.getAnimatedValue();
}
);
animator4.setDuration(150);
animator4.start();
animator3.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
params3.rightMargin = (Integer) valueAnimator.getAnimatedValue();
}
});
animator3.setDuration(150);
animator3.start();
animator2.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
params2.rightMargin = (Integer) valueAnimator.getAnimatedValue();
}
});
animator2.setDuration(150);
animator2.start();
animator1.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
params1.rightMargin = (Integer) valueAnimator.getAnimatedValue();
button1.requestLayout();
}
});
animator1.setDuration(150);
animator1.start();
I feel above code is too much to animate margin change for 5 button. Can any one tell me a better way of doing this? or what other options i have? I have to support sdk 16 +
You might want to rethink your design, using ViewPropertyAnimator could save you you a lot lines of code and is very readable/maintainable.
Using ViewPropertyAnimator is pretty simple :
Button button1 = (Button) findViewById(R.id.button1);
button1.animate()
.translationX(toX)
.translationY(toY)
.setDuration(milliseconds); // add more if you
// like (alpha, startDelay)
// check the docs for available methods
The animation always starts at the current position of the View.
For the sake of cleaner code why dont you try something like this :
Write your own Animation Method that takes a View(in your case a Button), and all the values that you need as parameters (minimal implementation of moving a view on the x and y axis) :
private void animateView(View view, float toX, float toY, int duration) {
view.animate()
.translationX(toX)
.translationY(toY)
.setDuration(duration);
}
Then you can simply call this method on your buttons to animate them individually f.e. :
animateView(button1, 5.0f, 2.5f, 150);
animateView(button2, 2.5f, 1.0f, 150);
of course 5.0f, 2.5f etc are only fictional numbers, you'd have to fill in where you want to move the Views yourself.
Extra Suggestion : It is always nice to use Interpolators to make your animation more life like f. e. :
view.animate()
.translationX(...)
.translationY(...)
.setInterpolator(new AccelerateDecelerateInterpolator())
.setDuration(...);
If this was not thorough enough or anything is unclear let me know.
UPDATE :
If you want to set a listener you can either implement the Animator.AnimatorListener Interface in your class like this :
public class yourClass extends AppCompatActivity implements Animator.AnimatorListener {...
Then you are forced to implement the following methods :
#Override
public void onAnimationEnd(Animator animation) {
// here you can call stuff that should happen when the animation ends,
// f.e. start the next animation
// the method names explain themselves
}
#Override
public void onAnimationStart(Animator animation) {
}
#Override
public void onAnimationCancel(Animator animation) {
}
#Override
public void onAnimationRepeat(Animator animation) {
}
Then all you have to do is add another line to your animateView method :
view.animate()
...
...
.setListener(this);
Or you can do it in an anonymous inner class like this :
view.animate()
...
...
.setListener(new Animator.AnimatorListener() {
#Override
public void onAnimationEnd(Animator animation) {
}
// same methods as above ...
...
)};
Why don't you just animate the whole relative layout containing the 4 buttons.
Also I suggest to use View.animate method
public ViewPropertyAnimator animate ()
Added in API level 12
This method returns a ViewPropertyAnimator object, which can be used to animate specific properties on this View.
Returns
ViewPropertyAnimator The ViewPropertyAnimator associated with this View.
Related
I'm developing an m3u livestreaming app, and I want to show all of the channels in a listview. Now I don't know how to make a listview appear from the right as shown in the picture with a slide animation when a button is clicked and make it disappear (slide out animation) when I click the button again.
Can somebody help me out ?picture of what I want to achieve
Edit: I tried this, but it didn't work unfortunately.
//slide in imgbutton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
imgbuttonisclicked = true;
ValueAnimator widthAnimator = ValueAnimator.ofInt(0, 255);
widthAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
int animatedValue = (int) animation.getAnimatedValue();
list.getLayoutParams().width = animatedValue;
list.requestLayout();
}
});
//slide out if (imgbuttonisclicked){
widthAnimator = ValueAnimator.ofInt(255, 0);
widthAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
int animatedValue = (int) animation.getAnimatedValue();
list.getLayoutParams().width = animatedValue;
list.requestLayout();
}
});
}
}
});
You could perhaps play on the width property of the list. Set it to 0 at first then on the button click event animate it to a bigger width. You can learn more about property animation in this article.
Edit: This answer provides a better solution.
I'm trying to change the X and Y scales of a Matrix object in conjunction with the changing shape of an ImageView to make it seem like the ImageView is merely a mask.
So in order to do that, I would change the scale of the ImageView's Matrix to reciprocate the changing scale of the ImageView itself.
So far, I'm using a single ValueAnimator to change the X scale of the Matrix and it worked:
final ValueAnimator xScaleIterationMatrix = ValueAnimator.ofFloat((1/xScaleSrcToDst)*initMatDstVals[0], initMatDstVals[0]);
final Matrix mat = initMatrixDst;
xScaleIterationMatrix.setDuration(animDurationCommon);
xScaleIterationMatrix.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
mat.setScale((float)xScaleIterationMatrix.getAnimatedValue(), initMatDstVals[4]);
toReplace.interfaceImageView().setImageMatrix(mat);
}
});
xScaleIterationMatrix.start();
But that's just one ValueAnimator, and only one property (scaleX) of the Matrix.
I was thinking of using 2 ValueAnimators (to change the scaleX and scaleY [while setting the matrix in the latter]) to do that, but I wish for something simpler.
Can I use an ObjectAnimator for this task?
EDIT: The sample code for the 2 ValueAnimators proposal:
final ValueAnimator xScaleIterationMatrix = ValueAnimator.ofFloat(xScaleSrcToDst * initMatSrcVals[0], initMatSrcVals[0]);
xScaleIterationMatrix.setDuration(animDurationCommon);
xScaleIterationMatrix.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
resultXScale = (float)xScaleIterationMatrix.getAnimatedValue();
}
});
xScaleIterationMatrix.start();
final ValueAnimator yScaleIterationMatrix = ValueAnimator.ofFloat(yScaleSrcToDst * initMatSrcVals[4], initMatSrcVals[4]);
yScaleIterationMatrix.setDuration(animDurationCommon);
yScaleIterationMatrix.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
resultYScale = (float)yScaleIterationMatrix.getAnimatedValue();
mat.setScale(resultXScale, resultYScale);
source.interfaceImageView().setImageMatrix(mat);
}
});
yScaleIterationMatrix.start();
I want to do a translate animation using this following
public static void move(TextView view){
ValueAnimator va = ValueAnimator.ofFloat(0f, 3f);
int mDuration = 3000; //in millis
va.setDuration(mDuration);
va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
public void onAnimationUpdate(ValueAnimator animation) {
}
});
va.setRepeatCount(5);
va.start();
}
But I don't know how to use onAnimationUpdate method.
Can anyone help please?
If you really, really, really want to use ValueAnimator for animating translation of the View you can do it this way (finishing your example, assuming you meant translationX.
Bare in mind that you're animating translation from 0px to 3px, so you probably won't see much difference.
fun move(view: TextView) {
val va = ValueAnimator.ofFloat(0f, 3f)
va.duration = 3000 //in millis
va.addUpdateListener { animation -> view.translationX = animation.animatedValue as Float }
va.repeatCount = 5
va.start()
}
ValueAnimator is a great tool for making animations. Usually we have three steps:
Step 1- Create your ValueAnimator class by
ValueAnimator animator = ValueAnimator.ofFloat(start value, end value);
Step 2- Adding one update listener and overriding at least onAnimationUpdate() function
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
float value = (float) animator.getAnimatedValue();
//this value should be used to update properties of views.
//just don't forget to run invalidate function of your views
// to redraw them.
}
});
Step 3-
animator.start();
In android 5.0 i am trying to work with circular reveal animation
Problem
When i click on button to start reveal animation, on first click animation doesn't start
Second Click onwards it works normally
My Code
public class MainActivity extends ActionBarActivity {
Animator a;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final View cardType = findViewById(R.id.cardtype);
cardType.setVisibility(View.GONE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
a = ViewAnimationUtils.createCircularReveal(cardType,
cardType.getWidth(),
cardType.getHeight(),
0,
cardType.getHeight() * 2)
.setDuration(2500);
a.addListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationStart(Animator animation) {
super.onAnimationStart(animation);
cardType.setVisibility(View.VISIBLE);
}
});
a.addListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
cardType.setVisibility(View.GONE);
}
});
findViewById(R.id.icon_first_activity).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
a.start();
}
});
}
}}
I haven't tried your code, but I think you have a small ordering problem. I think you just need to set the cardType visible before you start the animation.
Edited to add:
... and you should be setting your button View.INVISIBLE, not View.GONE.
Here: This code works.
Edited once more to add:
Yes. Your problem is that you set the view GONE initially. That means it has 0 size. Then you use cardType.getHeight and cardType.getWidth as reveal coordinates. They are 0. You are going to want to set the view INVISIBLE, initially, and then use width/2 and height/2 as the center of the reveal.
Basically what others answers say, it's correct, but the problem is if you want visibility GONE (because your layout requires it GONE!) you have to set visibility INVISIBLE in the xml with height 0dp (and/or width 0dp as well) and programmatically set the correct LayoutParams even inside the click event it will work. For example my code:
...
expandButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//To not have empty scroll, the container is INVISIBLE with 0dp height.
//Otherwise the Reveal effect will not work at the first click.
//Here I set the parameters programmatically.
viewContainer.setLayoutParams(new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT));
if (viewContainer.getVisibility() == View.VISIBLE) {
expandButton.animate().rotation(0f).setDuration(duration).start();
Utils.unReveal(viewContainer, 0, 0);
} else {
expandButton.animate().rotation(180f).setDuration(duration).start();
Utils.reveal(viewContainer, viewContainer.getWidth(), 0);
}
}
});
...
#TargetApi(VERSION_CODES.LOLLIPOP)
public static void reveal(final View view, int cx, int cy) {
if (!hasLollipop()) {
view.setVisibility(View.VISIBLE);
return;
}
//Get the final radius for the clipping circle
int finalRadius = Math.max(view.getWidth(), view.getHeight());
//Create the animator for this view (the start radius is zero)
Animator animator =
ViewAnimationUtils.createCircularReveal(view, cx, cy, 0, finalRadius);
//Make the view VISIBLE and start the animation
view.setVisibility(View.VISIBLE);
animator.start();
}
#TargetApi(VERSION_CODES.LOLLIPOP)
public static void unReveal(final View view, int cx, int cy) {
if (!hasLollipop()) {
view.setVisibility(View.GONE);
return;
}
//Get the initial radius for the clipping circle
int initialRadius = view.getWidth();
//Create the animation (the final radius is zero)
Animator animator =
ViewAnimationUtils.createCircularReveal(view, cx, cy, initialRadius, 0);
//Make the view GONE when the animation is done
animator.addListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
view.setVisibility(View.GONE);
}
});
//Start the animation
animator.start();
}
If you set only GONE in the xml, the first time will never work because height/width/x/y/etc.. are 0. Also, if you just set INVISIBLE before the call to the animation it will not work as well, but if you start with visibility INVISIBLE it will initialize the layout params.
what i did is, Like i have two view with same height,As we now visibility gone returns 0 {height and width} than i am giving visible view height every time and its work for me.
The solution is don't get values directly into code
Either put the animation code on click and the values outside onclick
or get the values from other activity
By values i mean cardType.getWidth() and cardType.getHeight()
I am doing translate animation using object animator. After successful translation I was trying to retain the location of animation using setting up new layoutparams. But on animation end when I set existing layoutparams without changing any margin view retains its location and working perfectly.
private void IndicatorAnimation(View currentView){
final FrameLayout fl = (FrameLayout)findViewById(R.id.indicator);
RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) fl.getLayoutParams();
final int[] origin = new int[2];
fl.getLocationInWindow(origin);
final int[] destination = new int[2];
currentView.getLocationInWindow(destination);
ObjectAnimator oa = ObjectAnimator.ofFloat(fl, "translationX", origin[0], destination[0]);
oa.setDuration(300);
oa.start();
oa.addListener(new AnimatorListener() {
#Override
public void onAnimationStart(Animator animation) {
}
#Override
public void onAnimationRepeat(Animator animation) {
}
#Override
public void onAnimationEnd(Animator animation) {
RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) fl.getLayoutParams();
fl.setLayoutParams(lp);
}
#Override
public void onAnimationCancel(Animator animation) {
}
});
}
My Question is how it is possible? Because getLayoutparams at animation end gives me same parameters which were there before animation starts.
There are actual properties that belong to the [View.TransformationInfo][1] class which include mTranslationX and mTranslationY. This is basically how the View keeps track of this information, not by updating the LayoutParams.
The TransformationInfo class is not publicly available, but (on API 14) you are able to use the View's properties of X, Y, etc. to get this information. onAnimationEnd you could reset those values and set the LayoutParams margins to accommodate (this of course depends on your layout how to be accomplish this).