目录
- 前言
- 效果图
- 快速上手
- CollapsingToolbarLayout折叠模式
- AppBarLayout滚动方式
- CoordinatorLayout配合Snackbar
- 自定义伸缩头部
- 最后
前言
垂直滚动这个操作, 水果机一代就有了, 是移动App最常见的内容展示视图了, 之前也是写了RecyclerView的内容, 这次再补充伸缩头部的实现. 港真, 伸缩头部是那种看到第一眼就会爱上的视图效果, 好看又简洁.
效果图
先上案例的效果图, 有兴趣再看下去:
快速上手
先来实操一下, 看看从默认的滚动模板(Scrolling Activity)到效果图要几步.
- 首先, 在Toolbar上面加入ImageView, 参数之后再说明.
<android.support.design.widget.CollapsingToolbarLayout
android:id="@+id/toolbar_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
app:contentScrim="?attr/colorPrimary"
app:layout_scrollFlags="scroll|exitUntilCollapsed"
app:toolbarId="@+id/toolbar">
<ImageView
android:id="@+id/iv_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:contentDescription="@string/desc"
android:scaleType="centerCrop"
app:layout_collapseMode="pin" />
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_collapseMode="pin" />
</android.support.design.widget.CollapsingToolbarLayout>
- 然后在java代码中使用Glide加载图片.
导包:
implementation 'com.github.bumptech.glide:glide:3.7.0'
// 加载图片
ImageView ivMain = (ImageView) findViewById(R.id.iv_main);
Glide.with(this).load(R.drawable.p5).into(ivMain);
- 看下效果:
发现两个问题, 由于背景是白色, 标题栏字体颜色要变成黑色, 默认就是黑色, 所以就是删除xml中的主题设置. 当然, 如果你是深色背景, 这里就无需动它. 然后标题栏需要变成透明的.
将标题栏设置透明色
那由于5.0之前是不能变的, 将styles.xml从5.0区分开, 5.0之前什么都不做, 之后版本设置标题栏为透明色. 现在styles.xml中写入:
<style name="MyTheme" parent="AppTheme" />
然后复制styles.xml:
删除重复部分:
<resources>
<style name="MyTheme" parent="AppTheme">
<item name="android:statusBarColor">@android:color/transparent</item>
</style>
</resources>
- 然后在配置文件中设置新主题, 顺带改下标题名称, 再次运行看下效果:
// 设置标题
ActionBar supportActionBar = getSupportActionBar();
if (supportActionBar != null) {
supportActionBar.setTitle(UIUtil.getString(R.string.p5));
}
这样就完成了.
CollapsingToolbarLayout折叠模式
app:layout_collapseMode="parallax"
app:layout_collapseMode="pin"
app:layout_collapseMode="none"
从xml中的参数说吧, 来看CollapsingToolbarLayout的折叠模式. 下面我也是截了一段官方文档内容.
COLLAPSE_MODE_OFF
int COLLAPSE_MODE_OFF
The view will act as normal with no collapsing behavior.
Constant Value: 0 (0x00000000)
COLLAPSE_MODE_PARALLAX
int COLLAPSE_MODE_PARALLAX
The view will scroll in a parallax fashion. See setParallaxMultiplier(float) to change the multiplier used.
Constant Value: 2 (0x00000002)
COLLAPSE_MODE_PIN
int COLLAPSE_MODE_PIN
The view will pin in place until it reaches the bottom of the CollapsingToolbarLayout.
Constant Value: 1 (0x00000001)
列个表再看下:
参数 | 效果 |
---|---|
none | 视图将正常运行, 没有折叠行为 |
pin | 视图将固定到位, 直到它到达CollapsingToolbarLayout的底部 |
parallax | 视图将以视差方式滚动 |
是不是该怎么懵还是怎么懵, 来看效果图:
注意看人物的脚, parallax模式下人物最终滑动到身体部位消失. pin模式下, 人物滑到脚部位消失. 也就是说, pin模式下, 下面的滚动视图和图片是同步滑动的, 但是这样的观感其实不好. parallax则改进了这一点, 看起来很和谐, 尽管两者不再同步, 这就是翻译后说的以视差方式滚动了.
AppBarLayout滚动方式
滚动方式主要依靠参数组合(scroll必须要), 列个表再看下效果图, 官方文档就不截了.
参数 | 效果 |
---|---|
scroll | 视图将滚动与滚动事件直接相关. 需要设置此标志才能使任何其他标志生效. 如果在此之前的任何兄弟视图没有此标志, 则此值无效. |
exitUntilCollapsed | 退出(滚动屏幕)时, 视图将滚动直到“折叠”. 折叠高度由视图的最小高度定义。 |
snap | 在滚动结束时, 如果视图仅部分可见, 则它将被捕捉并滚动到其最近的边缘. |
enterAlways | 当进入(在屏幕上滚动)时, 无论滚动视图是否也在滚动, 视图都将滚动任何向下滚动事件. 这通常被称为“快速返回”模式. |
enterAlwaysCollapsed | 'enterAlways'的另一个标志, 它修改返回的视图, 最初只回滚到它的折叠高度. 一旦滚动视图到达其滚动范围的末尾, 该视图的其余部分将滚动到视图中. 折叠高度由视图的最小高度定义. |
- 看看单scroll的情况:
app:layout_scrollFlags="scroll"
可以看到整个滚上去了, 没有保留Toolbar.
那我现在用的是
app:layout_scrollFlags="scroll|exitUntilCollapsed"
, 效果大家也见过了.- 喜闻乐见的吸附效果,
app:layout_scrollFlags="scroll|snap"
, 例如, 还剩下25%没滚完, 松手就自己滚出去; 如果还有75%没滚完, 松手直接全部显示. 但是我感觉体验不好, 会让人有着操作不灵敏的错觉.
- 快速返回, 就是把滚出去的部分快速显示出来, 可以对比之前的返回速度来看:
app:layout_scrollFlags="scroll|enterAlways"
- 对比快速返回来看, 这个相对柔和一些, 可以理解为二段式的快速返回, 总之就是返回没有enterAlways那么迅速:
app:layout_scrollFlags="scroll|enterAlwaysCollapsed"
CoordinatorLayout配合Snackbar
先来看看自带的点击悬浮按钮的效果:
不让悬浮按钮吸附在Toolbar上, 将它放置到底部, 再看下效果:
android:layout_gravity="end|bottom"
如果不是CoordinatorLayout, 可就没有这种效果了哦.
自定义伸缩头部
再来看一个改动更大, 更自定义的. 先上效果图:
相比于之前的, 最大的变化在于对滚动幅度的监听. 依据滚动幅度变化Toolbar内容.
布局文件
先来看下主布局文件的变化, Toolbar包含了两个布局文件, 相互切换. 然后展开部分由之前的ImageView变成了一个布局文件, 这里要注意
app:contentInsetLeft="0dp"
,app:contentInsetStart="0dp"
, 这个就像html的默认边距一样, 需要清零. 不写的话左侧有默认的边距.
<android.support.design.widget.CollapsingToolbarLayout
android:id="@+id/toolbar_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
app:layout_scrollFlags="scroll|exitUntilCollapsed"
app:toolbarId="@+id/toolbar">
<include
layout="@layout/open_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="?attr/actionBarSize"
app:layout_collapseMode="parallax" />
<android.support.v7.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:contentInsetLeft="0dp"
app:contentInsetStart="0dp"
app:layout_collapseMode="pin">
<include
android:id="@+id/toolbar_open"
layout="@layout/toolbar_open" />
<include
android:id="@+id/toolbar_close"
layout="@layout/toolbar_close" />
</android.support.v7.widget.Toolbar>
</android.support.design.widget.CollapsingToolbarLayout>
三个小的布局代码我就贴一个做栗子:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/colorPrimary">
<ImageView
android:id="@+id/iv_first"
android:layout_width="@dimen/twenty_four_dp"
android:layout_height="@dimen/twenty_four_dp"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_centerVertical="true"
android:layout_marginLeft="@dimen/twenty_dp"
android:layout_marginStart="@dimen/twenty_dp"
android:contentDescription="@string/desc"
android:class="lazyload" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC" data-original="@android:drawable/btn_star_big_on" />
<ImageView
android:id="@+id/iv_second"
android:layout_width="@dimen/twenty_four_dp"
android:contentDescription="@string/desc"
android:layout_height="@dimen/twenty_four_dp"
android:layout_centerVertical="true"
android:layout_marginLeft="@dimen/twenty_dp"
android:layout_marginStart="@dimen/twenty_dp"
android:layout_toEndOf="@+id/iv_first"
android:layout_toRightOf="@+id/iv_first"
android:class="lazyload" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC" data-original="@android:drawable/btn_star_big_on" />
<ImageView
android:id="@+id/iv_third"
android:layout_width="@dimen/twenty_four_dp"
android:layout_height="@dimen/twenty_four_dp"
android:contentDescription="@string/desc"
android:layout_centerVertical="true"
android:layout_marginLeft="@dimen/twenty_dp"
android:layout_marginStart="@dimen/twenty_dp"
android:layout_toEndOf="@+id/iv_second"
android:layout_toRightOf="@+id/iv_second"
android:class="lazyload" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC" data-original="@android:drawable/btn_star_big_on" />
<ImageView
android:id="@+id/iv_forth"
android:layout_width="@dimen/twenty_four_dp"
android:contentDescription="@string/desc"
android:layout_height="@dimen/twenty_four_dp"
android:layout_centerVertical="true"
android:layout_marginLeft="@dimen/twenty_dp"
android:layout_marginStart="@dimen/twenty_dp"
android:layout_toEndOf="@+id/iv_third"
android:layout_toRightOf="@+id/iv_third"
android:class="lazyload" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC" data-original="@android:drawable/btn_star_big_on" />
<ImageView
android:layout_width="@dimen/twenty_four_dp"
android:layout_height="@dimen/twenty_four_dp"
android:layout_centerVertical="true"
android:layout_marginLeft="@dimen/twenty_dp"
android:contentDescription="@string/desc"
android:layout_marginStart="@dimen/twenty_dp"
android:layout_toEndOf="@+id/iv_forth"
android:layout_toRightOf="@+id/iv_forth"
android:class="lazyload" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC" data-original="@android:drawable/btn_star_big_on" />
<View
android:id="@+id/toolbar_close_mask"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
要点在最后的View, 这是一个遮罩, 依据滚动幅度变化其透明度起到遮罩效果.
AppBarLayout.OnOffsetChangedListener
官方文档写的很简单, 使用起来也不难. 添加implements AppBarLayout.OnOffsetChangedListener. 实现public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset)方法.
@Override
public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
int offset = Math.abs(verticalOffset);
int scrollRange = appBarLayout.getTotalScrollRange();
if (offset <= scrollRange / 2) {
mToolbarOpen.setVisibility(View.VISIBLE);
mToolbarClose.setVisibility(View.GONE);
float openPer = (float) offset / (scrollRange / 2);
int openAlpha = (int) (255 * openPer);
mToolbarOpenMask.setBackgroundColor(Color.argb(openAlpha, 48, 63, 159));
} else {
mToolbarClose.setVisibility(View.VISIBLE);
mToolbarOpen.setVisibility(View.GONE);
float closePer = (float) (scrollRange - offset) / (scrollRange / 2);
int closeAlpha = (int) (255 * closePer);
mToolbarCloseMask.setBackgroundColor(Color.argb(closeAlpha, 48, 63, 159));
}
float per = (float) offset / scrollRange;
int alpha = (int) (255 * per);
mContentMask.setBackgroundColor(Color.argb(alpha, 48, 63, 159));
}
前面也说了, 就是变化遮罩透明度, 这个颜色是对应了布局中设置的颜色的, 否则过渡效果就不对了. 可以用下PS将colors.xml中6位颜色变成rgb填入.
最后
看到这里也很不容易啦(手动比心). 喜欢记得点赞, 有意见或者建议评论区见, 暗中关注我也是可以的哦~
共同学习,写下你的评论
评论加载中...
作者其他优质文章