zoukankan      html  css  js  c++  java
  • 实现一个与内容合二为一的ActionBar动画效果


    实现一个与内容合二为一的ActionBar动画效果,让你的actionbar更生动。以下是效果图:

    这样的效果的优点是让actionbar也成为了内容的一部分,实际应用的效果比图片展示的效果要好,除了actionbar渐渐出现的效果外。背景图片另一种称之为 Ken Burns effect 的动态效果。


    以下解说实现过程。

    设置actionbar的样式。我们须要例如以下两点:

    1.actionbar是透明的。

    2.开启overlay mode模式

    <resources>
        <style name="TransparentTheme" parent="@android:style/Theme.Holo.Light">
            <item name="android:windowBackground">@null</item>
            <item name="android:actionBarStyle">@style/ActionBarStyle.Transparent</item>
            <item name="android:windowActionBarOverlay">true</item>
        </style>
        <style name="ActionBarStyle.Transparent" parent="@android:Widget.ActionBar">
            <item name="android:background">@null</item>
            <item name="android:displayOptions">homeAsUp|showHome|showTitle</item>
            <item name="android:titleTextStyle">@style/ActionBarStyle.Transparent.TitleTextStyle</item>
        </style>
        <style name="ActionBarStyle.Transparent.TitleTextStyle" parent="@android:style/TextAppearance.Holo.Widget.ActionBar.Title">
            <item name="android:textColor">@android:color/white</item>
        </style>
    </resources>

    布局

    布局是实现的关键,FrameLayout中包括一个ListView以及另外一个FrameLayout(头部,显示图片的地方,这里我们称之为header)。header中包括了两个ImageView,一个用于显示背景图片,一个用于显示logo。这个logo会跟着listView的滚动而变化。并终于跑到actionbar中。

    <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <ListView
            android:id="@+id/listview"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="@android:color/white" />
        <FrameLayout
            android:id="@+id/header"
            android:layout_width="match_parent"
            android:layout_height="@dimen/header_height">
            <ImageView
                android:id="@+id/header_picture"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:src="@drawable/picture0" />
            <ImageView
                android:id="@+id/header_logo"
                android:layout_width="@dimen/header_logo_size"
                android:layout_height="@dimen/header_logo_size"
                android:layout_gravity="center"
                android:src="@drawable/ic_header_logo" />
        </FrameLayout>
    </FrameLayout>
    这里的技巧是给Listview加入一个伪造的header,而且将它的高度设置成真实header的高度

    伪造header的xml代码

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="@dimen/header_height"
        android:orientation="vertical">
    </LinearLayout>
    将他inflate后加入到ListView中:
    mFakeHeader = getLayoutInflater().inflate(R.layout.fake_header, mListView, false);
    mListView.addHeaderView(mFakeHeader);

    获得滚动位置。这段代码是从stackOverflow上摘抄的:
    public int getScrollY() {
        View c = mListView.getChildAt(0);
        if (c == null) {
            return 0;
        }
        int firstVisiblePosition = mListView.getFirstVisiblePosition();
        int top = c.getTop();
        int headerHeight = 0;
        if (firstVisiblePosition >= 1) {
            headerHeight = mPlaceHolderView.getHeight();
        }
        return -top + firstVisiblePosition * c.getHeight() + headerHeight;
    }

    移动header的位置

    当ListView滚动的时候,你必须移动header的位置让他和伪造的ListView header保持同步,注意移动到了actionbar的边界为止。

    mListView.setOnScrollListener(new AbsListView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(AbsListView view, int scrollState) {
            }
            @Override
            public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
                int scrollY = getScrollY();
                //sticky actionbar
                mHeader.setTranslationY(Math.max(-scrollY, mMinHeaderTranslation));
            }
        });

    标题的渐变

    actionbar标题文字的出现是渐变的。而标题文字所在的TextView控件能够通过Resource的getIdentifier方法获得。

    private TextView getActionBarTitleView() {
        int id = Resources.getSystem().getIdentifier("action_bar_title", "id", "android");
        return (TextView) findViewById(id);
    }

    初始化的时候将它的透明度设置为0:
    getActionBarTitleView().setAlpha(0f);

    ListView滚动的时候这个透明度变化的根据是header移动距离的一个比值ratio:
    mListView.setOnScrollListener(new AbsListView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(AbsListView view, int scrollState) {
            }
            @Override
            public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
                float ratio = clamp(mHeader.getTranslationY() / mMinHeaderTranslation, 0.0f, 1.0f);
                //actionbar title alpha
                getActionBarTitleView().setAlpha(clamp(5.0F * ratio - 4.0F, 0.0F, 1.0F));
            }
        });

    Alpha values: f(x) = 5x-4

    clamp是一个主要的数学计算公式:


    public static float clamp(float value, float max, float min) {
        return Math.max(Math.min(value, min), max);
    }

    应用图标和logo的移动和缩放

    首先你要获得图标所在的ImageView:

    private ImageView getActionBarIconView() {
        return (ImageView) findViewById(android.R.id.home);
    }

    设置一个透明的图标
    ActionBar actionBar = getActionBar();
    actionBar.setIcon(R.drawable.ic_transparent);

    然后在ListView滑动的过程中依据header的移动比率相应用图标和logo移动与缩放。在这个过程中通过获得应用图标和header中的logo在屏幕上的矩形区域来推断该怎样移动与缩放。

    mListView.setOnScrollListener(new AbsListView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(AbsListView view, int scrollState) {
            }
            @Override
            public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
                float ratio = clamp(mHeader.getTranslationY() / mMinHeaderTranslation, 0.0f, 1.0f);
                //move & scale
                interpolation = mAccelerateDecelerateInterpolator.getInterpolation(ratio);
                View actionBarIconView = getActionBarIconView();
                getOnScreenRect(mRect1, mHeaderLogo);
                getOnScreenRect(mRect2, actionBarIconView);
                float scaleX = 1.0F + interpolation  (mRect2.width() / mRect1.width() – 1.0F);
                float scaleY = 1.0F + interpolation  (mRect2.height() / mRect1.height() – 1.0F);
                float translationX = 0.5F  (interpolation  (mRect2.left + mRect2.right – mRect1.left – mRect1.right));
                float translationY = 0.5F  (interpolation  (mRect2.top + mRect2.bottom – mRect1.top – mRect1.bottom));
                mHeaderLogo.setTranslationX(translationX);
                mHeaderLogo.setTranslationY(translationY – mHeader.getTranslationY());
                mHeaderLogo.setScaleX(scaleX);
                mHeaderLogo.setScaleY(scaleY);
            }
        });

    注:对上面的代码,我自己的理解是应用图标至始至终都没有显示,仅仅是他的位置被header中的logo占领了。

    代码下载:https://github.com/flavienlaurent/NotBoringActionBar


    译文原文:http://flavienlaurent.com/blog/2013/11/20/making-your-action-bar-not-boring/

  • 相关阅读:
    3:基于乐观锁(两种)控制并发: version、external锁
    4: ES内执行Groovy脚本,做文档部分更新、执行判断改变操作类型
    2 Match、Filter、排序、分页、全文检索、短语匹配、关键词高亮
    第63章 ASP.NET Identity 支持
    第62章 EntityFramework支持
    第61章 IdentityServer Options
    第60章 设备流交互服务
    第59章 IdentityServer交互服务
    第58章 Profile Service
    第57章 GrantValidationResult
  • 原文地址:https://www.cnblogs.com/mengfanrong/p/5059374.html
Copyright © 2011-2022 走看看