I want to create a custom animation for fragment transition. The animation is in form of a circle that increases his radius from a specific size until it is equal to window height. I can use ScaleAnimation, but then I'll lose drawable quality. Does someone have any ideas ho to do this. Thanks in advance.
Ok. I'll answer my own question :). To animate the fragment transition with my custom transition (a colored circle that increases his size) I have done the following:
- create a custom view that draws a circle and add the possibility to increase his radius;
- place this view in my FragmentActivity xml layout;
- when the target button is clicked and fragment should change I've called ValueAnimator to change circle radius;
Final code will look something like this:
public class MainScreenActivity extends AppCompatActivity {
private Button targetButton;
private CircleView circleView;
public void targetButtonClick(View view) {
AnimationHandler handler = new AnimationHandler(view.getX(),view.getY());
handler.animate();
}
private class AnimationHandler implements Animator.AnimatorListener, ValueAnimator.AnimatorUpdateListener {
private static final int TRANSITION_ANIM_DURATION = 500;
private final float centerX;
private final float centerY;
private ValueAnimator valueAnimator;
public AnimationHandler(float x, float y) {
this.centerX = x;
this.centerY = y;
init();
}
private void init() {
Point point = new Point();
getWindowManager().getDefaultDisplay().getSize(point);
valueAnimator = ValueAnimator.ofFloat(0, point.y);
valueAnimator.setDuration(TRANSITION_ANIM_DURATION);
valueAnimator.addUpdateListener(this);
valueAnimator.addListener(this);
}
#Override
public void onAnimationStart(Animator animation) {
}
#Override
public void onAnimationEnd(Animator animation) {
}
#Override
public void onAnimationCancel(Animator animation) {
}
#Override
public void onAnimationRepeat(Animator animation) {
}
#Override
public void onAnimationUpdate(ValueAnimator animation) {
circleView.setRadius((float) animation.getAnimatedValue());
}
public void animate() {
circleView.setXCenter(centerX);
circleView.setYCenter(centerY);
int color = getResources().getColor(android.R.color.dark_red);
circleView.setStrokeWidth(getResources().getDimensionPixelSize(R.dimen.trans_circle_width));
valueAnimator.start();
}
}
}
Related
I have extended the Animation class to show circle being drawn as animation here is my Animation class
public class CircleAnimation extends Animation {
private Circle circle;
private float oldAngle;
private float newAngle;
public CircleAnimation(Circle circle, int newAngle) {
this.oldAngle = circle.getAngle();
this.newAngle = newAngle;
this.circle = circle;
setInterpolator(new ReverseInterpolator());
}
#Override
protected void applyTransformation(float interpolatedTime, Transformation transformation) {
float angle = oldAngle + ((newAngle - oldAngle) * interpolatedTime);
circle.setAngle(angle);
circle.requestLayout();
}
}
Everything works as expected and now I wanted to reverse the animation i.e start from a full circle to half circle and finally to empty.
The easiest way I found was to use a custom Interpolator as described here in the stackoverflow answer and I implemented the same.
public class ReverseInterpolator implements Interpolator {
#Override
public float getInterpolation(float paramFloat) {
return Math.abs(paramFloat - 1f);
}
}
I have two button to start and stop the animation
public void stop(View v) {
if (circle != null) {
circle.getAnimation().cancel();
}
}
public void start(View v) {
CircleAnimation animation = new CircleAnimation(circle, 360);
animation.setDuration(10000);
circle.startAnimation(animation);
}
The problem is if I use the setInterpolator(new ReverseInterpolator()); in my animation to reverse the circle being drawn the start and stop works only once and if I try starting it again the animation does not work but if I remove the ReverseInterpolator from my animation to draw circle the start and stop works any number of time, Can someone explain why is this happening?
I want to achieve the below animation in android I have tried scenes but scenes do not work with text as per docs it is confirmed :
"If you try to resize a TextView with an animation, the text will pop to a new location before the object has completely resized. To avoid this problem, do not animate the resizing of views that contain text."
Please any solution , the enlarged layout text can contain images too.
animation video
this thing worked some how ,but the animation is little jittery,I guess layout height final value is attained first and layoutWidth later, have to fix this. this is my enlarge/reduce animation :
public class EnlargeAnimation extends Animation {
private final int diffHeight;
private final int diffWidth;
private final int initialHeight;
private final int initialWidth;
private final View targetView;
public EnlargeAnimation(View targetView, float targetHeight, float targetWidth) {
this.targetView = targetView;
this.initialHeight = targetView.getMeasuredHeight();
this.initialWidth = targetView.getMeasuredWidth();
this.diffHeight = (int) (targetHeight-initialHeight);
this.diffWidth = (int) (targetWidth-initialWidth);
}
#Override
public boolean willChangeBounds() {
return true;
}
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
float newHeight = initialHeight + diffHeight * interpolatedTime;
float newWidth = initialWidth + diffWidth * interpolatedTime;
targetView.getLayoutParams().height = (int) newHeight;
targetView.getLayoutParams().width = (int) newWidth;
targetView.requestLayout();
}
}
this is when enlarge animation is called :
I am using viewpager so i have to make padding negative to enlarge the card size :
ValueAnimator paddingAnimator = ValueAnimator.ofInt(20, -10).setDuration(400);
paddingAnimator.setInterpolator(new LinearInterpolator());
paddingAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
int padding = (int) animation.getAnimatedValue();
view.setPadding(padding,
(int) DeviceUtils.convertDpToPx(50, v.getContext()), padding,
(int) DeviceUtils.convertDpToPx(50, v.getContext()));
view.requestLayout();
}
});
viewPagerItemSizeListener.onEnlarged();
EnlargeAnimation
enlargeAnimation =
new EnlargeAnimation(cardView, screenHeight, screenWidth);
enlargeAnimation.setDuration(400);
enlargeAnimation.setAnimationListener(new Animation.AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
paddingAnimator.start();
seeExampleText.setVisibility(View.INVISIBLE);
}
#Override
public void onAnimationEnd(Animation animation) {
stage.setVisibility(View.GONE);
cardEnlargedWidth = cardView.getLayoutParams().width;
cardEnlargedHeight = cardView.getLayoutParams().height;
crossContianer.setVisibility(View.VISIBLE);
detailTextContianer.setVisibility(View.VISIBLE);
}
#Override
public void onAnimationRepeat(Animation animation) {
}
});
view.startAnimation(enlargeAnimation);
I guess no one is reading it, but if something is confusing about variables let me know i will edit the answer.
public class MainActivity extends Activity {
ValueAnimator animator;
ImageButton testImageButton;
float rad=1;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
testImageButton = findViewById(R.id.ib_test);
testImageButton.setOutlineProvider(outlineProvider);
testImageButton.setClipToOutline(true);
animator = (ValueAnimator)
AnimatorInflater.loadAnimator(this,R.animator.radius);
animator.addUpdateListener(mListener);
}
public void clickedAnimator(View v){
animator.start();
}
public void testMe(View v){
l("testMe");
}
ValueAnimator.AnimatorUpdateListener mListener = new
ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
float i= (float) valueAnimator.getAnimatedValue();
rad = i;
testImageButton.setOutlineProvider(outlineProvider);
}
};
ViewOutlineProvider outlineProvider = new ViewOutlineProvider() {
#Override
public void getOutline(View view, Outline outline) {
outline.setRoundRect(0,0,view.getWidth(),view.getHeight(),
rad*((float)view.getWidth())/2);
}
};
public void l(String s){
Log.v(getClass().getSimpleName(),s);
}
}
I am trying to animate the outline of an ImageView from Circle to Rectangle by constantly updating the ViewOutlineProvider that is set to the imageview.
Everything works fine but when the animation ends, the Image in the imageView Blinks once.
The value of rad changes from 1 to 0 in 500ms.
Hmmm, this line might help you: android:fillAfter="true"
Place this in your animation XML file inside the set attribute like:
<set [...]
android:fillAfter="true">
This helped me on a number of occasions when the animation was bugging at the end. Might work for you, might not, it's worth trying.
im trying to rotate an imageview in certain degrees and at the same time expand this imageview's height !
my code so far is :
public class TongueHandler {
ImageView tongue;
RelativeLayout.LayoutParams tongue_params;
ObjectAnimator rotate_tongue;
ScaleAnimation scale_tongue;
AnimationSet as;
ImageView frog;
Button btn;
public TongueHandler(Button btn,ImageView frog,ImageView tongue){
this.btn=btn;
this.frog=frog;
this.tongue=tongue;
}
public void Initialize()
{
tongue.setBackgroundColor(Color.RED);
tongue_params=new RelativeLayout.LayoutParams(10,100);
tongue_params.leftMargin=frog.getLeft()+frog.getWidth()/2;
tongue_params.topMargin=frog.getTop()+frog.getHeight()/2;
tongue.setLayoutParams(tongue_params);
Animate();
}
public void Animate()
{
as=new AnimationSet(true);
rotate_tongue=ObjectAnimator.ofFloat(tongue, "rotationX", 90.0f, 90+CalcAngle(frog,btn));
rotate_tongue.setDuration(1);
rotate_tongue.start();
System.out.println("This is Angle: "+CalcAngle(frog,btn));
/* scale_tongue=new ScaleAnimation(1,1,30,btn.getY(),1,2);
scale_tongue.setDuration(500); */
// as.addAnimation(scale_tongue);
tongue.startAnimation(as);
as.setAnimationListener(new Animation.AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
}
#Override
public void onAnimationEnd(Animation animation) {
/* as=new AnimationSet(false);
rotate_tongue=new RotateAnimation(0,CalcAngle(frog,btn),frog.getWidth()/2,frog.getHeight()/2);
rotate_tongue.setDuration(1);
scale_tongue=new ScaleAnimation(30,btn.getY(),2,2,15,0);
scale_tongue.setDuration(200);
as.addAnimation(rotate_tongue);
as.addAnimation(scale_tongue);
tongue.startAnimation(as);*/
}
#Override
public void onAnimationRepeat(Animation animation) {
}
});
}
public float CalcAngle(View frog,View fly)
{
float angle = (float) Math.toDegrees(Math.atan2(frog.getY() - DataBase.FlyY, frog.getX() - DataBase.FlyX));
if(angle < 0){
angle += 360;
}
/* if (frog.getX()>=DataBase.FlyX)
{
angle=-90f-angle;
}
else
{
angle=-90f+angle;
}*/
return angle;
}
}
the problem is that doesnt work at all as the image rotates but in wrong degrees (also i would like to do this instantly not even with 1ms delay) and generally the result is not sutisfying
any help is really appreciated
thank you ;)
I would like to apply the same alphaanimation for button A, B and C, and therefore implemented the following codes:
public void onCreate(Bundle savedInstanceState)
{
animation = new MutableAlphaAnimation();
animation.setAnimationListener(this);
btn_A.setOnClickListener(new OnClickListener()
{
public void onClick(View v)
{
animation.setResetBlocked(true);
btn_A.setAnimation(animation);
animation.setResetBlocked(false);
animation.start(0.0f, 0.5f, FADE_IN_DURATION);
btn_A.invalidate();
}
});
}
public class MutableAlphaAnimation extends Animation
{
private float mFromAlpha;
private float mToAlpha;
private boolean resetBlocked;
public MutableAlphaAnimation()
{
}
public void start(float fromAlpha, float toAlpha, long duration)
{
mFromAlpha = fromAlpha;
mToAlpha = toAlpha;
setDuration(duration);
setStartTime(START_ON_FIRST_FRAME);
}
public void setResetBlocked(boolean resetBlocked)
{
this.resetBlocked = resetBlocked;
}
#Override
public void reset()
{
if (! resetBlocked) super.reset();
}
#Override
protected void applyTransformation(float interpolatedTime, Transformation t)
{
final float alpha = mFromAlpha;
t.setAlpha(alpha + ((mToAlpha - alpha) * interpolatedTime));
}
#Override
public boolean willChangeTransformationMatrix()
{
return false;
}
#Override
public boolean willChangeBounds()
{
return false;
}
}
Question:
btn_A, B and C are implemented with the same setOnClickListener. However, when they are pressed, nothing happen. Why? How can the above be modified?
Thanks!
You aren't applying your transformation to btn_home. Yes you set the it to the animation, but your animation class never touches btn_home. Your t.setAlpha... should really be btn_home.setAlpha..., so pass in the view using the constructor and then you can use it with any view in the future.