Android 材质设计:在工具栏之间切换时添加圆形分隔缝动画

Android 材质设计:在工具栏之间切换时添加圆形分隔缝动画,android,material-design,android-animation,android-design-library,android-actionmode,Android,Material Design,Android Animation,Android Design Library,Android Actionmode,我正在阅读《材料设计指南》(material design guidelines())中的“选择”部分,我想在我的应用程序中添加的一个效果是在应用程序栏和动作模式之间切换时显示圆形动画?其他工具栏 以下是指南中的一个示例: 我没有找到任何关于如何做到这一点的解释。 我甚至不知道他们是否使用动作模式或其他方式 有没有人能给我一个好的方向 编辑: 明斯克21 编辑2: 看看状态栏,它本身也会改变 谢谢 François您可以使用自定义ripple库制作此类动画。可以找到它 然后,为了达到同样的效果

我正在阅读《材料设计指南》(material design guidelines())中的“选择”部分,我想在我的应用程序中添加的一个效果是在应用程序栏和动作模式之间切换时显示圆形动画?其他工具栏

以下是指南中的一个示例:

我没有找到任何关于如何做到这一点的解释。 我甚至不知道他们是否使用动作模式或其他方式

有没有人能给我一个好的方向

编辑: 明斯克21

编辑2: 看看状态栏,它本身也会改变

谢谢
François

您可以使用自定义ripple库制作此类动画。可以找到它

然后,为了达到同样的效果,您的工具栏将驻留在rippleview中,如自定义库的说明所示。您需要从涟漪效应开始的位置获得坐标,对于您的情况,此坐标是带有加号的浮动操作按钮的中心。然后,在项目选择上,运行此涟漪动画并淡入浮动动作按钮,您将获得相同的效果

在上面提到的库页面上可以看到一些类似的例子


希望有帮助

好的,我终于找到了解决办法

这不是一个很好的。。。但我没有其他想法使用其他东西,所以如果你有一些其他的提示分享。。。不客气

以下是最终结果和代码:

GitHub上的示例项目

目标和想法

