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/

  • 相关阅读:
    [极客大挑战 2019]BuyFlag
    [极客大挑战 2019]BabySQL
    [网鼎杯 2018]Fakebook
    C语言学习笔记_内存数据和字符串
    剑指OFFER_数据流中的中位数
    剑指OFFER_滑动窗口的最大值
    剑指OFFER_矩阵中的路径
    C语言学习笔记_指针相关知识
    剑指OFFER_机器人的运动范围
    剑指OFFER_剪绳子
  • 原文地址:https://www.cnblogs.com/gcczhongduan/p/5093501.html
Copyright © 2011-2022 走看看