新活动的圆形揭示转场效果

76
根据https://developer.android.com/training/material/animations.html

ViewAnimationUtils.createCircularReveal()方法使您能够动画显示或隐藏视图的剪切圆圈。

要使用此效果显示先前不可见的视图:

// 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();

这是为了呈现一种视图。我该如何使用它来循环地揭示整个操作,而没有任何共享元素?

具体来说,我想让我的searchActivity从工具栏中的搜索操作按钮循环显示。


不,我不再积极寻找它了。 - Ishaan Garg
5个回答

93

在经过半天的寻找解决方法而没有结果后,我自己开始实现。我使用了一个具有相应根布局的透明 Activity。根布局是一个视图,可以使用createCircularReveal()来显示。

我的代码看起来像这样:

在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>

AndroidManifest.xml中的Activity定义

<activity
        android:name=".ui.CircularRevealActivity"
        android:theme="@style/Theme.Transparent"
        android:launchMode="singleTask"
        />

然后我为我的活动声明了一个布局(我选择了DrawerLayout,这样我就可以有一个NavDrawer。这里应该适用于任何布局。)

<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>

重要的是带有id为root_layout的FrameLayout。该视图将在活动中显示。

最后,我实现了CircularRevealActivity并重写了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);
                    } 
                }
            });
        }
    }
}

circularRevealActivity() 放入 OnGlobalLayoutListener 中非常重要,因为动画需要绘制视图。

circularRevealActivity() 看起来像是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();
}

编辑 1

添加了对R.anim.do_not_move的定义。但是,如果您的设计未指定活动的默认过渡效果,即使没有这行代码也应该能正常工作。让我知道。

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>

对我来说运行得很好。我只是觉得这种循环揭示应该可以在不将两个活动合并为一个的情况下实现...如果有问题请让我知道。 - Stefan Medack
1
运行良好。我改变了cx和cy的值以改变原点。 - ch3tanz
R.anim.do_not_move 的定义是什么? - Jaka Dirnbek
@JakaDirnbek 添加 R.anim.do_not_move 来覆盖默认动画。但是,如果您没有定义默认的活动动画,那么它也应该可以正常工作。如果需要,我已经将 R.anim.do_not_move 的代码添加到列表中。 - Stefan Medack
1
@StefanMedack 在 onGlobalLayout() 中应添加 getViewTreeObserver().removeGlobalOnLayoutListener(this);。这可以避免多次调用,从而导致双重揭示。在这里有效。 - WindRider
显示剩余18条评论

13

如果你想在离开活动时反转循环揭示效果,请使用下面的代码修改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();
    }
}

9

1
仅适用于API 23及以上版本,否则将默认为标准动画。 - Bob bobbington
1
有没有办法在返回按钮上使用它,以返回先前的活动? - JavierSegoviaCordoba

8

要反转CircularReveal动画,请交换startRadiusendRadius参数。此外,您需要设置一个AnimatorListener,在onAnimationEnd()回调方法中可以调用finishAfterTransition()。这是为了当您按下向上导航或单击返回按钮时。


你应该详细说明如何实现反向动画。 - Pei

2
你需要绘制圆形视图,然后创建一个动画效果。
创建圆形视图:
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;
    }
}

创建动画类以设置新角度:

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();
    }
}

将圆形加入您的布局中:
<com.package.Circle
    android:id="@+id/circle"
    android:layout_width="300dp"
    android:layout_height="300dp" />

最后开始动画:

Circle circle = (Circle) findViewById(R.id.circle);

CircleAngleAnimation animation = new CircleAngleAnimation(circle, 240);
animation.setDuration(1000);
circle.startAnimation(animation);

这不是用户要求的内容,但无论如何,这对我帮助很大。 - AMAN SINGH

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接