android recycleView 簡單使用二---分割線

轉自:https://www.jianshu.com/p/b46a4ff7c10agit

RecyclerView沒有像以前ListView提供divider屬性,而是提供了方法github

recyclerView.addItemDecoration()

其中ItemDecoration須要咱們本身去定製重寫,一開始可能有人會以爲麻煩很差用,最後你會發現這種可插拔設計不只好用,並且功能強大。app

ItemDecoration類主要是三個方法:ide

public void onDraw(Canvas c, RecyclerView parent, State state)
public void onDrawOver(Canvas c, RecyclerView parent, State state)
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state)

官方源碼雖然都寫的很清楚,但還很多小夥伴不知道怎麼理解,怎麼用或用哪一個方法,下面我畫個簡單的圖來幫大家理解一下。函數

 

首先咱們假設綠色區域表明的是咱們的內容,紅色區域表明咱們本身繪製的裝飾,能夠看到:字體

圖1:表明了getItemOffsets(),能夠實現相似padding的效果this

圖2:表明了onDraw(),能夠實現相似繪製背景的效果,內容在上面spa

圖3:表明了onDrawOver(),能夠繪製在內容的上面,覆蓋內容設計

注意上面是我我的從應用角度的見解,事實上實現上面的效果可能三個方法每一個方法均可以實現。只不過這種方法更好理解。code

下面是咱們沒有添加任何ItemDecoration的界面

 

padding

從前面的圖能夠看到實現這個效果,須要重寫getItemOffsets方法。

//分割線
public class DividerItemDecoration extends RecyclerView.ItemDecoration {
    @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值
        outRect.bottom =100 ;
        outRect.left =50 ;
    }
}

分割線

分割線在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); } } } }

效果以下

 

組合

不要忘記的是ItemDecoration是能夠疊加的

//ItemDecoration分割線
 mRecycleView.addItemDecoration(new DividerItemDecoration(this));
//標籤
 mRecycleView.addItemDecoration(new LeftAndRightTagDecoration(this));

效果以下:

 

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 取名字真是個麻煩事。

先看下咱們簡單實現的效果

 
stickyheader

首先一看到圖,咱們就應該想到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有多強大了吧! 固然還有更多就須要你本身去發現了。

相關文章
相關標籤/搜索