jjzjj

android - Material 设计 : Add circular reveal animation while switching between toolbars

coder 2023-12-29 原文

我正在阅读 Material 设计指南 (https://material.io/guidelines/patterns/selection.html) 中的“选择”部分,我想在我的应用程序中添加一个效果是在应用程序栏和 ActionMode 之间切换时的圆形显示动画?另一个工具栏?

这是指南中的示例: https://storage.googleapis.com/material-design/publish/material_v_10/assets/0Bwp7jtSjaW36RGF3eUFsRkdqU1U/patterns_selection_item_controlling_desktop_click.webm

我没有找到关于如何做到这一点的任何解释。 我什至不知道他们是否使用 ActionMode 或其他东西......

有没有人可以给我好的指导?

编辑: 最小SDK 21

编辑 2: 查看状态栏,它也会自行更改...

谢谢 弗朗索瓦

最佳答案

好吧,我终于找到了解决方案。

这不是一个很好的...但我没有其他想法可以使用其他东西,所以如果您有其他建议可以分享...欢迎您!

下面是最终结果和代码:

GitHub 上的示例项目

https://github.com/fbourlieux/android-material-circular_reveal_animation

目标与想法

使用“平滑”的圆形显示动画从工具栏切换到另一个工具栏。该动画需要更新应用栏和状态栏。

为此,首先我们需要使用 android:fitsSystemWindows=false 强制 Activity 在状态栏下显示它的内容。主布局容器上的属性和 <item name="android:windowTranslucentStatus">true</item>在应用程序主题中。基于此,我们不仅会创建一个 Toolbar还有一个将显示在状态栏下的 View ,只是为了在动画期间绘制一个漂亮的背景。这是我的示例中我不喜欢的一点,但我没有找到任何其他解决方案。

让我们看看代码

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:windowTranslucentStatus属性(property)。

app_bar_main.xml

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

创建 2 个重叠的布局,其中包含一个用于绘制状态栏的 View 和一个用于绘制工具栏的 View 。默认情况下,要设置动画的布局设置为不可见。

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 触发我的第二个工具栏组(状态 View + 工具栏)的动画方法。第一个参数是要设置动画的 View ,然后是动画的起始坐标和半径。

在 onClick 方法中,我还启动了一个 reverse当我点击箭头或第二次点击 ToggleButton 时的动画。

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

希望我的解决方案可以帮助别人。

弗朗索瓦

有用的链接:

关于android - Material 设计 : Add circular reveal animation while switching between toolbars,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41933605/

有关android - Material 设计 : Add circular reveal animation while switching between toolbars的更多相关文章

  1. ruby-on-rails - Rails - 子类化模型的设计模式是什么? - 2

    我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co

  2. ruby-on-rails - 使用 rails 4 设计而不更新用户 - 2

    我将应用程序升级到Rails4,一切正常。我可以登录并转到我的编辑页面。也更新了观点。使用标准View时,用户会更新。但是当我添加例如字段:name时,它​​不会在表单中更新。使用devise3.1.1和gem'protected_attributes'我需要在设备或数据库上运行某种更新命令吗?我也搜索过这个地方,找到了许多不同的解决方案,但没有一个会更新我的用户字段。我没有添加任何自定义字段。 最佳答案 如果您想允许额外的参数,您可以在ApplicationController中使用beforefilter,因为Rails4将参数

  3. LC滤波器设计学习笔记(一)滤波电路入门 - 2

    目录前言滤波电路科普主要分类实际情况单位的概念常用评价参数函数型滤波器简单分析滤波电路构成低通滤波器RC低通滤波器RL低通滤波器高通滤波器RC高通滤波器RL高通滤波器部分摘自《LC滤波器设计与制作》,侵权删。前言最近需要学习放大电路和滤波电路,但是由于只在之前做音乐频谱分析仪的时候简单了解过一点点运放,所以也是相当从零开始学习了。滤波电路科普主要分类滤波器:主要是从不同频率的成分中提取出特定频率的信号。有源滤波器:由RC元件与运算放大器组成的滤波器。可滤除某一次或多次谐波,最普通易于采用的无源滤波器结构是将电感与电容串联,可对主要次谐波(3、5、7)构成低阻抗旁路。无源滤波器:无源滤波器,又称

  4. 计算机毕业设计ssm+vue基本微信小程序的小学生兴趣延时班预约小程序 - 2

    项目介绍随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱小学生兴趣延时班预约小程序的设计与开发被用户普遍使用,为方便用户能够可以随时进行小学生兴趣延时班预约小程序的设计与开发的数据信息管理,特开发了小程序的设计与开发的管理系统。小学生兴趣延时班预约小程序的设计与开发的开发利用现有的成熟技术参考,以源代码为模板,分析功能调整与小学生兴趣延时班预约小程序的设计与开发的实际需求相结合,讨论了小学生兴趣延时班预约小程序的设计与开发的使用。开发环境开发说明:前端使用微信微信小程序开发工具:后端使用ssm:VU

  5. 安卓apk修改(Android反编译apk) - 2

    最近因为项目需要,需要将Android手机系统自带的某个系统软件反编译并更改里面某个资源,并重新打包,签名生成新的自定义的apk,下面我来介绍一下我的实现过程。APK修改,分为以下几步:反编译解包,修改,重打包,修改签名等步骤。安卓apk修改准备工作1.系统配置好JavaJDK环境变量2.需要root权限的手机(针对系统自带apk,其他软件免root)3.Auto-Sign签名工具4.apktool工具安卓apk修改开始反编译本文拿Android系统里面的Settings.apk做demo,具体如何将apk获取出来在此就不过多介绍了,直接进入主题:按键win+R输入cmd,打开命令窗口,并将路

  6. ruby-on-rails - 设计注册确认 - 2

    我在我的项目中有一个用户和一个管理员角色。我使用Devise创建了身份验证。在我的管理员角色中,我没有任何确认。在我的用户模型中,我有以下内容:devise:database_authenticatable,:confirmable,:recoverable,:rememberable,:trackable,:validatable,:timeoutable,:registerable#Setupaccessible(orprotected)attributesforyourmodelattr_accessible:email,:username,:prename,:surname,:

  7. ruby-on-rails - 设计通过 reset_password_token 获取用户 - 2

    我正在尝试创建密码规则来设计可恢复的密码更改。我通过passwords_controller.rb做了一个父类(superclass),但我需要在应用规则之前检查用户角色,但我所拥有的只是reset_password_token。 最佳答案 假设您的模型是用户:User.with_reset_password_token(your_token_here)Source 关于ruby-on-rails-设计通过reset_password_token获取用户,我们在StackOverflow

  8. ruby-on-rails - Rails 5,公寓和设计 : sign in with subdomains are not working - 2

    我已经使用Apartment设置了一个Rails5应用程序(1.2.0)和Devise(4.2.0)。由于某些DDNS问题,应用只能在app.myapp.com下访问(请注意子域app)。myapp.com重定向到app.myapp.com。我的用例是每个注册该应用的用户(租户)都应该通过他们的子域(例如tenant.myapp.com)访问他们的特定数据。用户不应限定在其子域内。基本上应该可以从任何子域登录。重定向到租户的正确子域由ApplicationController处理。根据Devise标准,登录页面位于app.myapp.com/users/sign_in。这就是问题开始的

  9. ruby-on-rails - 设计中的 ArgumentError::RegistrationsController#new 错误的参数数量(2 代表 0..1) - 2

    我在关注RyanbatesRailsCast的devise和omniauth(第235集-devise-and-omniauth-revised)。当我尝试使用Twitter登录时,标题中不断出现错误。defself.new_with_session(params,session)ifsession["devise.user_attributes"]new(session["devise.user_attributes"],without_protection:true)do|user|user.attributes=paramsuser.valid?end完整跟踪:C:/Ruby20

  10. ruby-on-rails - 使用用户或管理员模型和 Basecamp 样式子域设计登录 - 2

    我为Devise用户和管理员提供了不同的模型。我也在使用Basecamp风格的子域。除了我需要能够以用户或管理员身份进行身份验证的一些Controller和操作外,一切都运行良好。目前我有authenticate_user!在我的application_controller.rb中设置,对于那些只有管理员才能访问的Controller和操作,我使用skip_before_filter跳过它。不幸的是,我不能简单地指定每个Controller的身份验证要求,因为我仍然需要一些Controller和操作才能被用户或管理员访问。我尝试了一些方法都无济于事。看来,如果我移动authentica

随机推荐