如何使ActionBar不那么单调
此时我没有生产博客,此时我只是优秀博客的搬运工!
博客原址,有兴趣的可以查看一下。
详情如下:
使ActionBar不那么单调
回到2013年5月份,每一个人都发觉了Play Music中ActionBar的漂亮效果。这种效果是很好的一种方式,使得ActionBar成为UI中有回馈的部分。
昨天我发现了Newsstand—这个应用引进了新的方式,使得ActionBar达到了新的水平。如果你打开这个应用的发布页,你会注意到不带图标的ActionBar是半透明的,而且和一个大的图片集(一个大的杂志图标,描述了一些新闻)交叠在一起。一旦你开始下滑,大杂志图标会位移并缩放,以匹配ActionBar的图标所在。
这是一个本效果的gif展示图:
这样做的主要优势是使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); }
感谢Cyril Mottier指出这点。这个一个非常简洁的解决方案。
移动并缩放图标
要实现平移,你需要检索到图标视图。这个视图可以通过经典的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动画
总结
正如此处所言,问题有不同实现方式的称为“同步滚动”的伎俩。这个效果的天才之处完全是通过它来实现的!
如何使ActionBar不那么单调的更多相关文章
- FZU 1894 志愿者选拔 (单调队列)
/****************************************************************** 题目: 志愿者选拔(FZU 1894) 算法: 单调队列 算法思 ...
- Making Your ActionBar Not Boring
这篇文章转自国外一个技术大牛的博客,首先感谢这位大牛的无私奉献. Android应用中有一名位 Google书报摊的应用,他实现了一种新的ActionBar风格.当用户初始进入该界面的时候,为一个透明 ...
- Android UI ActionBar功能-自动隐藏 Action Bar
为了使ActionBar不影响Activity的布局内容,我们还可以设置ActionBar,将其设置为透明,并且让Activity是头部自动空出一个ActionBar的空间: 官方文档:http:// ...
- 漂亮的ActionBar效果
Newsstand—这个应用引进了新的方式,使得ActionBar达到了新的水平.如果你打开这个应用的发布页,你会注意到不带图标的ActionBar是半透明的,而且和一个大的图片集(一个大的杂志图标, ...
- 【模板时间】◆模板·III◆ 单调子序列
◆模板·III◆ 单调子序列 以前只知道DP用 O(n2) 的做法,现在才发现求单调子序列方法好多…… ◇ 模板简述 单调子序列包括 升序/降序/非升序/非降序 子序列.主要题型如下: ①在原串中找到 ...
- Codeforces Round #FF(255) DIV2
A - DZY Loves Hash 水题,开辟一个数组即可 #include <iostream> #include <vector> #include <algori ...
- 《CODE》读后笔记——第1~13章
1.电筒密谈 Morse code表 文中提到"英语词汇就是一种编码".这句话仿佛有一种哲学思想在里面,万物皆可以以任何形式编码,只是编码的方式和途径不同.有些编码简单易懂易于接受 ...
- 《CODE》书摘
2016-11-08 14:59:16 可以说英语词汇就是一种编码. 2016-11-08 15:19:04 实际上任何两种不同的东西经过一定的组合都可以代表任何种类的信息. 2016-11-08 1 ...
- Android ActionBarSherlock使用教程
Android ActionBarSherlock使用教程 本文转自 http://www.chenwg.com/android/actionbarsherlock%E4%BD%BF%E7%94%A8 ...
随机推荐
- 手机号ID开关星号(*)
.h文件 <span style="font-size:18px;">/** * 转成星号工具 */ @interface AsteriskTool : NSObjec ...
- poj 3400 Dropping the stones
//next_permutation全阵列 # include <stdio.h> # include <algorithm> # include <string.h&g ...
- (札记)Java应用架构设计-模块化模式与OSGi
本书主要模块化模式的优点.模块化方法与模式.OSGi简单使用等内容.分3大部分: 第一部分介绍了模块化概念.为什么要模块化,以及一些模块化要考虑的东西,如模块粒度,依赖关系,重用性灵活性等. 第二部分 ...
- ORA-07445: :一个意料之外的问题发生了 核心转储 [ldxsnf()+625] [SIGSEGV
ALERT登录错误消息: Mon Jan 20 15:03:22 2014 Incremental checkpoint up to RBA [0x442f.abd.0], current log t ...
- 用Maven整合SpringMVC+Spring+Hibernate 框架,实现简单的插入数据库数据功能
一.搭建開始前的准备 1.我用的MyEclipse2014版,大家也能够用IDEA. 2.下载Tomcat(免安装解压包).MySQL(zip包下载地址 免安装解压包,优点就是双击启动,最后我会把ba ...
- 首先运行application的name相应的类或做activity中间name相应的类?
今天找到该程序条目中找到以下两个条件name我写了一个测试程序,一般如以下: 看mainfest.xml <application android:allowBackup="true& ...
- 带你轻松玩转Git--瞬间创建本地仓库
在上一篇文章中我们对版本控制有了一个比较宏观的了解,同时也能够看到Git 所处在的历史地位.并且对版本控制系统的体系进行了一个宏观的对比,貌似让读者看起来挺复杂的样子. 笔者将会尽可能的简单向大家分享 ...
- SQL Profile (总结4)--使用演示示例
前提:sql profile工具的相关视图 dba_sql_profile 10g: sys.sqlprof$attr & sqlprof$ 11g: sys.sqlobj$data & ...
- 【MySQL案件】mysql登录-S失败
1.1.1. mysql登录mysql时间,-S参数失效 [环境的叙述性说明] mysql5.5.14 [问题叙述性说明] 配置多个实例 实例1 实例2 datadir /home/mysql_330 ...
- 基OOP知识
从今天开始,我开始总结GAO还通高老师<android道路的建筑师>,尝试一个星期写三个博客. 相对而言.看到这篇文章有点速度比你可以观看视频,刚才看的视频是更具体的.假设有兴趣,跟着我去 ...