展开/折叠棒棒糖工具栏动画(电报应用程序)

edo*_*oni 72 android android-animation android-toolbar android-6.0-marshmallow android-7.0-nougat

我正在试图弄清楚工具栏的展开/折叠动画是如何完成的.如果您查看Telegram应用程序设置,您将看到列表视图和工具栏.向下滚动时,工具栏会折叠,向上滚动时会展开.还有配置文件pic和FAB的动画.有人有任何线索吗?你认为他们建立了所有的动画吗?也许我错过了新API或支持库中的一些东西.

我注意到Google日历应用程序中的相同行为,当您打开Spinner时(我不认为它是一个微调器,但它看起来像):工具栏展开,当您向上滚动时,它会崩溃.

只是为了澄清:我不需要QuickReturn方法.我知道可能Telegram应用程序正在使用类似的东西.我需要的确切方法是Google日历应用效果.我试过了

android:animateLayoutChanges="true"
Run Code Online (Sandbox Code Playgroud)

并且expand方法运行良好.但显然,如果我向上滚动ListView,工具栏不会崩溃.

我也想过添加一个,GestureListener但我想知道是否有任何API或更简单的方法来实现这一点.

如果没有,我想我会跟着GestureListener.希望能有一个平滑的动画效果.

谢谢!

Mat*_*ree 109

编辑:

自Android Design支持库发布以来,有一个更简单的解决方案.检查joaquin的答案

-