public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener, View.OnClickListener {

    private final static int ANIMATION_DURATION = 400;
    private ToggleButton mActionButton;
    private View mRevealedToolBar;
    private ImageButton mArrowButton;
    private boolean mIsHidden = true;

    @Override
    protected void onCreate(final Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // main toolbar
        final Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        final DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
        final ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
        drawer.setDrawerListener(toggle);
        toggle.syncState();

        final NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
        navigationView.setNavigationItemSelectedListener(this);

        // trigger circular reveal animation
        mActionButton = (ToggleButton) findViewById(R.id.actionButton);
        mActionButton.setOnClickListener(this);

        // toolbar view to reveal. Inivisible by default
        mRevealedToolBar = findViewById(R.id.revealedToolBar);
        mRevealedToolBar.setVisibility(View.INVISIBLE);

        // button in revealed toolbar to dismiss it
        mArrowButton = (ImageButton) findViewById(R.id.toolbar_arrow);
        mArrowButton.setOnClickListener(this);
    }

    @Override
    public void onBackPressed() {
        final DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
        if (drawer.isDrawerOpen(GravityCompat.START)) {
            drawer.closeDrawer(GravityCompat.START);
        } else {
            super.onBackPressed();
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(final MenuItem item) {
        if (item.getItemId() == R.id.action_settings) {
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

    @SuppressWarnings("StatementWithEmptyBody")
    @Override
    public boolean onNavigationItemSelected(final MenuItem item) {
        DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
        drawer.closeDrawer(GravityCompat.START);
        return true;
    }



    @Override
    public void onClick(final View view) {

        if (view == mActionButton || view == mArrowButton) {


            // compute started X and Y co-ordinates for the animation + radius
            int x = mRevealedToolBar.getLeft();
            int y = mRevealedToolBar.getBottom();
            int startRadius = 0;
            int endRadius = Math.max(mRevealedToolBar.getWidth(), mRevealedToolBar.getHeight());
            int reverseStartRadius = endRadius;
            int reverseEndRadius = startRadius;



            if (mIsHidden) {

                // show secondary toolbar
                // performing circular reveal when icon will be tapped
                Animator animator = ViewAnimationUtils.createCircularReveal(mRevealedToolBar, x, y, startRadius, endRadius);
                animator.setInterpolator(new AccelerateDecelerateInterpolator());
                animator.setDuration(ANIMATION_DURATION);

                mRevealedToolBar.setVisibility(View.VISIBLE);
                animator.start();
                mIsHidden = false;


            } else {

                // dismiss secondary toolbar
                // performing circular reveal for reverse animation
                Animator animate = ViewAnimationUtils.createCircularReveal(mRevealedToolBar, x, y, reverseStartRadius, reverseEndRadius);
                animate.setInterpolator(new AccelerateDecelerateInterpolator());
                animate.setDuration(ANIMATION_DURATION);

                // to hide layout on animation end
                animate.addListener(new AnimatorListenerAdapter() {
                    @Override
                    public void onAnimationEnd(Animator animation) {
                        super.onAnimationEnd(animation);
                        mRevealedToolBar.setVisibility(View.INVISIBLE);
                        mIsHidden = true;
                    }
                });

                mRevealedToolBar.setVisibility(View.VISIBLE);
                animate.start();
            }
        }
    }
}
使用“平滑”圆形动画从工具栏切换到另一个工具栏。该动画需要更新应用程序栏和状态栏

为此,首先我们需要通过在主布局容器上使用
android:fitsystemwindows=false
属性和在应用程序主题中使用
true
来强制活动在状态栏下显示其内容。基于此,我们不仅将创建一个
工具栏
,还将创建一个显示在状态栏下的视图,以便在动画期间绘制一个漂亮的背景。这是我不喜欢的一点,但我没有找到任何其他解决方案

让我们看看代码

styles.xml

<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
    <!-- Customize your theme here. -->
    <item name="colorPrimary">@color/colorPrimary</item>
    <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
    <item name="colorAccent">@color/colorAccent</item>
    <item name="android:windowTranslucentStatus">true</item>
</style>
<android.support.design.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="false"
    tools:context="sample.test.fbo.circularrevealanimation.MainActivity">

    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/AppTheme.AppBarOverlay">

        <!-- used to force the two toolbars to display above each other -->
        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <!-- initial toolbar layout with the status bar 
            and the original toolbar. That layout need to have a 
            background to show the elevation even if it will never 
            be visible (because of inner component backgrounds) -->
            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:background="@color/colorPrimary"
                android:elevation="4dp"
                android:orientation="vertical">

                <!-- status bar background: height of 24dp 
                and initial color darker than the toolbar color -->
                <View
                    android:layout_width="match_parent"
                    android:layout_height="24dp"
                    android:background="@color/colorPrimaryDark" />

                <!-- main toolbar. A very basic one.-->
                <android.support.v7.widget.Toolbar
                    android:id="@+id/toolbar"
                    android:layout_width="match_parent"
                    android:layout_height="?attr/actionBarSize"
                    android:background="?attr/colorPrimary"
                    app:popupTheme="@style/AppTheme.PopupOverlay" />

            </LinearLayout>

            <!-- reveal section layout. Here is our second toolbar
            section which will be animated. It contains a view to
            fake the status bar background and the second toolbar
            to display. -->
            <LinearLayout
                android:id="@+id/revealedToolBar"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:background="@color/colorAccentDark"
                android:elevation="4dp"
                android:orientation="vertical"
                android:visibility="invisible">

                <!-- revealed status bar. Just to change it background. -->
                <View
                    android:id="@+id/revealBackgroundStatus"
                    android:layout_width="match_parent"
                    android:layout_height="24dp"
                    android:background="@color/colorAccentDark" />

                <!-- revealed toolbar. The second one with in our case 
                a simple button and text inside. -->
                <android.support.v7.widget.Toolbar
                    android:id="@+id/toolbar2"
                    android:layout_width="match_parent"
                    android:layout_height="?attr/actionBarSize"
                    android:background="@color/colorAccent"
                    app:popupTheme="@style/AppTheme.PopupOverlay">

                    <!-- a click on that button will trigger 
                         the animation close event -->
                    <ImageButton
                        android:id="@+id/toolbar_arrow"
                        android:layout_width="48dp"
                        android:layout_height="48dp"
                        android:background="@android:color/transparent"
                        android:src="@drawable/arrow_left" />

                    <TextView
                        android:layout_width="match_parent"
                        android:layout_height="match_parent"
                        android:layout_marginLeft="24dp"
                        android:fontFamily="sans-serif-regular"
                        android:gravity="center_vertical"
                        android:text="Foo Bar Baz"
                        android:textColor="@android:color/white"
                        android:textSize="18sp"
                        android:textStyle="bold"
                        tools:text="Foo Bar Baz" />
                </android.support.v7.widget.Toolbar>
            </LinearLayout>

        </RelativeLayout>
    </android.support.design.widget.AppBarLayout>

    <!-- content_main just contains a ToggleButton to trigger 
    the animation-->
    <include layout="@layout/content_main" />

</android.support.design.widget.CoordinatorLayout>
创建两个重叠布局,其中包含用于绘制状态栏的视图和用于绘制工具栏的视图。默认情况下,要设置动画的布局设置为不可见

MainActivity.java

public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener, View.OnClickListener {

    private final static int ANIMATION_DURATION = 400;
    private ToggleButton mActionButton;
    private View mRevealedToolBar;
    private ImageButton mArrowButton;
    private boolean mIsHidden = true;

    @Override
    protected void onCreate(final Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // main toolbar
        final Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        final DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
        final ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
        drawer.setDrawerListener(toggle);
        toggle.syncState();

        final NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
        navigationView.setNavigationItemSelectedListener(this);

        // trigger circular reveal animation
        mActionButton = (ToggleButton) findViewById(R.id.actionButton);
        mActionButton.setOnClickListener(this);

        // toolbar view to reveal. Inivisible by default
        mRevealedToolBar = findViewById(R.id.revealedToolBar);
        mRevealedToolBar.setVisibility(View.INVISIBLE);

        // button in revealed toolbar to dismiss it
        mArrowButton = (ImageButton) findViewById(R.id.toolbar_arrow);
        mArrowButton.setOnClickListener(this);
    }

    @Override
    public void onBackPressed() {
        final DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
        if (drawer.isDrawerOpen(GravityCompat.START)) {
            drawer.closeDrawer(GravityCompat.START);
        } else {
            super.onBackPressed();
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(final MenuItem item) {
        if (item.getItemId() == R.id.action_settings) {
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

    @SuppressWarnings("StatementWithEmptyBody")
    @Override
    public boolean onNavigationItemSelected(final MenuItem item) {
        DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
        drawer.closeDrawer(GravityCompat.START);
        return true;
    }



    @Override
    public void onClick(final View view) {

        if (view == mActionButton || view == mArrowButton) {


            // compute started X and Y co-ordinates for the animation + radius
            int x = mRevealedToolBar.getLeft();
            int y = mRevealedToolBar.getBottom();
            int startRadius = 0;
            int endRadius = Math.max(mRevealedToolBar.getWidth(), mRevealedToolBar.getHeight());
            int reverseStartRadius = endRadius;
            int reverseEndRadius = startRadius;



            if (mIsHidden) {

                // show secondary toolbar
                // performing circular reveal when icon will be tapped
                Animator animator = ViewAnimationUtils.createCircularReveal(mRevealedToolBar, x, y, startRadius, endRadius);
                animator.setInterpolator(new AccelerateDecelerateInterpolator());
                animator.setDuration(ANIMATION_DURATION);

                mRevealedToolBar.setVisibility(View.VISIBLE);
                animator.start();
                mIsHidden = false;


            } else {

                // dismiss secondary toolbar
                // performing circular reveal for reverse animation
                Animator animate = ViewAnimationUtils.createCircularReveal(mRevealedToolBar, x, y, reverseStartRadius, reverseEndRadius);
                animate.setInterpolator(new AccelerateDecelerateInterpolator());
                animate.setDuration(ANIMATION_DURATION);

                // to hide layout on animation end
                animate.addListener(new AnimatorListenerAdapter() {
                    @Override
                    public void onAnimationEnd(Animator animation) {
                        super.onAnimationEnd(animation);
                        mRevealedToolBar.setVisibility(View.INVISIBLE);
                        mIsHidden = true;
                    }
                });

                mRevealedToolBar.setVisibility(View.VISIBLE);
                animate.start();
            }
        }
    }
}
因此,在
MainActivity
中,在收听了我的
ToggleButton
的onclick事件后,我使用
ViewAnimationUtils.createCircularReveal
方法触发第二个工具栏组(状态视图+工具栏)的动画。第一个参数是要设置动画的视图,后跟动画的开始坐标和半径

在onClick方法中,当我点击箭头或第二次点击我的切换按钮时,我也会启动一个
反向
动画

最后,这是一个非常简单的解决方案,即使我们需要伪造状态栏背景

希望我的解决方案能帮助别人

弗朗索瓦

用户链接: