zoukankan      html  css  js  c++  java
  • Making Your ActionBar Not Boring


          这篇文章转自国外一个技术大牛的博客。首先感谢这位大牛的无私奉献。

    Android应用中有一名为 Google书报摊的应用,他实现了一种新的ActionBar风格。效果如以下的动画,因为上传图片大小的限制。对图片做了处理。效果受到了影响,能够查看《Making Your ActionBar Not Boring》会有清晰流畅的动画效果。

          当用户初始进入该界面的时候,为一个透明的 ActiionBar ,这样利用充分的空间显示大图片,假设用户滚动页面须要查看内容的时候,则大图收缩到 ActionBar 中。


                    


        这个的主要优势是使ActionBar和内容完美的结合在一起。整个操作看起来浑然天成,给人一种新鲜的感觉。这篇文章将会解说ActionBar效果和 Ken Burns动画效果的实现。


    The ActionBar trick

    Styles:

    第一步先制作合适的Style,这里须要使用ActionBar的overlay模式并设置透明的ActionBar背景。

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


    布局结构

        布局结构是很重要,基本的布局是一个由ListView和还有一个的FrameLayout(即题图)组成的FrameLayout。

    题图包括两个图片。一个背景大图(即header_picture),一个logo图像(即header_logo)。


    <?xml version="1.0" encoding="utf-8"?>
    
    <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".NoBoringActionBarActivity">
    
        <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">
    
            <com.flavienlaurent.notboringactionbar.KenBurnsView
                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 view 来实现该动画。 能够用一个布局文件来作为该虚拟 header 的 view。


    <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加入上虚拟 header view

    mFakeHeader = getLayoutInflater().inflate(R.layout.fake_header, mListView, false);
    mListView.addHeaderView(mFakeHeader);
    


    获取Scroll位置

    布局文件搞定,须要计算出ListView的滚动位置

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


    特别提示。假设listview第一个可视视图位置大于1。须要计算虚拟视图的高度。


    移动题头

        伴随着listview的滚动,你须要移动题头,以跟踪虚拟题头的移动。

    这些移动以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));
            }
        });


    Title渐变

         这里的Title有个渐变效果,他是怎么实现的呢,首先获取到这个view,使用的Resources.getIdentifier方法。

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

        然后设置初始的 alpha 值。

    getActionBarTitleView().setAlpha(0f);


    在 ListView 滚动的时候,计算该 alpha 值。


    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 值的变化方程式为 f(x) = 5x-4。

    关于该方程式參考:Wikipedia





        而关于标题的淡出Cyril Mottier提供了一个更好的方案。

        在该方案中无需获取 ActionBar title view。使用一个具有自己定义 ForegroundColorSpan 的  SpannableString  。然后在该  SpannableString  上设置文字的 Alpha 值。


    public class AlphaForegroundColorSpan extends ForegroundColorSpan {
    
        private float mAlpha;
    
        public AlphaForegroundColorSpan(int color) {
            super(color);
            }
            […]
    
            @Override
            public void updateDrawState(TextPaint ds) {
                    ds.setColor(getAlphaColor());
            }
    
        public void setAlpha(float alpha) {
            mAlpha = alpha;
        }
    
        public float getAlpha() {
            return mAlpha;
        }
    
        private int getAlphaColor() {
            int foregroundColor = getForegroundColor();
            return Color.argb((int) (mAlpha * 255), Color.red(foregroundColor), Color.green(foregroundColor), Color.blue(foregroundColor));
        }
    }


        滚动的时候改动该  SpannableString  的 Alpha值并设置为 Title,使用相同的 AlphaForegroundColorSpan 和 SpannableString 避免频繁 GC 来提升性能。


    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
                setTitleAlpha(clamp(5.0F * ratio – 4.0F, 0.0F, 1.0F));
            }
        });
    
    private void setTitleAlpha(float alpha) {
            mAlphaForegroundColorSpan.setAlpha(alpha);
            mSpannableString.setSpan(mAlphaForegroundColorSpan, 0, mSpannableString.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
            getActionBar().setTitle(mSpannableString);
        }


    移动&缩放icon


    先获取该 图标 View, 然后在 ActionBar 上设置一个透明的图标。

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

    ActionBar actionBar = getActionBar();
    actionBar.setIcon(R.drawable.ic_transparent);


          当 ListView 滚动时候。依据 header 的高度来移动和缩放图标。该缩放和位移是依据两个图标的位置关系和大小关系来计算的。

    代码例如以下:


    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);
            }
        });

        注意你也能够用 AccelerateDecelerateInterpolator 来让动画看起来更平滑一些。


        在该演示样例代码中还包括了一个 Ken Burns 动画,使题图能够移动。能够參考事实上现:KenBurnsView.java

    完整演示样例项目代码.下载。


    总结:

    View的同步Scroll总有他的类似之处,大家要多思考。
           As it’s said here, it’s always (with a few different details) the same trick called synchronized scrolling. The true genius of this effect is to have thought about it!

    參考:

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

  • 相关阅读:
    什么样的代码称得上是好代码?
    九年程序人生 总结分享
    Docker入门 第一课 --.Net Core 使用Docker全程记录
    阿里云 Windows Server 2012 r2 部署asp.net mvc网站 平坑之旅
    Visual studio 2015 Community 安装过程中遇到问题的终极解决
    Activiti6.0 spring5 工作流引擎 java SSM流程审批 项目框架
    java 进销存 库存管理 销售报表 商户管理 springmvc SSM crm 项目
    Leetcode名企之路
    24. 两两交换链表中的节点
    21. 合并两个有序链表
  • 原文地址:https://www.cnblogs.com/gcczhongduan/p/5093501.html
Copyright © 2011-2022 走看看