这是我如何做到的,可能有很多其他的解决方案,但这个对我有用.

  1. 首先,你必须使用Toolbar透明背景.扩张和崩溃Toolbar实际上是一个虚假的,在透明的情况下Toolbar.(你可以在下面的第一个截图中看到 - 带有边距的截图 - 这也是他们在电报中的表现).

    我们只保留了实际ToolbarNavigationIcon和溢出MenuItem.

    1.透明工具栏 -  2.扩展标题 -  3.折叠标题

  2. 第二个屏幕截图中的红色矩形中的所有内容(即假的ToolbarFloatingActionButton)实际上您添加到设置ListView(或ScrollView)的标题.

    因此,您必须在单独的文件中为此标头创建一个布局,如下所示:

     <!-- The headerView layout. Includes the fake Toolbar & the FloatingActionButton -->
    
     <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
    
        <RelativeLayout
            android:id="@+id/header_container"
            android:layout_width="match_parent"
            android:layout_height="@dimen/header_height"
            android:layout_marginBottom="3dp"
            android:background="@android:color/holo_blue_dark">
    
            <RelativeLayout
                android:id="@+id/header_infos_container"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_alignParentBottom="true"
                android:padding="16dp">
    
                <ImageView
                    android:id="@+id/header_picture"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_centerVertical="true"
                    android:layout_marginRight="8dp"
                    android:src="@android:drawable/ic_dialog_info" />
    
                <TextView
                    android:id="@+id/header_title"
                    style="@style/TextAppearance.AppCompat.Title"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_toRightOf="@+id/header_picture"
                    android:text="Toolbar Title"
                    android:textColor="@android:color/white" />
    
                <TextView
                    android:id="@+id/header_subtitle"
                    style="@style/TextAppearance.AppCompat.Subhead"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_below="@+id/header_title"
                    android:layout_toRightOf="@+id/header_picture"
                    android:text="Toolbar Subtitle"
                    android:textColor="@android:color/white" />
    
            </RelativeLayout>
        </RelativeLayout>
    
        <FloatingActionButton
            android:id="@+id/header_fab"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="bottom|right"
            android:layout_margin="10dp"
            android:src="@drawable/ic_open_in_browser"/>
    
    </FrameLayout>
    
    Run Code Online (Sandbox Code Playgroud)

    (注意,您可以使用负边距/填充来跨越晶圆厂2 Views)

  3. 现在是有趣的部分.为了激活我们假货的扩展Toolbar,我们实施了ListView onScrollListener.

    // The height of your fully expanded header view (same than in the xml layout)
    int headerHeight = getResources().getDimensionPixelSize(R.dimen.header_height);
    // The height of your fully collapsed header view. Actually the Toolbar height (56dp)
    int minHeaderHeight = getResources().getDimensionPixelSize(R.dimen.action_bar_height);
    // The left margin of the Toolbar title (according to specs, 72dp)
    int toolbarTitleLeftMargin = getResources().getDimensionPixelSize(R.dimen.toolbar_left_margin);
    // Added after edit
    int minHeaderTranslation;
    
    private ListView listView;
    
    // Header views
    private View headerView;
    private RelativeLayout headerContainer;
    private TextView headerTitle;
    private TextView headerSubtitle;
    private FloatingActionButton headerFab;
    
    
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
    {
        View rootView = inflater.inflate(R.layout.listview_fragment, container, false);
        listView = rootView.findViewById(R.id.listview);
    
        // Init the headerHeight and minHeaderTranslation values
    
        headerHeight = getResources().getDimensionPixelSize(R.dimen.header_height);
        minHeaderTranslation = -headerHeight + 
            getResources().getDimensionPixelOffset(R.dimen.action_bar_height);
    
        // Inflate your header view
        headerView = inflater.inflate(R.layout.header_view, listview, false);
    
        // Retrieve the header views
        headerContainer = (RelativeLayout) headerView.findViewById(R.id.header_container);
        headerTitle = (TextView) headerView.findViewById(R.id.header_title);
        headerSubtitle = (TextView) headerView.findViewById(R.id.header_subtitle);
        headerFab = (TextView) headerView.findViewById(R.id.header_fab);;
    
        // Add the headerView to your listView
        listView.addHeaderView(headerView, null, false);
    
        // Set the onScrollListener
        listView.setOnScrollListener(this);        
    
        // ...
    
        return rootView;
    }
    
    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState)
    {
        // Do nothing
    }
    
    
    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount)
    {
        Integer scrollY = getScrollY(view);
    
        // This will collapse the header when scrolling, until its height reaches
        // the toolbar height
        headerView.setTranslationY(Math.max(0, scrollY + minHeaderTranslation));
    
        // Scroll ratio (0 <= ratio <= 1). 
        // The ratio value is 0 when the header is completely expanded, 
        // 1 when it is completely collapsed
        float offset = 1 - Math.max(
            (float) (-minHeaderTranslation - scrollY) / -minHeaderTranslation, 0f);
    
    
        // Now that we have this ratio, we only have to apply translations, scales,
        // alpha, etc. to the header views
    
        // For instance, this will move the toolbar title & subtitle on the X axis 
        // from its original position when the ListView will be completely scrolled
        // down, to the Toolbar title position when it will be scrolled up.
        headerTitle.setTranslationX(toolbarTitleLeftMargin * offset);
        headerSubtitle.setTranslationX(toolbarTitleLeftMargin * offset);
    
        // Or we can make the FAB disappear when the ListView is scrolled 
        headerFab.setAlpha(1 - offset);
    }
    
    
    // Method that allows us to get the scroll Y position of the ListView
    public int getScrollY(AbsListView view)
    {
        View c = view.getChildAt(0);
    
        if (c == null)
            return 0;
    
        int firstVisiblePosition = view.getFirstVisiblePosition();
        int top = c.getTop();
    
        int headerHeight = 0;
        if (firstVisiblePosition >= 1)
            headerHeight = this.headerHeight;
    
        return -top + firstVisiblePosition * c.getHeight() + headerHeight;
    }
    
    Run Code Online (Sandbox Code Playgroud)

请注意,此代码的某些部分我没有测试过,所以请随意突出显示错误.但总的来说,我知道这个解决方案有效,即使我确信它可以改进.

编辑2:

上面的代码中有一些错误(我直到今天才测试...),所以我改了几行才能使它工作:

  1. 我介绍了另一个变量minHeaderTranslation,它取代了minHeaderHeight;
  2. 我更改了应用于标题View的Y转换值:

        headerView.setTranslationY(Math.max(-scrollY, minHeaderTranslation));
    
    Run Code Online (Sandbox Code Playgroud)

    至 :

        headerView.setTranslationY(Math.max(0, scrollY + minHeaderTranslation));
    
    Run Code Online (Sandbox Code Playgroud)

    以前的表达根本没有用,我很抱歉......

  3. 比率计算也发生了变化,因此它现在从工具栏的底部(而不是屏幕顶部)演变为完全展开的标题.

  • 一旦向下滚动太远,标题不会消失吗?Afaik 标题也被回收,因此即使您翻译标题,使其看起来像固定在顶部,我认为一旦实际位置移出视线,它就会消失。 (5认同)
  • 现在,使用新的[Android设计支持库](http://android-developers.blogspot.com.ar/2015/05/android-design-support-library.html),一切都变得更加简单.根据Chris Banes的工作,你可以按照[来自Suleiman用户的这个教程](http://blog.grafixartist.com/toolbar-animation-with-android-design-support-library/)来达到这个效果. [Cheesesquare示例应用程序](https://github.com/chrisbanes/cheesesquare).<br> <br>**编辑**<br>有些用户询问他们是否可以使用相同的想法,但带有图标.[Saulmm Github用户试过类似的东西](https://github.com/saulmm (3认同)

hid*_*dro 26

另请查看CollapsingTitleLayout由Chris Banes在Android团队中撰写的文章:https: //plus.google.com/+ChrisBanes/posts/J9Fwbc15BHN

在此输入图像描述

代码:https://gist.github.com/chrisbanes/91ac8a20acfbdc410a68


den*_*niz 8

使用设计支持库http://android-developers.blogspot.in/2015/05/android-design-support-library.html

在build.gradle中包含它

compile 'com.android.support:design:22.2.0'    
compile 'com.android.support:appcompat-v7:22.2.+'
Run Code Online (Sandbox Code Playgroud)

对于回收者视图也包括这一点

compile 'com.android.support:recyclerview-v7:22.2.0' 
Run Code Online (Sandbox Code Playgroud)

    <!-- AppBarLayout allows your Toolbar and other views (such as tabs provided by TabLayout) 
    to react to scroll events in a sibling view marked with a ScrollingViewBehavior.-->
    <android.support.design.widget.AppBarLayout
        android:id="@+id/appbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:fitsSystemWindows="true">

        <!-- specify tag app:layout_scrollFlags -->
        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:layout_scrollFlags="scroll|enterAlways"/>

        <!-- specify tag app:layout_scrollFlags -->
        <android.support.design.widget.TabLayout
            android:id="@+id/tabLayout"
            android:scrollbars="horizontal"
            android:layout_below="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="?attr/colorPrimary"
            app:layout_scrollFlags="scroll|enterAlways"/>

        <!--  app:layout_collapseMode="pin" will help to pin this view at top when scroll -->
        <TextView
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:text="Title"
            android:gravity="center"
            app:layout_collapseMode="pin" />

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

    <!-- This will be your scrolling view. 
    app:layout_behavior="@string/appbar_scrolling_view_behavior" tag connects this features -->
    <android.support.v7.widget.RecyclerView
        android:id="@+id/list"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

    </android.support.v7.widget.RecyclerView>

</android.support.design.widget.CoordinatorLayout>
Run Code Online (Sandbox Code Playgroud)

您的活动应该扩展AppCompatActivity

public class YourActivity extends AppCompatActivity {

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

        //set toolbar
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
    }

}
Run Code Online (Sandbox Code Playgroud)

您的应用主题应该是这样的

    <resources>
            <!-- Base application theme. -->   
            <style name="AppTheme" parent="Theme.AppCompat.NoActionBar">
            </style>
    </resources>
Run Code Online (Sandbox Code Playgroud)