CoordinatorLayout使用过程中遇到的问题

CoordinatorLayout使用过程中遇到的问题

CoordinatorLayout 一般配合 NestedScrollView 或者 RecyclerView 使用
常常用于ScrollView滚动,ToolBar伸缩,渐变等背景视差效果等

CoordinatorLayout + AppBarLayout + CollapsingToolbarLayout 配合使用才会有比较完美的效果
app:layout_behavior属性,只有CoordinatorLayout的直接子布局才能响应。

  • 无法联动CoordinatorLayout
    增加app:layout_behavior=”@string/appbar_scrolling_view_behavior”
    我这边用了一个自定义的FrameLayout结果在滚动的时候,AppBarLayout无法跟着联动,
    原因分析: 默认FrameLayout没有实现NestedScrollingChild接口,RecyclerView和NestedScrollView都实现了NestedScrollingChild接口,
    解决方法:ViewCompat.setNestedScrollingEnabled(frameLayout, true);
  • 监听AppBarLayout 位置移动 AppBarLayout.addOnOffsetChangedListener(this);
    运行后发现 SystemBarUtils.transparencyAndDark() 改变状态栏背景的代码会导致 AppBarLayout 重绘,从而重新调用 onOffsetChanged() 方法,导致出现循环
    解决方法增加去重等,也可以增加当前状态栏标志位进行过滤。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
@Override
public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset)
{
// 修改状态栏会触发 AppBarLayout重绘 onOffsetChanged() 方法,这里加个去重
if (mCurVerticalOffset == verticalOffset)
return;
mCurVerticalOffset = verticalOffset;
int offset = Math.abs(verticalOffset);
int scrollRange = appBarLayout.getTotalScrollRange();
if (offset <= scrollRange / 2)
{
mTitleOpenView.setVisibility(View.VISIBLE);
mTitleCloseView.setVisibility(View.GONE);
} else
{
SystemBarUtils.transparencyAndDark(TestActivity.this, true);
mTitleOpenView.setVisibility(View.GONE);
mTitleCloseView.setVisibility(View.VISIBLE);
}
}
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">

<in.srain.cube.views.ptr.PtrClassicFrameLayout
android:id="@+id/ptr"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
app:ptr_duration_to_close="300"
app:ptr_duration_to_close_header="2000"
app:ptr_keep_header_when_refresh="true"
app:ptr_pull_to_fresh="false"
app:ptr_ratio_of_header_height_to_refresh="1.2"
app:ptr_resistance="1.7">

<android.support.v7.widget.RecyclerView
android:id="@+id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"/>

</in.srain.cube.views.ptr.PtrClassicFrameLayout>

<android.support.design.widget.AppBarLayout
android:id="@+id/app_bar"
android:layout_width="match_parent"
android:layout_height="@dimen/app_bar_height"
android:fitsSystemWindows="true">

<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">

<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="56dp"
app:layout_collapseMode="pin"/>

</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
</android.support.design.widget.CoordinatorLayout>

注意:

1、AppBarLayout的直接子控件可以设置的属性:layout_scrollFlags

  • scroll|exitUntilCollapsed如果AppBarLayout的直接子控件设置该属性,该子控件可以滚动,向上滚动NestedScrollView出父布局(一般为CoordinatorLayout)时,会折叠到顶端,向下滚动时NestedScrollView必须滚动到最上面的时候才能拉出该布局
  • scroll|enterAlways:只要向下滚动该布局就会显示出来,只要向上滑动该布局就会向上收缩
  • scroll|enterAlwaysCollapsed:向下滚动NestedScrollView到最底端时该布局才会显示出来
  • 如果不设置改属性,则改布局不能滑动

2、CollapsingToolbarLayout,字面意思是折叠的toolbar,它确实是起到折叠作用的,可以把自己的自布局折叠 继承自framLayout,所以它的直接子类可以设置layout_gravity来控制显示的位置,它的直接子布局可以使用的属性:app:layout_collapseMode(折叠模式):可取的值如下:

  • pin:在滑动过程中,此自布局会固定在它所在的位置不动,直到CollapsingToolbarLayout全部折叠或者全部展开
  • parallax:视察效果,在滑动过程中,不管上滑还是下滑都会有视察效果,不知道什么事视察效果自己看gif图(layout_collapseParallaxMultiplier视差因子 0~1之间取值,当设置了parallax时可以配合这个属性使用,调节自己想要的视差效果)
  • 不设置:跟随NestedScrollView的滑动一起滑动,NestedScrollView滑动多少距离他就会跟着走多少距离

3、在 support 25 及以下版本中,滑动并不流畅。在 RecyclerView 滑动到中间部分然后下拉的时候,顶部的 AppBarLayout 并不会跟随向下滑动详见视频。
在 26.1.0 以上的版本官方已经修改了。这里提供两个方法解决这个问题,但效果还不太理想。
第一种,自定义 AppBarLayout.Behavior ,监听 RecyclerView 的滚动事件,调用 onNestedFling 处理
在 layout android.support.design.widget.AppBarLayout 标签中使用
app:layout_behavior=”xx.xxx.Behavior”
第二种,直接代码监听 RecyclerView 的滚动事件,当一条数据完全展示时,将 AppBarLayout 设置为展开状态。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
RecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener()
{
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState)
{
super.onScrollStateChanged(recyclerView, newState);
if (newState == RecyclerView.SCROLL_STATE_IDLE)
{
int firstVisiblePosition = manager.findFirstCompletelyVisibleItemPosition();
if (firstVisiblePosition == 0)
{
appbar.setExpanded(true, true);
}
}
}
});

4、其他方案
github 上有个库也是解决这个问题的 smooth-app-bar-layout
当然解决的原理跟本文相差甚远,有兴趣可以阅读下,我自己也有用过,不过这个库比较蛋疼的就是得为RecyclerView 设置一个 head item, 这个 item 的高度必须跟 AppBarLayout 一样的高度,所以对于 wrap_content 就比较麻烦了,当然,这个跟这个库的实现思路有关系。

com.android.support:appcompat-v7:25.3.0

com.android.support:appcompat-v7:26.0.0

5、项目地址

https://github.com/android9527/CollapsingToobarLayoutDemo