RecyclerView实现粘性头部

最近在研究CoordinatorLayout和Behavior的相关特性,在泡网看到Behavior实现的简书快速返回效果,觉得实现相对简单,突然灵机一动,想到用Behavior来做RecyclerView的粘性头部,通过不断尝试发现Behavior中获得的滑动距离不大准确,并不是我想要的。于是果断放弃此路,通过监听RecyclerView的滑动来实现。

效果图

实现思路

1.布局结构

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


<!--toolbar-->
<android.support.design.widget.AppBarLayout
android:id="@+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content">

<android.support.v7.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:navigationIcon="?attr/homeAsUpIndicator"
app:title="title" />

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

<android.support.v7.widget.RecyclerView
android:id="@+id/rv_behavior"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#f2f2f2"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
/>


<TextView
android:id="@+id/sticky_header"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="20dp"
android:background="#e89112"
android:gravity="center"
android:text="sticky header"
android:textColor="@android:color/white"

/>


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

RecyclerView普通header

1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="wrap_content">

<ImageView
android:layout_width="match_parent"
android:layout_height="200dp"
android:src="@mipmap/girl"
android:scaleType="centerCrop"
/>

</LinearLayout>

RecyclerView sticky header

1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="wrap_content">

<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="20dp"
/>

</LinearLayout>

RecyclerView item

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="wrap_content">

<TextView
android:layout_marginBottom="1dp"
android:id="@+id/tv_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dp"
android:background="@android:color/white"
/>

</LinearLayout>

ps:RecyclerView中包括三部分,普通header、sticky header、item;其中sticky header是假view,只是为了占个高度。
真正的sticky header在CoordinatorLayout中。

2.滑动处理
主要就是添加滑动监听,把RecyclerView滑动距离算出来,然后与最大滑动距离比较,即普通header的高度,如果差值>0,则滑动此距离,<0就不滑动。这样就可以把某个header定在一处。

主要代码如下

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

//200dp是普通header的高度
maxDist=ScreenUtils.dip2px(this,200);
stickyView=findViewById(R.id.sticky_header);
recyclerView= (RecyclerView) findViewById(R.id.rv_behavior);
initData();
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(new StickyHeaderAdapter(this,data));


appbar= (AppBarLayout) findViewById(R.id.appbar);
appbar.post(new Runnable() {
@Override
public void run() {
barHeight= appbar.getMeasuredHeight();
stickyView.setTranslationY(maxDist+barHeight);
}
});

recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
}

@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
totalChange+=dy;
Log.e(TAG,"totalChange:"+totalChange);
int tranY=Math.max(0,maxDist-totalChange);
//移动距离超过maxDist,就定在0处 ,barHeight是appbar的高度,必须加上
stickyView.setTranslationY(tranY+barHeight);

}
});

总结

其实实现过程还是蛮容易的,但还存在一些问题,比如要实现多个sticky header就比较困难。FlexibleAdapter这个开源项目就考虑得很全面,很多功能都有。下一篇博文将介绍此文章的姊妹篇,ScrollView实现粘性头部,敬请期待!

参考资料

  1. http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2016/0224/3991.html
  2. http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0913/3447.html