RecyclerView學習(四)----ItemDecoration實現的城市導航列表(下)

之前用RecyclerView實現了寫過一篇城市導航列表:

動手寫一個城市導航列表

關於自定義的導航條,滑動監聽,漢字轉拼音等零碎知識,大家可以查看我之前那篇博客。

今天主要說的是懸停列表的實現,之前的實現方式是每一個RecyclerView的item的佈局裏面都包含一個頭部佈局,然後判斷當前item和上一個item的頭部佈局裏的索引字母是否相同,來決定是否展示item的頭部佈局。這種實現方式顯得佈局冗餘,效率降低,而且不夠優雅。今天這裏我用ItemDecoration來實現城市列表的頭部懸停。

ItemDecoration是用來修飾RecyclerView裏的Item,ItemDecoration類主要有三個方法:

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)

這三個方法作用依次是:
onDraw():可以實現類似繪製背景的效果,item的內容在上面

onDrawOver():可以繪製在內容的上面,覆蓋item的內容

getItemOffsets():可以實現類似padding的效果

onDraw()的繪製會先於子ItemView的繪製,如果你在onDraw()方法中繪製的東西在子ItemView邊界內,就會被ItemView蓋住,所以我們一般先調用getItemOffsets()創造空間。而onDrawOver()會在子ItemView繪製之後再繪製,繪製的內容會覆蓋在子ItemView上。

執行順序是先執行ItemDecoration的onDraw()、再執行子ItemView的onDraw()、再執行ItemDecoration的onDrawOver()。

下面我們通過改造這個例子來分析這三個方法:

1.getItemOffsets()

我移除了之前項目裏用到的懸停,側邊導航欄與滑動監聽還保留,看下現在的效果:

這裏寫圖片描述

ok,現在我們來自定義一個ItemDecoration:

public class StickyDecoration extends RecyclerView.ItemDecoration {
    private int topHeight;

    public StickyDecoration(Context context) {
        Resources res = context.getResources();
        topHeight = res.getDimensionPixelSize(R.dimen.top);
    }

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        super.getItemOffsets(outRect, view, parent, state);
        int position = parent.getChildAdapterPosition(view);
        if (isFirstInGroup(position)) {
            outRect.top = topHeight;
        } else {
            outRect.top = 0;
        }

    }


    private boolean isFirstInGroup(int position) {
        boolean isFirst;
        if (position == 0) {
            isFirst = true;
        } else {
            if (CityActivity.cityList.get(position).getFirstPinYin().
                    equals(CityActivity.cityList.get(position - 1).getFirstPinYin())) {
                isFirst = false;
            } else {
                isFirst = true;
            }
        }
        return isFirst;
    }
}

繼承自RecyclerView.ItemDecoration,然後重寫構造方法,並且重寫getItemOffsets()方法。進行判斷,該item如果是第一次出現該字母,就給這個item的上方繪製一個40dp的高度,否則就不處理。Activity中使用:

  recyclerView.addItemDecoration(new StickyDecoration(getApplicationContext()));

最後我們看一下效果:

這裏寫圖片描述

可以看到,在每一個字母首次出現的item上,都多了一個40dp的空間,這裏用來放我們的索引字母。

2.onDraw()

onDraw()可以實現類似繪製背景的效果,item的內容在上面,我們通過getItemOffsets()創造了空間,然後用onDraw()方法繪製字母索引:


    private TextPaint textPaint;
    private Paint paint;
    private int topHeight;

    public StickyDecoration(Context context) {
        Resources res = context.getResources();

        paint = new Paint();
        paint.setColor(res.getColor(R.color.colorAccent));
        textPaint = new TextPaint();
        textPaint.setAntiAlias(true);
        textPaint.setTextSize(60);
        textPaint.setColor(Color.WHITE);
        topHeight = res.getDimensionPixelSize(R.dimen.top);
    }

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        super.getItemOffsets(outRect, view, parent, state);
        int position = parent.getChildAdapterPosition(view);
        if (isFirstInGroup(position)) {
            outRect.top = topHeight;
        } 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);
            String textLine = CityActivity.cityList.get(position).getFirstPinYin();
            if (position == 0 || isFirstInGroup(position)) {
                float top = view.getTop() - topHeight;
                float bottom = view.getTop();
                c.drawRect(left, top, right, bottom, paint);//繪製紅色矩形
                c.drawText(textLine, left + 30, bottom - 30, textPaint);//繪製文本
            }
        }
    }

這裏的onDraw()方法與自定義View的onDraw()一樣,只不過這裏繪製的對象是RecyclerView子item的view。我們看下最後實現的效果:

這裏寫圖片描述

OK,在首次出現該字母的item上都出現了字母索引,接下來就是懸停的實現了。

3.onDrawOver()

onDrawOver()方法會繪製在內容的上面,覆蓋item的內容,所以我們拿來做懸停很合適。

    @Override
    public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
        super.onDrawOver(c, parent, state);
        int position = ((LinearLayoutManager) (parent.getLayoutManager())).findFirstVisibleItemPosition();
        int left = parent.getPaddingLeft();
        int right = parent.getWidth() - parent.getPaddingRight();
        c.drawRect(left, 0, right, topHeight, paint);//繪製紅色矩形
        String text = CityActivity.cityList.get(position).getFirstPinYin();
        c.drawText(text, 30, topHeight - 30, textPaint);//繪製文本
    }

實現的代碼其實很簡單,拿到RecyclerView可見區域第一個item的position,得到大寫字母。依次繪製紅色背景與文字即可。最後實現的效果如下:

這裏寫圖片描述

OK,和之前實現的效果還是沒什麼區別的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章