android - componentes - material design files
Circular revela transiciĆ³n para nueva actividad (5)
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 circularmente una actividad completa, sin elementos compartidos?
Específicamente, me gustaría que mi searchActivity se revele circularmente desde el botón de acción de búsqueda en la barra de herramientas.
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))
Después de buscar una solución durante 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 raíz es una vista que luego se puede revelar con
createCircularReveal()
.
Mi código se ve así:
Definición del tema en styles.xml
<style name="Theme.Transparent" parent="Theme.AppCompat.Light.NoActionBar">
<item name="android:windowIsTranslucent">true</item>
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:windowBackground">@android:color/transparent</item>
</style>
Definición de actividad en AndroidManifest.xml
<activity
android:name=".ui.CircularRevealActivity"
android:theme="@style/Theme.Transparent"
android:launchMode="singleTask"
/>
luego declare un diseño para mi actividad (elegí DrawerLayout, para poder tener un NavDrawer. Todos los diseños deberían funcionar aquí).
<android.support.v4.widget.DrawerLayout
android:id="@+id/drawer_layout"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<FrameLayout
android:id="@+id/root_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/honey_melon"
>
<!-- Insert your actual layout here -->
</FrameLayout>
</android.support.v4.widget.DrawerLayout>
Importante es 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 un
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, también debería funcionar sin esa línea, si su diseño no especifica las transiciones predeterminadas para las actividades.
Házmelo saber
R.anim.do_not_move:
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:fromYDelta="0"
android:toYDelta="0"
android:duration="@android:integer/config_mediumAnimTime"
/>
</set>
Para invertir la animación
CircularReveal
intercambie los argumentos
startRadius
y
endRadius
.
También deberá configurar un
AnimatorListener
y en el método de devolución de llamada
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 revelación circular al salir de la actividad, use la siguiente modificación para 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();
}
}
tienes que dibujar la vista circular, y luego debes crear una animación para ella.
Crear 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;
}
}
Crear 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();
}
}
Poner círculo en su diseño:
<com.package.Circle
android:id="@+id/circle"
android:layout_width="300dp"
android:layout_height="300dp" />
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);