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();
  if (c == null) {
    return ;
  }
  int firstVisiblePosition = mListView.getFirstVisiblePosition();
  int top = c.getTop();
  int headerHeight = ;
  if (firstVisiblePosition >= ) {
   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 * ), 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

总结

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

漂亮的ActionBar效果的更多相关文章

  1. C#自动生成漂亮的水晶效果头像

    C#自动生成漂亮的水晶效果头像 与其他的微博系统相同,在“多可内网微博系统”的用户也可上传自己的头像,并支持头像裁剪. 但“多可内网微博系统”的头像可以更漂亮,因为系统实现了水晶效果的头像.C#程序实 ...

  2. SweetAlert – 替代 Alert 的漂亮的提示效果

    Sweet Alert 是一个替代传统的 JavaScript Alert 的漂亮提示效果.SweetAlert 自动居中对齐在页面中央,不管您使用的是台式电脑,手机或平板电脑看起来效果都很棒.另外提 ...

  3. 一个炫酷的Actionbar效果

    今天在网上看到一个炫酷的Actionbar效果,一个老外做的DEMO,目前很多流行的app已经加入了这个效果. 当用户初始进入该界面的时候,为一个透明的 ActiionBar ,这样利用充分的空间显示 ...

  4. 20个漂亮 CSS3 按钮效果及优秀的制作教程

    在这篇文章中,我们编译了一组有用的 CSS3 动画按钮教程和引人注目的实验.正如我们都知道的,CSS3在网页设计方面是最重要和最关键的,可以使您的网站对访客更具吸引力和互动性.你可以学习这些教程和试验 ...

  5. android仿今日头条App、多种漂亮加载效果、选择器汇总、记事本App、Kotlin开发等源码

    Android精选源码 android漂亮的加载效果 android各种 选择器 汇总源码 Android仿bilibili搜索框效果 Android记事本app.分类,涂鸦.添加图片或者其他附件 仿 ...

  6. openscales实现漂亮的冒泡效果

    使用的时候openscales 默认的冒泡效果确实有点简陋,想实现那种看着比较舒服的效果,只能自己定义了.参考现有的openscales实现的方式,它是通过控件的状态实现的,每个状态中使用Path绘制 ...

  7. swift 实现漂亮的粒子效果CAEmitterLayer

    一些粒子效果 我们经常会在一些游戏或者应用中看到一些炫酷的粒子效果,我们在iOS中也能很轻松的搞一些粒子效果 我们本次做得是一个下雪的效果,看下效果图 源码地址: https://github.com ...

  8. C# WPF 漂亮的loading 效果

    <UserControl x:Class="TestLoadPic.Loading" xmlns="http://schemas.microsoft.com/win ...

  9. 如何使ActionBar不那么单调

    此时我没有生产博客,此时我只是优秀博客的搬运工! 博客原址,有兴趣的可以查看一下. 详情如下: 使ActionBar不那么单调 回到2013年5月份,每一个人都发觉了Play Music中Action ...

随机推荐

  1. OS之进程管理 --- 死锁

    什么是死锁 在正常操作模式下,进程按如下顺序来使用资源: 申请:进程请求资源 使用:进程对资源进行操作 释放:进程释放资源 当一组进程中的每一个进程度在等待一个事件,而这事件只能有一组进程的另一个进程 ...

  2. chromedriver与google版本的对应

    解决chromedriver与chrome版本不兼容的问题 附chromedriver下载地址http://npm.taobao.org/mirrors/chromedriver/ 以下是对应的chr ...

  3. UIScrollView之isTracking delaysContentTouches canCancelContentTouches

    UIScrollView有一个BOOL类型的tracking属性,用来返回用户是否已经触及内容并打算开始滚动,我们从这个属性开始探究UIScrollView的工作原理: 当手指触摸到UIScrollV ...

  4. tensorflow训练了10万次,运行完毕,对这个word2vec终于有点感觉了

    tensorflow训练了10万次,运行完毕,对这个word2vec终于有点感觉了 感觉它能找到词与词之间的关系,应该可以用来做推荐系统.自动摘要.相关搜索.联想什么的 tensorflow1.1.0 ...

  5. sqldeveloper的安装及其使用教程

    1.安装 下载地址:http://www.oracle.com/technetwork/developer-tools/sql-developer/downloads/index-098778.htm ...

  6. ambari 2.6.2 安装 hdp 2.6.5.0 遇到的问题

    1.hive-client 无法安装 一直报错(symlink target  already exists and it is not a symlink.),hive-client 已经存在且不是 ...

  7. 【Docker系列】:全面认识Docker和基本指令

    Docker逐渐成为虚拟化技术的佼佼者,Docker技术也是架构师的必备技能. 什么是Docker Docker 是一个开源的应用容器引擎,基于Go语言,诞生于2013年初,最初发起者是dotClou ...

  8. 全局描述符表(GDT)——《x86汇编语言:从实模式到保护模式》读书笔记09

    在进入保护模式之前,我们先要学习一些基础知识.今天我们看一下全局描述符表(Global Descriptor Table, 简称GDT). 同实模式一样,在保护模式下,对内存的访问仍然使用段地址加偏移 ...

  9. j2ee高级开发技术课程第二周(web请求的整个过程、XML)

    博客非原创,只是收集整理了一下网上的一些文章 一.web请求的整个过程 1)把URL分割成几个部分:协议.网络地址.资源路径.其中网络地址指示该连接网络上哪一台计算机,可以是域名或者IP地址,可以包括 ...

  10. Innosetup新增Wizard Page

    Innosetup 新增一个向导页面,让用户选择安装方式 转载于:http://www.docin.com/p-612536939.html 在Innosetup的向导页面中,新增一个页面,提供两种安 ...