zoukankan      html  css  js  c++  java
  • 漂亮的ActionBar效果

    Newsstand—这个应用引进了新的方式,使得ActionBar达到了新的水平。如果你打开这个应用的发布页,你会注意到不带图标的ActionBar是半透明的,而且和一个大的图片集(一个大的杂志图标,描述了一些新闻)交叠在一起。一旦你开始下滑,大杂志图标会位移并缩放,以匹配ActionBar的图标所在。

    这样做的主要优势是使ActionBar真正成为内容的一部分。

    在这篇文章中,我主要解释一下如何制作整个发布页面(ActionBar效果和Ken Burns动画)。我将详细介绍一下ActionBar效果的实现细节。我将解释一下如何创建带有Ken Burns动画的视图。

    ActionBar效果

    风格

    第一步是设置风格:

    • 半透明ActionBar
    • 启用overlay模式
    <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(如header_picture和header_logo),前者是一个普通的图片,后者是将要位移并缩放到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拥有相同的高度。你可以使用如下布局来描述这个伪header:

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

    之后,你必须用代码填充和添加伪header:

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

    有一点请注意:如果ListView第一个可见item的索引不小于1的话,你必须在计算中关注header的高度。

    平移header

    在ListView的滑动过程中,为了赶得上ListView的伪header,你必须平移真的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));
       }
    }
    );

    标题可见性

    为了达到渐变的效果,你需要检索标题View。这个视图可以通过使用Resources.getIdentifier方法检索到。

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

    然后,只要初始化这个View的alpha值:

    getActionBarTitleView().setAlpha(0f);

    在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); //actionbar title alpha 
        getActionBarTitleView().setAlpha(clamp(5.0F * ratio - 4.0F, 0.0F, 1.0F)); 
      }
     });

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

    这个钳位方法是基本的Math方法(http://en.wikipedia.org/wiki/Clamping_(graphics);

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

    来自Cyril Mottier的更好的解决方案

    首先,你不必再获取ActionBar的标题视图的引用了。我们将要使用带有自定义ForegroundColorSpan(http://developer.android.com/reference/android/text/style/ForegroundColorSpan.html)的SpannableString(http://developer.android.com/reference/android/text/SpannableString.html)。我们需要自定义的能够设置颜色可见性的ForegroundColorSpan:

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

    在ListView的滚动过程中,我们在AlphaForegroundColorSpan对象上设置alpha值并最终调用ActionBar.setTitle来刷新标题视图。请注意:考虑到性能(避免GC),你必须保持相同的AlphaForegroundColorSpan和SpannableString对象。

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

    移动并缩放图标

    要实现平移,你需要检索到图标视图。这个视图可以通过经典的findViewById方法来实现。

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

    然后,在ActionBar上设置透明的图标。

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

    在ListView滚动的过程中,你必须依赖于header的平移率来移动并缩放图标视图。主要原则是在透明可变的ActionBar图标视图和header的logo视图之间产生”diff”。这个”diff”是使用两个屏幕上的Rect来计算的,并且这个”diff”产生scaleX, scaleY,translationX和translationY值。最终,这些值用于平移和缩放header的logo视图,直到logo视图匹配了透明度可变的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) {
        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动画

    代码地址:https://github.com/flavienlaurent/NotBoringActionBar/blob/master/App/src/main/java/com/flavienlaurent/notboringactionbar/KenBurnsView.java

    总结

    正如此处所言,问题有不同实现方式的称为“同步滚动”的伎俩。

  • 相关阅读:
    转 Scott Mitchell的ASP.NET2.0数据指南中文版索引
    ASP.NET2.0中Gridview中数据操作技巧
    SQL语句导入导出大全
    SQL2000存储过程的基础教程
    如何取出Gridview选中行的数值
    asp.net2.0将EXCEL导入到MS Sql server2000
    C#泛型秘诀(5)
    C#泛型秘诀(4)
    C#泛型秘诀(1)
    C#泛型秘诀(2)
  • 原文地址:https://www.cnblogs.com/zhujiabin/p/4190799.html
Copyright © 2011-2022 走看看