吸頂效果的另一種實現
前面介紹過一篇文章,是使用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