这篇文章转自国外一个技术大牛的。首先感谢这位大牛的无私奉献。
Android应用中有一名为 Google书报摊的应用,他实现了一种新的ActionBar风格。效果如以下的动画,因为上传图片大小的限制。对图片做了处理。效果受到了影响,能够查看会有清晰流畅的动画效果。
当用户初始进入该界面的时候,为一个透明的 ActiionBar ,这样利用充分的空间显示大图片,假设用户滚动页面须要查看内容的时候,则大图收缩到 ActionBar 中。
这个的主要优势是使ActionBar和内容完美的结合在一起。整个操作看起来浑然天成,给人一种新鲜的感觉。这篇文章将会解说ActionBar效果和 动画效果的实现。
The ActionBar trick
Styles:
第一步先制作合适的Style,这里须要使用ActionBar的并设置透明的ActionBar背景。
布局结构
布局结构是很重要,基本的布局是一个由ListView和还有一个的FrameLayout(即题图)组成的FrameLayout。
题图包括两个图片。一个背景大图(即header_picture),一个logo图像(即header_logo)。
通过在 ListView 上加入一个高度和 题图一样高的 虚拟 header view 来实现该动画。 能够用一个布局文件来作为该虚拟 header 的 view。
使用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,使用的方法。
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。
关于该方程式參考:
而关于标题的淡出提供了一个更好的方案。
在该方案中无需获取 ActionBar title view。使用一个具有自己定义 的 。然后在该 上设置文字的 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)); }}
滚动的时候改动该 的 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); } });
注意你也能够用 来让动画看起来更平滑一些。
在该演示样例代码中还包括了一个 Ken Burns 动画,使题图能够移动。能够參考事实上现:
完整演示样例项目
总结:
參考:
http://flavienlaurent.com/blog/2013/11/20/making-your-action-bar-not-boring/