分割線
分割線在app中是常常用到的,用ItemDecoration怎麼實現呢,其實上面padding改爲1dp就實現了分割線的效果,可是分割線的顏色只能是背景灰色,因此不能用這種方法。
要實現分割線效果須要 getItemOffsets()和 onDraw()2個方法,首先用 getItemOffsets給item下方空出必定高度的空間(例子中是1dp),而後用onDraw繪製這個空間
//分割線
public class DividerItemDecoration extends RecyclerView.ItemDecoration {
private Context mContext; //上下文
private int dividerHeight; //分割線的高度
private Paint mPaint; //畫筆
//自定義構造方法,在構造方法中初始化一些變量
public DividerItemDecoration(Context context){
mContext = context;
dividerHeight = 2; //context.getResources().getDimensionPixelSize(R.dimen.divider_height);
mPaint = new Paint();
mPaint.setColor(context.getResources().getColor(R.color.colorAccent)); //設置顏色
}
//設置padding
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
//outRect.bottom、left,right,top設置爲int值,設置每一項的padding
outRect.bottom =dividerHeight ;
}
//畫圖
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDraw(c, parent, state);
//獲取item個數
int childCount = parent.getChildCount();
//左右是固定的
int left = parent.getPaddingLeft();
int right = parent.getWidth() - parent.getPaddingRight() ;
//高度
for (int i = 0; i < childCount - 1; i++) {
View view = parent.getChildAt(i);
float top = view.getBottom();
float bottom = view.getBottom() + dividerHeight;
//畫圖
c.drawRect(left, top, right, bottom, mPaint);
}
}
}
效果以下:
如今不少電商app會給商品加上一個標籤,好比「推薦」,「熱賣」,「秒殺」等等,能夠看到這些標籤都是覆蓋在內容之上的,這就能夠用onDrawOver()來實現,咱們這裏簡單實現一個有趣的標籤
public class LeftAndRightTagDecoration extends RecyclerView.ItemDecoration {
private int tagWidth; //標籤的寬度
private Paint leftPaint; //左邊的畫筆
private Paint rightPaint; //右邊的畫筆
public LeftAndRightTagDecoration(Context context){
leftPaint =new Paint();
leftPaint.setColor(context.getResources().getColor(R.color.colorPrimary));
rightPaint = new Paint();
rightPaint.setColor(context.getResources().getColor(R.color.colorOrange));
//上面是畫筆的初始化,和設置顏色,下面是標籤寬度的獲取
tagWidth = context.getResources().getDimensionPixelSize(R.dimen.tag_width);
}
//繪製標籤
@Override
public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDrawOver(c, parent, state);
int childCount = parent.getChildCount();
for(int i=0;i<childCount;i++){
View child = parent.getChildAt(i);
int pos = parent.getChildAdapterPosition(child);
boolean isLeft = pos%2==0;
if(isLeft){
float left = child.getLeft();
float right = left+tagWidth;
float top = child.getTop();
float bottom =child.getBottom();
c.drawRect(left,top,right,bottom,leftPaint);
}else{
float right = child.getRight();
float left = right-tagWidth;
float top = child.getTop();
float bottom =child.getBottom();
c.drawRect(left,top,right,bottom,rightPaint);
}
}
}
}
section
這個是什麼呢,先看下咱們實現的效果
一看這個就很熟悉吧,手機上面的通信錄聯繫人,知乎日報都是這樣效果,能夠叫分組,也能夠叫section分塊 先無論它叫什麼。
這個怎麼實現呢? 其實和實現分割線是同樣的道理 ,只是否是全部的item都須要分割線,只有同組的第一個須要。
咱們首先定義一個接口給activity進行回調用來進行數據分組和獲取首字母
public interface DecorationCallback {
long getGroupId(int position);
String getGroupFirstLine(int position);
}
而後再來看咱們的ItemDecoration
public class SectionDecoration extends RecyclerView.ItemDecoration {
private DecorationCallback callback;
private TextPaint textPaint; //文字畫筆
private Paint paint; //普通畫筆
private int topGap; //padding_top
private Paint.FontMetrics fontMetrics;
//自定義構造函數
public SectionDecoration(Context context, DecorationCallback decorationCallback) {
Resources res = context.getResources();
this.callback = decorationCallback;
//畫筆
paint = new Paint();
paint.setColor(res.getColor(R.color.colorPrimary));
//文字畫筆,樣式設置
textPaint = new TextPaint();
textPaint.setTypeface(Typeface.DEFAULT_BOLD); //加粗
textPaint.setAntiAlias(true);
textPaint.setTextSize(70); //字體大小
textPaint.setColor(Color.BLACK); //字體顏色
textPaint.getFontMetrics(fontMetrics); //字體的材質
textPaint.setTextAlign(Paint.Align.LEFT); //字體的向左對齊
fontMetrics = new Paint.FontMetrics();
topGap = res.getDimensionPixelSize(R.dimen.sectioned_top);//32dp Padding——top的值
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
//視圖的位置
int pos = parent.getChildAdapterPosition(view);
long groupId = callback.getGroupId(pos);
if (groupId < 0) return;
if (pos == 0 || isFirstInGroup(pos)) {//同組的第一個才添加padding
outRect.top = topGap;
} else {
outRect.top = 0;
}
}
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDraw(c, parent, state);
int left = parent.getPaddingLeft();
int right = parent.getWidth() - parent.getPaddingRight();
int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
View view = parent.getChildAt(i);
int position = parent.getChildAdapterPosition(view);
long groupId = callback.getGroupId(position);
if (groupId < 0) return;
//分組的文本
String textLine = callback.getGroupFirstLine(position).toUpperCase();
if (position == 0 || isFirstInGroup(position)) {
float top = view.getTop() - topGap;
float bottom = view.getTop();
c.drawRect(left, top, right, bottom, paint);//繪製矩形
c.drawText(textLine, left, bottom, textPaint);//繪製文本
}
}
}
//是不是一個組
private boolean isFirstInGroup(int pos) {
if (pos == 0) {
return true;
} else {
long prevGroupId = callback.getGroupId(pos - 1);
long groupId = callback.getGroupId(pos);
return prevGroupId != groupId;
}
}
//接口實現分組的依據
public interface DecorationCallback {
long getGroupId(int position);
String getGroupFirstLine(int position);
}
}
能夠看到和divider實現同樣,都是重寫getItemOffsets()和onDraw()2個方法,不一樣的是根據數據作了處理。
在Activity中使用
mRecycleView.addItemDecoration(new SectionDecoration(this, new SectionDecoration.DecorationCallback() {
@Override
public long getGroupId(int position) {
return Character.toUpperCase(homeAdapter.mTitles[position].charAt(0));
}
@Override
public String getGroupFirstLine(int position) {
return homeAdapter.mTitles[position].substring(0, 1).toUpperCase();
}
}));
乾淨舒服,很多github相似的庫都是去adapter進行處理 侵入性太強 或許ItemDecoration是個更好的選擇,可插拔,可替換。
到這裏細心的人就會發現了,header不會動啊,我手機上的通信錄但是會隨的滑動而變更呢,這個能夠實現麼?
StickyHeader
這個東西怎麼叫我也不知道啊 粘性頭部?英文也有叫 pinned section 取名字真是個麻煩事。
先看下咱們簡單實現的效果
首先一看到圖,咱們就應該想到header不動確定是要繪製item內容之上的,須要重寫onDrawOver()方法,其餘地方和section實現同樣。
public class PinnedSectionDecoration extends RecyclerView.ItemDecoration {
private static final String TAG = "PinnedSectionDecoration";
private DecorationCallback callback;
private TextPaint textPaint;
private Paint paint;
private int topGap;
private Paint.FontMetrics fontMetrics;
public PinnedSectionDecoration(Context context, DecorationCallback decorationCallback) {
Resources res = context.getResources();
this.callback = decorationCallback;
paint = new Paint();
paint.setColor(res.getColor(R.color.colorAccent));
textPaint = new TextPaint();
textPaint.setTypeface(Typeface.DEFAULT_BOLD);
textPaint.setAntiAlias(true);
textPaint.setTextSize(80);
textPaint.setColor(Color.BLACK);
textPaint.getFontMetrics(fontMetrics);
textPaint.setTextAlign(Paint.Align.LEFT);
fontMetrics = new Paint.FontMetrics();
topGap = res.getDimensionPixelSize(R.dimen.sectioned_top);
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
int pos = parent.getChildAdapterPosition(view);
long groupId = callback.getGroupId(pos);
if (groupId < 0) return;
if (pos == 0 || isFirstInGroup(pos)) {
outRect.top = topGap;
} else {
outRect.top = 0;
}
}
@Override
public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDrawOver(c, parent, state);
int itemCount = state.getItemCount();
int childCount = parent.getChildCount();
int left = parent.getPaddingLeft();
int right = parent.getWidth() - parent.getPaddingRight();
float lineHeight = textPaint.getTextSize() + fontMetrics.descent;
long preGroupId, groupId = -1;
for (int i = 0; i < childCount; i++) {
View view = parent.getChildAt(i);
int position = parent.getChildAdapterPosition(view);
preGroupId = groupId;
groupId = callback.getGroupId(position);
if (groupId < 0 || groupId == preGroupId) continue;
String textLine = callback.getGroupFirstLine(position).toUpperCase();
if (TextUtils.isEmpty(textLine)) continue;
int viewBottom = view.getBottom();
float textY = Math.max(topGap, view.getTop());
if (position + 1 < itemCount) { //下一個和當前不同移動當前
long nextGroupId = callback.getGroupId(position + 1);
if (nextGroupId != groupId && viewBottom < textY ) {//組內最後一個view進入了header
textY = viewBottom;
}
}
c.drawRect(left, textY - topGap, right, textY, paint);
c.drawText(textLine, left, textY, textPaint);
}
}
}
好了,如今發現ItemDecoration有多強大了吧! 固然還有更多就須要你本身去發現了。