1. 程式人生 > >Recycleview模仿瑞幸咖啡菜單物品列表

Recycleview模仿瑞幸咖啡菜單物品列表

ood ont ret 效果圖 recycler lda over protect protected

  • 首先得明白一點Recycleview的ItemDecoration非常強大,你可以使用它實現listview的分割線,懸浮窗,甚至一些非常炫的動畫。
  • 先看下效果圖
    技術分享圖片
  • ItemDecoration

    • onDraw():和普通view的onDraw差不多,就是繪制在畫布上繪制東西。
    • onDrawOver():就是基於onDraw上再次繪制點東西,over的意思。
    • getItemOffsets():Recycleview可以通過他拿到每一個item的間距,所以只需要控制這個間距,並在間距裏利用onDrawOver再繪制你想繪制的東西。

      實現這個的思路,我們只需要在指定的的行數通過getItemOffsets預留出我們要空出的高度,然後通過onDrawOver繪制出你所希望的view即可。


    一:手動構造數據格式,如下,返回list

    Goods goods1 = new Goods("人氣TOP", "正果拿鐵1", "Y27", "默認:大/單糖/熱");
    Goods goods99 = new Goods("人氣TOP", "正果拿鐵2", "Y27", "默認:大/單糖/熱");
    Goods goods91 = new Goods("人氣TOP", "正果拿鐵3", "Y27", "默認:大/單糖/熱");
    Goods goods2 = new Goods("大師咖啡", "拿鐵", "Y27", "默認:大/單糖/熱");
    Goods goods3 = new Goods("大師咖啡", "香草拿鐵", "Y24", "默認:大/單糖/熱");
    Goods goods4 = new Goods("大師咖啡", "焦糖拿鐵", "Y26", "默認:大/半糖/熱");
    List<Goods> list = new ArrayList<>();

    二:書寫自己的ItemDecoration

    • getItemOffsets預留空間,只需要在每個數組的第一個數據預留一個高度,比如第一個人氣TOP,第一個大師咖啡。
    第一個必須預留,當前位置的name和前一個不相等則為預留空間
      @Override
    public boolean isParent(int position) {
        if (position == 0) return true;
            if (!list.get(position).getType().equals(list.get(position - 1).getType()))
                return true;
        return false;
    }
    //是否為當前最後一個item
    protected boolean lastOneInGroup(int position) {
            String parentName = mDecorListener.parentName(position);
            String nextGroupName;
            try {
                nextGroupName = mDecorListener.parentName(position + 1);
            } catch (Exception e) {
                nextGroupName = null;
            }
            if (nextGroupName == null) {
                return false;
            }
            return !TextUtils.equals(parentName, nextGroupName);//與下一行的name不一樣說明當前是最後一行
        }
    
    由上isParent判斷是第一個的返回你要預留的高度大小,否則為不需要空間0
     @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 (parent.getLayoutManager() instanceof LinearLayoutManager && mDecorListener.isParent(position)) {
                outRect.top = decorationHeight;
                return;
            }
            outRect.top = 0;
        }
    
    • 在預留的空間上畫上你的view
     @Override
        public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
            super.onDrawOver(c, parent, state);
            final int itemCount = state.getItemCount(); 全部item的數量
            final int childCount = parent.getChildCount(); 可看見的排除懸停的分割線的個數
            final int left = parent.getPaddingLeft();
            final int right = parent.getWidth() - parent.getPaddingRight();
            for (int i = 0; i < childCount; i++) {
                View childView = parent.getChildAt(i);
                int position = parent.getChildAdapterPosition(childView);//就是當前可見每一行的position,從0開始
                //默認第一個就是有個Group
                if (mDecorListener.isParent(position) || i == 0) {//中第一個位置和可見的第一個才有這個懸停
                    //繪制懸浮,
                    int bottom = Math.max(decorationHeight, (childView.getTop() + parent.getPaddingTop()));
                    //決定當前頂部第一個懸浮Group的bottom,拿到item高度和規定高度對比,只是選擇一個合適的高度定義分割線
                    if (position + 1 < itemCount) {
                        //下一組的第一個View接近頭部
                        int viewBottom = childView.getBottom();
                        if (lastOneInGroup(position) && viewBottom < bottom) {
                            bottom = viewBottom;                    //如果這個關掉,會覆蓋,頂上去效果失去,其實viewBottom逐漸變為0,這樣動態的放置即將消失的懸浮攔,看上去就是下一個懸浮攔頂上來的
                        }
                    }
                    drawDecoration(c, position, left, right, bottom, parent);
                    stickyHeaderPosArray.put(position, bottom);
                } 
            }
        }
    
        private void drawDecoration(Canvas c, int position, int left, int right, int bottom, RecyclerView parent) {
            c.drawRect(left, bottom - decorationHeight, right, bottom, mGroutPaint);
            Paint.FontMetrics fm = mTextPaint.getFontMetrics();
            //文字豎直居中顯示
            float baseLine = bottom - (decorationHeight - (fm.bottom - fm.top)) / 2 - fm.bottom;
            //獲取文字寬度
            mSideMargin = Math.abs(mSideMargin);
            c.drawText(mDecorListener.parentName(position), left + mSideMargin, baseLine, mTextPaint);//x軸,baseLine
            Rect rect = new Rect();//為了得到當前text的屬性
            mTextPaint.getTextBounds(mDecorListener.parentName(position), 0, mDecorListener.parentName(position).length(), rect);
            //繪制那條橫線
            c.drawLine(left + mSideMargin * 2 + rect.width(), baseLine - rect.height() / 2, parent.getWidth() -
                    mSideMargin, baseLine - rect.height() / 2, mTextPaint);
    
        }
    

    完結

    Recycleview模仿瑞幸咖啡菜單物品列表