前面介绍过一篇文章,是使用ItemDecoration来实现吸顶效果,使用起来很解耦,简单,方便,但是优缺点是拓展性比较差,今天就通过另一种方式来实现吸顶效果,并且吸顶栏可以高度制定布局和交互,步入正题,下面来实现它,先看看效果图:
一、实现原理
头部的内容位于Recycleview上面,监听Recycleview的滑动,当Recycleview的分组item顶部接触到头部的内容布局底部时候,浮层随处Recycleview的上滑而上推,当分组完全到达顶部时候,浮层复位,这样吸顶效果就产生了,依据这个思路来实现吸顶效果。
二、布局文件实现
1、抽取公共的吸顶布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/sticky_text"
android:layout_width="match_parent"
android:layout_height="@dimen/head_top"
android:background="@color/bg_header"
android:gravity="center_vertical"
android:paddingLeft="@dimen/sticky_text_margin"/>
</LinearLayout>
2、MainActivity
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="cn.com.stickyrecycleview.MainActivity">
<!--内容栏-->
<android.support.v7.widget.RecyclerView
android:id="@+id/recycleview"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<!--吸顶栏-->
<include layout="@layout/sticky_layout"/>
</FrameLayout>
三、Adapter实现
1、布局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<!--分组栏-->
<include layout="@layout/sticky_layout"/>
<!--内容栏-->
<TextView
android:id="@+id/tv_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dp"
android:text="@string/app_name"/>
</LinearLayout>
布局文件里面包含两个View,一个是分组的View,一个是内容item的View,需要注意的是分组的View一定要和吸顶的View一致,这里为了方便期间,都设置为一个TextView。当需要展示分组的是让分组的View为VISIBLE,否则GONE掉。
2、onBindViewHolder
//无吸顶
public static final int NONE_STICKY_VIEW = 0;
//有吸顶
public static final int HAS_STICKY_VIEW = 1;
//第一个
public static final int FIRST_STICKY_VIEW = 2;
onBindViewHolder是核心处理方法,如果是第0个,肯定是吸顶,设置tag:FIRST_STICKY_VIEW。如果是其他的,当前的分组依据和上一个一致,则认为是同一组,不展示分组item,设置tag:NONE_STICKY_VIEW,否则是不同组,展示分组item,设置tag:HAS_STICKY_VIEW。最后通过ContentDescription方法来记录并获取要吸顶展示的信息。详细如下备注:
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
DataBean dataBean = mDataList.get(position);
if (position == 0) {
//第0个展示吸顶栏,设置tag:FIRST_STICKY_VIEW
holder.stickyText.setVisibility(View.VISIBLE);
holder.stickyText.setText(dataBean.data);
holder.itemView.setTag(FIRST_STICKY_VIEW);
} else {
if (!TextUtils.equals(dataBean.data, mDataList.get(position - 1).data)) {
//和上一个分组的依据不同,展示吸顶栏,设置tag:HAS_STICKY_VIEW
holder.stickyText.setVisibility(View.VISIBLE);
holder.stickyText.setText(dataBean.data);
holder.itemView.setTag(HAS_STICKY_VIEW);
} else {
//其他的,隐藏分组栏
holder.stickyText.setVisibility(View.GONE);
holder.itemView.setTag(NONE_STICKY_VIEW);
}
}
holder.textView.setText(dataBean.des);
// ContentDescription 用来记录并获取要吸顶展示的信息
holder.itemView.setContentDescription(dataBean.data);
}
四、滑动监听
最后需要通过addOnScrollListener方法监听Recycleview的滑动,来控制吸顶变化,原理看备注:
recycleview.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
//找到RecyclerView的item中,和RecyclerView的getTop 向下相距5个像素的那个item,
//该方法是根据坐标获取item的View,坐标点是以RecyclerView控件作为坐标轴,并不是以屏幕左上角作为坐标原点。
View stickyInfoView = recyclerView.findChildViewUnder(stickyText.getMeasuredWidth() / 2, 5);
if (stickyInfoView != null && stickyInfoView.getContentDescription() != null) {
//设置吸顶栏内容
stickyText.setText(String.valueOf(stickyInfoView.getContentDescription()));
}
//找到固定在顶部的View的下面一个item的View
View transInfoView = recyclerView.findChildViewUnder(stickyText.getMeasuredWidth() / 2, stickyText.getMeasuredHeight() + 1);
if (transInfoView != null && transInfoView.getTag() != null) {
//获取该View的tag
int transViewStatus = (int) transInfoView.getTag();
int dealtY = transInfoView.getTop() - stickyText.getMeasuredHeight();
if (transViewStatus == MyAdapter.HAS_STICKY_VIEW) {
if (transInfoView.getTop() > 0) {
//最上面的itemView没滑出屏幕,给顶部的View设置,
//注意setTranslationY移动的ViewGroup,而scrollTo()/scrollBy()移动的是View的内容,如文字、图片等
stickyText.setTranslationY(dealtY);
} else {
//最上面的itemView滑出屏幕,顶部的复位
stickyText.setTranslationY(0);
}
} else if (transViewStatus == MyAdapter.NONE_STICKY_VIEW) {
//如果是没有分组,即无吸顶的item,则设置顶部的View移动为0,保持原位置
stickyText.setTranslationY(0);
}
}
}
});
源码地址:https://github.com/yoonerloop/StickyViewLayout