RecyclerView實現帶有頭部的頂部懸浮置頂佈局
阿新 • • 發佈:2018-12-30
由於專案需求需要,需要一個帶有頭部的吸頂佈局,在網上搜索了好多實現辦法,都不太理想,最終使用對RecyclerView新增分割線的方式,重寫了RecyclerView的分割線來實現這個懸浮欄。效果比較簡單,但是比較實用,下面主要介紹實現過程。
首先先貼上效果圖:
1.第一步
首先要明白資料的資料結構,我們需要將資料分為幾組,每一組都是相同的一類,我這裡將資料分為了三組,分別為類別一,類別二,類別三然後建立SectionDecoration讓其繼承 RecyclerView.ItemDecoration,類內部的實現如下:
private PowerGroupListener mGroupListener;
/**
* 懸浮欄高度
*/
private int mGroupHeight = 80;
/**
* 是否靠左邊
* true 靠左邊
* false 不靠左
*/
private boolean isAlignLeft = true;
/**
通過建構函式將groupListener傳遞進來
*/
private SectionDecoration(PowerGroupListener groupListener) {
this.mGroupListener = groupListener;
}
PowerGroupListener 介面的實現:
package byd.com.byd.ceilingdemo.listener;
import android.view.View;
/**
* author:byd666 on 2017/12/2 15:13
*/
public interface PowerGroupListener {
//獲取每一組的組名
String getGroupName(int position);
//得到組View
View getGroupView(int position);
}
2.第二步
實現RecyclerView.ItemDecoration裡的兩個方法:(1)
getItemOffsets和(2)onDrawOver,這兩個方法是幹嘛的呢,(1)查找出RecyclerView的指定item的位移。(2)是給RecyclerView提供適當的裝飾到畫布上。由這個方法將任何內容在專案檢視繪製。具體實現:
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
//得到你新增的分割線View的位置
int pos = parent.getChildAdapterPosition(view);
//獲取組名
String groupId = getGroupName(pos);
if (groupId == null) {return;}
//只有是同一組的第一個才顯示懸浮欄
if (pos == 0 || isFirstInGroup(pos)) {
outRect.top = mGroupHeight;
}
}
獲取組名的方法:
/**
* 獲取組名
* @param position
* @return 組名
*/
private String getGroupName(int position) {
if (mGroupListener != null) {
return mGroupListener.getGroupName(position);
} else {
return null;
}
}
判斷是不是同一組的第一個:
/**
* 判斷是不是組中的第一個位置
* 根據前一個組名,判斷當前是否為新的組
*/
private boolean isFirstInGroup(int pos) {
if (pos == 0) {
return true;
} else {
String prevGroupId = getGroupName(pos - 1);
String groupId = getGroupName(pos);
return !TextUtils.equals(prevGroupId, groupId);
}
}
最重要的方法onDrawOver的實現:
程式碼中註釋很清楚,自行理解其繪製過程
@Override
public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDrawOver(c, parent, state);
//獲取單條的數目
int itemCount = state.getItemCount();
int childCount = parent.getChildCount();
//得出距離左邊和右邊的padding
int left = parent.getPaddingLeft();
int right = parent.getWidth() - parent.getPaddingRight();
//開始繪製
String preGroupName;
String currentGroupName = null;
for (int i = 0; i < childCount; i++) {
View view = parent.getChildAt(i);
int position = parent.getChildAdapterPosition(view);
preGroupName = currentGroupName;
currentGroupName = getGroupName(position);
if (currentGroupName == null || TextUtils.equals(currentGroupName, preGroupName))
{
continue;
}
int viewBottom = view.getBottom();
//top 決定當前頂部第一個懸浮Group的位置
int top = Math.max(mGroupHeight, view.getTop());
if (position + 1 < itemCount) {
//獲取下個GroupName
String nextGroupName = getGroupName(position + 1);
//下一組的第一個View接近頭部
if (!currentGroupName.equals(nextGroupName) && viewBottom < top) {
top = viewBottom;
}
}
//根據position獲取View
View groupView = getGroupView(position);
if (groupView == null){ return;}
ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, mGroupHeight);
groupView.setLayoutParams(layoutParams);
groupView.setDrawingCacheEnabled(true);
groupView.measure(
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
//指定高度、寬度的groupView
groupView.layout(0, 0, right, mGroupHeight);
groupView.buildDrawingCache();
Bitmap bitmap = groupView.getDrawingCache();
int marginLeft = isAlignLeft ? 0 : right - groupView.getMeasuredWidth();
c.drawBitmap(bitmap, left + marginLeft, top - mGroupHeight, null);
}
}
getGroupView的方法實現:
/**
* 獲取組View
* @param position
* @return 組名
*/
private View getGroupView(int position) {
if (mGroupListener != null) {
return mGroupListener.getGroupView(position);
} else {
return null;
}
}
3.第三步
為了方便初始化和使用,建立一個類部類,用來 初始化 listener,設定Group高度,確定其是否靠左邊:
public static class Builder {
SectionDecoration mDecoration;
private Builder(PowerGroupListener listener) {
mDecoration = new SectionDecoration(listener);
}
/**
* 初始化 listener
* @param listener
* @return
*/
public static Builder init(PowerGroupListener listener) {
return new Builder(listener);
}
/**
* 設定Group高度
* @param groutHeight 高度
* @return this
*/
public Builder setGroupHeight(int groutHeight) {
mDecoration.mGroupHeight = groutHeight;
return this;
}
/**
* 是否靠左邊
* true 靠左邊(預設)、false 靠右邊
* @param b b
* @return this
*/
public Builder isAlignLeft(boolean b){
mDecoration.isAlignLeft = b;
return this;
}
public SectionDecoration build() {
return mDecoration;
}
}
到這咱們就將分割線建立完成了,具體怎麼使用呢,
4. 第四步
具體的使用 ,為RecyclerView新增分割線即可。
/**
* 新增懸浮佈局
*/
private void initDecoration() {
SectionDecoration decoration = SectionDecoration.Builder
.init(new PowerGroupListener() {
@Override
public String getGroupName(int position) {
//獲取組名,用於判斷是否是同一組
if (dataList.size() > position) {
return dataList.get(position).getName();
}
return null;
}
@Override
public View getGroupView(int position) {
//獲取自定定義的組View
if (dataList.size() > position) {
View view = getLayoutInflater().inflate(R.layout.item_group, null, false);
((TextView) view.findViewById(R.id.tv)).setText(dataList.get(position).getName());
((ImageView) view.findViewById(R.id.iv)).setImageResource(dataList.get(position).getIcon());
return view;
} else {
return null;
}
}
})
//設定高度
.setGroupHeight(ScreenUtil.dip2px(this, 40))
.build();
mRecyclerView.addItemDecoration(decoration);
}
最後貼上xml佈局
<?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">
<android.support.design.widget.AppBarLayout
android:id="@+id/app_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true"
android:theme="@style/AppTheme.AppBarOverlay">
<android.support.design.widget.CollapsingToolbarLayout
android:id="@+id/toolbar_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true"
app:contentScrim="?attr/colorPrimary"
app:layout_scrollFlags="scroll|exitUntilCollapsed">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
app:layout_collapseMode="pin"
app:popupTheme="@style/AppTheme.PopupOverlay">
<TextView
android:id="@+id/title_tv"
android:layout_width="match_parent"
android:layout_height="56dp"
android:text="我是主標題"
android:gravity="center"
android:textSize="18sp"/>
<TextView
android:id="@+id/header_tv"
android:background="@color/colorAccent"
android:layout_width="match_parent"
android:layout_height="200dp"
android:textColor="#ffffff"
android:text="我是頭佈局"
android:gravity="center"
android:textSize="16sp"/>
</LinearLayout>
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<android.support.v7.widget.RecyclerView
android:id="@+id/rlv"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
android:layout_width="64dp"
android:layout_height="64dp"
android:layout_above="@+id/bottom_navigation"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_gravity="bottom|right"
android:layout_marginBottom="32dp"
android:layout_marginRight="32dp"
android:src="@mipmap/ic_launcher"
app:backgroundTint="@color/colorPrimary"
app:borderWidth="0dp"
app:elevation="8dp"
app:fabSize="normal" />
</android.support.design.widget.CoordinatorLayout>