Android-ObservableScrollView实现带图片的弹性空间效果
什么是弹性空间效果
弹性空间效果(Flexible Space)是Material Design中一种常见的视觉交互模式,它通常表现为当用户滚动内容时,顶部区域的图片或视图会产生视差滚动、缩放等动态效果。这种设计不仅增强了用户体验的沉浸感,还能有效利用屏幕空间。
实现原理概述
要实现带图片的弹性空间效果,我们需要以下几个核心组件:
- ObservableScrollView:可监听滚动事件的ScrollView
- ImageView:作为背景图片的视图
- 覆盖层(Overlay):用于实现淡入淡出效果
- 标题视图:随着滚动会缩放和移动的标题
- 浮动操作按钮(FAB):可选元素,增强Material Design风格
详细实现步骤
1. 布局结构设计
<FrameLayout>
<!-- 背景图片,实现视差滚动效果 -->
<ImageView android:id="@+id/image"/>
<!-- 覆盖层,实现淡入淡出效果 -->
<View android:id="@+id/overlay"/>
<!-- 可滚动内容区域 -->
<ObservableScrollView android:id="@+id/scroll">
<LinearLayout android:orientation="vertical">
<View/>
<TextView/>
</LinearLayout>
</ObservableScrollView>
<!-- 标题区域 -->
<LinearLayout android:orientation="vertical">
<TextView android:id="@+id/title"/>
<View/>
</LinearLayout>
<!-- 浮动操作按钮 -->
<FloatingActionButton android:id="@+id/fab"/>
</FrameLayout>
2. 初始化工作
在Activity的onCreate()
方法中,我们需要完成以下初始化:
// 设置标题
mTitleView.setText(getTitle());
setTitle(null);
// 获取各种尺寸参数
mFlexibleSpaceImageHeight = getResources().getDimensionPixelSize(R.dimen.flexible_space_image_height);
mFlexibleSpaceShowFabOffset = getResources().getDimensionPixelSize(R.dimen.flexible_space_show_fab_offset);
mFabMargin = getResources().getDimensionPixelSize(R.dimen.margin_standard);
mActionBarSize = getActionBarSize();
// 获取视图引用
mImageView = findViewById(R.id.image);
mOverlayView = findViewById(R.id.overlay);
mScrollView = (ObservableScrollView) findViewById(R.id.scroll);
mScrollView.setScrollViewCallbacks(this);
mTitleView = (TextView) findViewById(R.id.title);
mFab = findViewById(R.id.fab);
// 初始隐藏FAB
ViewHelper.setScaleX(mFab, 0);
ViewHelper.setScaleY(mFab, 0);
3. 滚动动画实现
在onScrollChanged()
回调中实现各种动画效果:
图片和覆盖层的视差滚动
float flexibleRange = mFlexibleSpaceImageHeight - mActionBarSize;
int minOverlayTransitionY = mActionBarSize - mOverlayView.getHeight();
// 覆盖层移动速度为1倍
ViewHelper.setTranslationY(mOverlayView, ScrollUtils.getFloat(-scrollY, minOverlayTransitionY, 0));
// 图片移动速度为0.5倍,产生视差效果
ViewHelper.setTranslationY(mImageView, ScrollUtils.getFloat(-scrollY / 2, minOverlayTransitionY, 0));
覆盖层透明度变化
ViewHelper.setAlpha(mOverlayView, ScrollUtils.getFloat((float) scrollY / flexibleRange, 0, 1));
标题缩放和移动
// 标题缩放
float scale = 1 + ScrollUtils.getFloat((flexibleRange - scrollY) / flexibleRange, 0, MAX_TEXT_SCALE_DELTA);
ViewHelper.setPivotX(mTitleView, 0);
ViewHelper.setPivotY(mTitleView, 0);
ViewHelper.setScaleX(mTitleView, scale);
ViewHelper.setScaleY(mTitleView, scale);
// 标题移动
int maxTitleTranslationY = (int) (mFlexibleSpaceImageHeight - mTitleView.getHeight() * scale);
int titleTranslationY = maxTitleTranslationY - scrollY;
ViewHelper.setTranslationY(mTitleView, titleTranslationY);
浮动操作按钮处理
// FAB移动
int maxFabTranslationY = mFlexibleSpaceImageHeight - mFab.getHeight() / 2;
float fabTranslationY = ScrollUtils.getFloat(
-scrollY + mFlexibleSpaceImageHeight - mFab.getHeight() / 2,
mActionBarSize - mFab.getHeight() / 2,
maxFabTranslationY);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
// 兼容旧版本
FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mFab.getLayoutParams();
lp.leftMargin = mOverlayView.getWidth() - mFabMargin - mFab.getWidth();
lp.topMargin = (int) fabTranslationY;
mFab.requestLayout();
} else {
ViewHelper.setTranslationX(mFab, mOverlayView.getWidth() - mFabMargin - mFab.getWidth());
ViewHelper.setTranslationY(mFab, fabTranslationY);
}
// 根据位置显示/隐藏FAB
if (fabTranslationY < mFlexibleSpaceShowFabOffset) {
hideFab();
} else {
showFab();
}
4. FAB显示/隐藏动画
private boolean mFabIsShown;
private void showFab() {
if (!mFabIsShown) {
ViewPropertyAnimator.animate(mFab).cancel();
ViewPropertyAnimator.animate(mFab).scaleX(1).scaleY(1).setDuration(200).start();
mFabIsShown = true;
}
}
private void hideFab() {
if (mFabIsShown) {
ViewPropertyAnimator.animate(mFab).cancel();
ViewPropertyAnimator.animate(mFab).scaleX(0).scaleY(0).setDuration(200).start();
mFabIsShown = false;
}
}
关键点解析
- 视差效果:通过让背景图片和覆盖层以不同速度滚动实现
- 弹性范围计算:
flexibleRange
决定了动画效果的作用范围 - 兼容性处理:针对不同Android版本采用不同的动画实现方式
- 性能优化:使用
ViewHelper
确保动画流畅,避免过度绘制
实际应用建议
- 图片选择:建议使用高分辨率图片以获得更好的视觉效果
- 颜色搭配:确保覆盖层颜色与内容区域协调
- 性能测试:在低端设备上测试滚动流畅度
- 响应式设计:考虑不同屏幕尺寸和方向的适配
通过以上步骤,开发者可以轻松实现Material Design风格的弹性空间效果,为用户带来更加丰富的视觉体验。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考