使用ItemDecoration自定義RecyclerView的分割線實現頭佈局
阿新 • • 發佈:2019-02-14
要實現的效果是仿微信交易記錄的頭佈局
之前做了一個demo,用來實現仿微信交易記錄,上面是日期和交易金額,下面是詳細記錄。滾動的時候隱藏標題,停止顯示標題。我以備註的形式來說明,很詳細。使用很簡單,mRecyclerView.addItemDecoration(new TitleItemDecoration(this,mContentList,mTitleList));//新增itemDecoration,是可以新增多個的
具體程式碼如下
public class TitleItemDecoration extends RecyclerView.ItemDecoration {
private List<String> mTitleList;
private List<String> mContentList;//存放內容的list,每月的收入和支出
private Paint mPaint;
private Rect mBounds;
private Activity mActivity;
private int mScreenWidth;
private int mDensity;
private boolean isScrolling = false ;
//下面這個方法是第一個呼叫的,用來給頭佈局留出空白的地方。
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
if (mContentList.isEmpty()) {
return;
}
int position = parent.getChildAdapterPosition(view);
if (position == 0) {
outRect.set(0, 50 * mDensity, 0, 0);//這個方法相當於是給item設定padding,,給title留出位置來。第一個條目肯定要title
} else if (position > 0) {
if (!mContentList.get(position).substring(0, 6).equals(mContentList.get(position - 1).substring(0, 6))) {
outRect.set(0, 50 * mDensity, 0, 0);//這個方法相當於是給item設定padding
} else {
outRect.set(0, 0, 0, 0);
}
}
}
//呼叫下面方法繪製頭佈局
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
int left = 10 * mDensity;//10dp的paddingLeft;
int right = 10 * mDensity;//10dp的paddingRight;
if (mContentList.isEmpty()) {
return;
}
for (int i = 0; i < parent.getChildCount(); i++) {
View child = parent.getChildAt(i);//這裡應該用這個方法獲取child,而onDrawOver不一樣
RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) child.getLayoutParams();
int position = layoutParams.getViewAdapterPosition();
// View child = parent.findViewHolderForLayoutPosition(i).itemView;//獲取第i個條目的view,如果用上面的方式,有可能為null
if (position > -1) {//重置後會變成-1,所以要加此判斷
if (position == 0) {
drawDecoration(c, left, position, child);
} else if (!mContentList.get(position).substring(0, 6).equals(mContentList.get(position - 1).substring(0, 6))) {
drawDecoration(c, left, position, child);
}
}
}
}
private void drawDecoration(Canvas c, int left, int i, View child) {
mPaint.setColor(Color.LTGRAY);
//top為第一個條目top-高度50,也就是從0開始
//
c.drawRect(0, child.getTop() - 50 * mDensity, mScreenWidth, child.getTop(), mPaint);
mPaint.setColor(Color.RED);
//下面一行,設定文字的區域範圍,引數1:文字內容也就是title,因為我寫的title和content的前6個字元是一樣的,所以就直接寫內容的前6字元了
//引數2:文字起始位置為0,引數3:文字終止位置,為文字長度,引數4,Rec是我們定義的mBounds
mPaint.getTextBounds(mContentList.get(i).substring(0, 6), 0, mContentList.get(i).substring(0, 6).length(), mBounds);
//下面一行,畫文字。引數1:文字內容,引數2:文字座標x,引數3:文字座標y,引數4,paint
// c.drawText(mContentList.get(i).substring(0,6),0,child.getTop()-mBounds.top,mPaint);
// 下一行解釋:引數2:文字離左邊距離,引數3,文字離上面距離,其中child.getTop是子控制元件距離上面高度,mBound.top是系統自帶的
// 偏移量,需要減去文字才能回到原點,-50*mDensity是一行的高度,因為文字不是從第一個子控制元件開始畫的而是從第一個子控制元件上面一行
// 開始畫的,(50-14)/2*mDensity是文字的padding,一行的高度減去文字高度,然後除以2就是文字距離上面的padding了
c.drawText(mContentList.get(i).substring(0, 6), left, child.getTop() - mBounds.top - 50 * mDensity + (50 - 14) / 2 * mDensity, mPaint);
}
public TitleItemDecoration(Activity activity, List<String> contentList, List<String> titleList) {
super();
mActivity = activity;
mTitleList = titleList;//注意titleList是肯定要用的,這裡因為我的ContentList內容各個條目的前6個字元等於titleList內容,所以直接用ContentList的前6個字元填充title了,就沒有用到titleListle了
mContentList = contentList;
init();
}
private void init() {
mPaint = new Paint();
mPaint.setAntiAlias(true);
mBounds = new Rect();
// WindowManager windowManager = mActivity.getWindowManager();
// mScreenWidth = windowManager.getDefaultDisplay().getWidth();
DisplayMetrics displayMetrics = mActivity.getResources().getDisplayMetrics();
mDensity = (int) displayMetrics.density;
mScreenWidth = displayMetrics.widthPixels;
mPaint.setTextSize(14*mDensity);
}
//下面方法是在onDraw之後執行的,那麼我們可以繪製一個佈局在最頂端,實現頂端一直有最新的頭
@Override
public void onDrawOver(final Canvas c, final RecyclerView parent, RecyclerView.State state) {
super.onDrawOver(c, parent, state);
parent.addOnScrollListener(new RecyclerView.OnScrollListener() {//RecyclerView可以新增多個滾動監聽,所以不會和外面的監聽衝突
//這裡監聽滾動的時候是否顯示頭佈局
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
isScrolling = false;
//該方法畫的不是在頂層,而是和條目重疊
} else {
isScrolling = true;
}
}
});
if (!isScrolling) {
addDecorationOver(parent, c);
}
}
private void addDecorationOver(RecyclerView parent, Canvas c) {
if (mContentList.isEmpty()) {
return;
}
//獲取第一個可見條目
LinearLayoutManager layoutManager = (LinearLayoutManager) parent.getLayoutManager();
int firstVisibleItemPosition = layoutManager.findFirstVisibleItemPosition();
// View child = parent.findViewHolderForAdapterPosition(firstVisibleItemPosition).itemView;//這裡要用此方法獲取child
int left = 10 * mDensity;//10dp的paddingLeft;
int right = 10 * mDensity;//10dp的paddingRight;
mPaint.setColor(Color.LTGRAY);
//top為第一個條目top-高度50,也就是從0開始
//
c.drawRect(0, 0, mScreenWidth, 50 * mDensity, mPaint);//直接畫在第一行,top一般是parent.getPaddintTop,我直接寫0了
mPaint.setColor(Color.RED);
mPaint.getTextBounds(mContentList.get(firstVisibleItemPosition).substring(0, 6), 0, mContentList.get(firstVisibleItemPosition).substring(0, 6).length(), mBounds);
c.drawText(mContentList.get(firstVisibleItemPosition).substring(0, 6), left, -mBounds.top + (50 - 14) / 2 * mDensity, mPaint);
}
}