NotBoringActionBar项目的思路解析
NotBoringActionBar项目实现方式十分巧妙。
从外表上看,ListView向上滚动时,ListView的头部会缩小,logo图标从中间不断缩小移向标题栏的左上角,并且标题栏逐渐显示出来。
<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>
布局文件中的FragmentLayout实际上就是我们看到的背景图片和Logo图标的区域。它们与ListView是重叠在一起的。那么问题来了,重叠在一起?那为什么ListView的内容不会被遮盖,或者背景图片和Logo图标的区域被遮盖呢?
源码中,ListView实际上被设置了ListView头部,看Activity文件中的setupListView方法,其中有以下代码段:
mPlaceHolderView = getLayoutInflater().inflate(R.layout.view_header_placeholder, mListView, false);
mListView.addHeaderView(mPlaceHolderView);
ListView被添加了ListView头部,并且ListView头部的高度与“背景图片和Logo图标的区域”的高度是一样的,这就是为什么ListView和“背景图片和Logo图标的区域”不会互相遮挡的原因。
不过等等,这样的话,屏幕一上滑或下滑,“背景图片和Logo图标的区域”会一直不动,ListView则会滚动,那这不乱套了吗?
同样看Activity文件中的setupListView方法,其中设置了ListView的滑动监听:
mHeader.setTranslationY(Math.max(-scrollY, mMinHeaderTranslation));
只要一滑动,“背景图片和Logo图标的区域”就会通过setTranslationY方法设置高度,这样看起来就像它会随着屏幕上滑而上滑,并且最终固定在标题栏的高度大小。
说到标题栏,实际上,它一直都在,只是刚开始它被设置为透明的。逐渐显示出来的效果实际上是标题栏文本(前景)的逐渐显示,Activity文件中的setupActionBar方法:
private void setupActionBar() {
ActionBar actionBar = getActionBar();
//设置标题栏透明
actionBar.setIcon(R.drawable.ic_transparent);
//getActionBarTitleView().setAlpha(0f);
}
那么它是怎样随着屏幕上滑逐渐显示出来的呢?秘密还在在ListView的滑动监听中:
setTitleAlpha(clamp(5.0F * ratio - 4.0F, 0.0F, 1.0F));
setTitleAlpha方法不断改变标题栏文本的透明度,这就出现了标题栏逐渐显示出来的效果。
最后一点,当屏幕上滑时,Logo图标会逐渐缩小并向左上移动,实现的方法是Activity文件中的interpolate:
private void interpolate(View view1, View view2, float interpolation) {
//获取Logo图片的Rect
getOnScreenRect(mRect1, view1);
//获取view2图片的Rect
getOnScreenRect(mRect2, view2);
//缩放比例
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));
//设置Logo的位移和缩放比例
view1.setTranslationX(translationX);
view1.setTranslationY(translationY - mHeader.getTranslationY());
view1.setScaleX(scaleX);
view1.setScaleY(scaleY);
}