Circular revela transición para nueva actividad

Según https://developer.android.com/training/material/animations.html

El método ViewAnimationUtils.createCircularReveal() permite animar un círculo de recorte para revelar u ocultar una vista.

Para revelar una vista previamente invisible usando este efecto:

 // previously invisible view View myView = findViewById(R.id.my_view); // get the center for the clipping circle int cx = (myView.getLeft() + myView.getRight()) / 2; int cy = (myView.getTop() + myView.getBottom()) / 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); // make the view visible and start the animation myView.setVisibility(View.VISIBLE); anim.start(); 

Esto está destinado a revelar una vista. ¿Cómo puedo usar esto para revelar de forma circular una actividad completa, sin elementos compartidos?

Específicamente, me gustaría que mi searchActivity revele circularmente desde el botón de acción de búsqueda en la barra de herramientas.

Después de buscar una solución por medio día sin resultado, se me ocurrió una implementación propia. Estoy usando una actividad transparente con un diseño de raíz coincidente. El diseño de la raíz es una vista que luego puede revelarse con createCircularReveal() .

Mi código se ve así:

Definición de tema en styles.xml

  

Definición de actividad en AndroidManifest.xml

  

luego, declaré un diseño para mi actividad (elegí DrawerLayout para poder tener un NavDrawer. Todos los diseños deberían funcionar aquí).

      

Importante es el FrameLayout con el ID root_layout . Esta vista se revelará en la actividad.

Finalmente implementé CircularRevealActivity y onCreate() :

 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); overridePendingTransition(R.anim.do_not_move, R.anim.do_not_move); setContentView(R.layout.activity_reveal_circular); if (savedInstanceState == null) { rootLayout.setVisibility(View.INVISIBLE); ViewTreeObserver viewTreeObserver = rootLayout.getViewTreeObserver(); if (viewTreeObserver.isAlive()) { viewTreeObserver.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { circularRevealActivity(); if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) { rootLayout.getViewTreeObserver().removeGlobalOnLayoutListener(this); } else { rootLayout.getViewTreeObserver().removeOnGlobalLayoutListener(this); } } }); } } } 

Era importante poner circularRevealActivity() en OnGlobalLayoutListener , porque la vista debe dibujarse para la animación.

circularRevealActivity() parece a la propuesta de Ishaan:

 private void circularRevealActivity() { int cx = rootLayout.getWidth() / 2; int cy = rootLayout.getHeight() / 2; float finalRadius = Math.max(rootLayout.getWidth(), rootLayout.getHeight()); // create the animator for this view (the start radius is zero) Animator circularReveal = ViewAnimationUtils.createCircularReveal(rootLayout, cx, cy, 0, finalRadius); circularReveal.setDuration(1000); // make the view visible and start the animation rootLayout.setVisibility(View.VISIBLE); circularReveal.start(); } 

Editar 1

Se agregó la definición de R.anim.do_not_move . Sin embargo, debería funcionar sin esa línea también, si su diseño no especifica transiciones predeterminadas para actividades. Házmelo saber

R.anim.do_not_move:

    

Para invertir la animación CircularReveal cambie los argumentos startRadius y endRadius . También necesitará configurar un AnimatorListener y en el método de callback onAnimationEnd() es donde puede llamar a finishAfterTransition() . Esto es para cuando presiona la up navigation o hace clic en el back button .

Si desea revertir la circular revela al abandonar la actividad, use la siguiente modificación en onBackPressed ().

 @Override public void onBackPressed() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { int cx = rootLayout.getWidth(); int cy = 0; float finalRadius = Math.max(rootLayout.getWidth(), rootLayout.getHeight()); Animator circularReveal = ViewAnimationUtils.createCircularReveal(rootLayout, cx, cy, finalRadius, 0); circularReveal.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animator) { } @Override public void onAnimationEnd(Animator animator) { rootLayout.setVisibility(View.INVISIBLE); finish(); } @Override public void onAnimationCancel(Animator animator) { } @Override public void onAnimationRepeat(Animator animator) { } }); circularReveal.setDuration(400); circularReveal.start(); }else{ super.onBackPressed(); } } 

Creo que puedes usar ActivityOptionsCompat.makeClipRevealAnimation .

[ https://developer.android.com/reference/android/support/v4/app/ActivityOptionsCompat.html#makeClipRevealAnimation(android.view.View , int, int, int, int)] ( https://developer.android .com / reference / android / support / v4 / app / ActivityOptionsCompat.html # makeClipRevealAnimation (android.view.View , int, int, int, int))

Debe dibujar la vista de círculo y, a continuación, debe crear una animación.

Creando la vista circular

 public class Circle extends View { private static final int START_ANGLE_POINT = 90; private final Paint paint; private final RectF rect; private float angle; public Circle(Context context, AttributeSet attrs) { super(context, attrs); final int strokeWidth = 40; paint = new Paint(); paint.setAntiAlias(true); paint.setStyle(Paint.Style.STROKE); paint.setStrokeWidth(strokeWidth); //Circle color paint.setColor(Color.RED); //size 200x200 example rect = new RectF(strokeWidth, strokeWidth, 200 + strokeWidth, 200 + strokeWidth); //Initial Angle (optional, it can be zero) angle = 120; } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.drawArc(rect, START_ANGLE_POINT, angle, false, paint); } public float getAngle() { return angle; } public void setAngle(float angle) { this.angle = angle; } } 

Creando la clase de animación para establecer el nuevo ángulo:

 public class CircleAngleAnimation extends Animation { private Circle circle; private float oldAngle; private float newAngle; public CircleAngleAnimation(Circle circle, int newAngle) { this.oldAngle = circle.getAngle(); this.newAngle = newAngle; this.circle = circle; } @Override protected void applyTransformation(float interpolatedTime, Transformation transformation) { float angle = oldAngle + ((newAngle - oldAngle) * interpolatedTime); circle.setAngle(angle); circle.requestLayout(); } } 

Ponga el círculo en su diseño:

  

Y finalmente comenzando la animación:

 Circle circle = (Circle) findViewById(R.id.circle); CircleAngleAnimation animation = new CircleAngleAnimation(circle, 240); animation.setDuration(1000); circle.startAnimation(animation);