展开/折叠 Lollipop 工具栏动画(Telegram 应用程序) [英] Expand/Collapse Lollipop toolbar animation (Telegram app)

本文介绍了展开/折叠 Lollipop 工具栏动画(Telegram 应用程序)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想弄清楚工具栏的展开/折叠动画是如何完成的.如果您查看 Telegram 应用程序设置,您会看到有一个列表视图和工具栏.向下滚动时,工具栏会折叠,向上滚动时会展开.还有头像和FAB的动画.有没有人对此有任何线索?你认为他们在它之上构建了所​​有动画吗?也许我遗漏了新 API 或支持库中的某些内容.

I'm trying to figure out how the expand/collapse animation of the toolbar is done. If you have a look at the Telegram app settings, you will see that there is a listview and the toolbar. When you scroll down, the toolbar collapse, and when you scroll up it expands. There is also the animation of the profile pic and the FAB. Does anyone have any clue on that? Do you think they built all the animations on top of it? Maybe I'm missing something from the new APIs or the support library.

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

I noticed the same behaviour on the Google calendar app, when you open the Spinner (I don't think it's a spinner, but it looks like): The toolbar expands and when you scroll up, it collapse.

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

Just to clearify: I don't need the QuickReturn method. I know that probably Telegram app is using something similar. The exact method that I need is the Google Calendar app effect. I've tried with

android:animateLayoutChanges="true"

并且 expand 方法效果很好.但很明显,如果我向上滚动 ListView,工具栏不会折叠.

and the expand method works pretty well. But obviously, If I scroll up the ListView, the toolbar doesn't collapse.

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

I've also thought about adding a GestureListener but I want to know if there are any APIs or simpler methods of achieving this.

如果没有,我想我会选择 GestureListener.希望有一个流畅的动画效果.

If there are none, I think I will go with the GestureListener. Hopefully to have a smooth effect of the Animation.

谢谢!

推荐答案

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

Since the release of the Android Design support library, there's an easier solution. Check joaquin's answer

--

这就是我的做法,可能还有很多其他解决方案,但这个对我有用.

Here's how I did it, there probably are many other solutions but this one worked for me.

  1. 首先,您必须使用具有透明背景的Toolbar.扩展&折叠的Toolbar 实际上是一个假的,它位于透明的Toolbar 之下.(您可以在下面的第一个屏幕截图中看到 - 有边距的那个 - 这也是他们在 Telegram 中的做法).

  1. First of all, you have to use a Toolbar with a transparent background. The expanding & collapsing Toolbar is actually a fake one that's under the transparent Toolbar. (you can see on the first screenshot below - the one with the margins - that this is also how they did it in Telegram).

我们只为 NavigationIcon 和溢出的 MenuItem 保留实际的 Toolbar.

We only keep the actual Toolbar for the NavigationIcon and the overflow MenuItem.

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

Everything that's in the red rectangle on the second screenshot (ie the fake Toolbar and the FloatingActionButton) is actually a header that you add to the settings ListView (or ScrollView).

所以你必须在一个单独的文件中为这个标题创建一个布局,看起来像这样:

So you have to create a layout for this header in a separate file that could look like this :

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

(请注意,您可以使用负边距/填充使 fab 跨在 2 个 Views)

(Note that you can use negative margins/padding for the fab to be straddling on 2 Views)

现在是有趣的部分.为了动画我们的假 Toolbar 的扩展,我们实现了 ListView onScrollListener.

Now comes the interesting part. In order to animate the expansion of our fake Toolbar, we implement the 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;
}

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

Note that there are some parts of this code I didn't test, so feel free to highlight mistakes. But overall, I'm know that this solution works, even though I'm sure it can be improved.

编辑 2:

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

There were some mistakes in the code above (that I didn't test until today...), so I changed a few lines to make it work :

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

  1. I introduced another variable, minHeaderTranslation, which replaced minHeaderHeight;
  2. I changed the Y translation value applied to the header View from :

    headerView.setTranslationY(Math.max(-scrollY, minHeaderTranslation));

到:

    headerView.setTranslationY(Math.max(0, scrollY + minHeaderTranslation));

之前的表达式根本不起作用,我很抱歉...

Previous expression wasn't working at all, I'm sorry about that...

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

The ratio calculation also changed, so that it now evolves from the bottom the toolbar (instead of the top of the screen) to the full expanded header.

这篇关于展开/折叠 Lollipop 工具栏动画(Telegram 应用程序)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