天天看點

RecycleView 系列(3)--利用 ItemDecoration 實作時光軸(物流時間)樣式

前言

上一篇文章中詳細介紹了 ItemDecoration 這個了類,了解了 RecycleView 實作分割線的原理。

下面我們來進入實戰篇,首先實作一個比較常見的時光軸(物流詳情)的效果。

一、 效果分析

首先我來分析一下常見的一種物流詳情效果圖:

RecycleView 系列(3)--利用 ItemDecoration 實作時光軸(物流時間)樣式

分析可知,正常的 item 布局實作無法滿足此需求,因為我們在 layout 中不知道 item 的高度為多少,中間那條豎線不容易實作。

有了上節對 ItemDecoration 的了解,我們可以将左邊布局(藍色框以左) 當做分割線來處理。

其中需要注意的有:

  1. 紅色框内表示整個 item 内容
  2. 藍色框内 item 在 布局中 固定的内容
  3. 綠色框内小圖示的繪制
  4. 黃色框内時間的繪制
  5. 灰色框内豎線的繪制

二、功能實作

既然知道了思路,根據上節的步驟按部就班的就好。

1.首先模拟下資料結構,定義好 dataBean,如下:

public class LogisticsInfoBean  implements Serializable {
    /**
     * 物流資訊
     */
    private String message;
    /**
     * 目前物流狀态
     */
    private LogisticsStatus status;
    /**
     * 日期
     */
    private String date;
    /**
     * 時間
     */
    private String time;
  }
  
public enum LogisticsStatus {
    /**
     *  一般提示語
     */
    TIPS,
    /**
     * 已下單
     */
    ORDERED,
    /**
     * 備貨中
     */
    STOCK_UP ,
    /**
     * 已發貨
     */
    DELIVERED,
    /**
     * 運輸中
     */
    TRANSPORTING,
    /**
     * 已收貨
     */
    RECEIVING
}
           

2.設定

getItemOffsets()

因為我們作為分割線的部分是藍色框以左,是以我們需要設定 item 左邊 left 的邊距,假設預留 120 像素的邊距。

/**
     * 設定 item  lef他方向的偏移量
     */
    private int leftOffset = 120;
   
      @Override
    public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
        super.getItemOffsets(outRect, view, parent, state);
        //設定item 左邊流出 leftOffset 的邊距
        outRect.left = leftOffset;
    }
           
  1. 通過

    onDraw()

    方法繪制左邊分割線内的内容

繪制是主要的部分,而

onDraw()

方法 作用于 RecycleView, 是以我們需要周遊 item ,計算繪制内容的坐标,

然後通過 canvas 的 draw 方法進行繪制。

/**
     * 畫的小圓點的半徑
     */
    private int circleRadius = 10;
    /**
     * 畫的小圖示的寬度
     */
    private int iconWidth = 50;
    private Context context;
    private int padding  = 24;
    @Override
    public void onDraw(@NonNull Canvas canvas, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
        Log.d(TAG,"----onDraw---");
        canvas.save();
        //先畫分割線整體背景色
        canvas.drawColor(context.getResources().getColor(R.color.white));
        final int childCount = parent.getChildCount();
        //周遊 
        for (int i = 0; i < childCount; i++) {
            final View child = parent.getChildAt(i);
            //1.先畫 1 px 的圖示上半部分的豎線
            int startX = leftOffset-30;
            int startY = child.getTop();
            int lineStopY = startY+padding;
            paint.setColor(context.getResources().getColor(R.color.gray_deep));
            canvas.drawLine(startX,startY,startX,lineStopY,paint);
            //2.畫圖形
            int positon = parent.getChildAdapterPosition(child);
            LogisticsInfoBean bean = dataBeanList.get(positon);
            Rect dst = new Rect(startX-iconWidth/2,lineStopY,startX+iconWidth/2,lineStopY+iconWidth);
            //根據不同狀态畫不同圖形
            switch (bean.getStatus()){
                case TIPS:
                    canvas.drawCircle(startX,lineStopY+circleRadius,circleRadius,paint);
                    break;
                case ORDERED:
                    canvas.drawBitmap(BitmapFactory.decodeResource(context.getResources(),R.drawable.ic_order),null,dst,null);
                    break;
                case STOCK_UP:
                    canvas.drawBitmap(BitmapFactory.decodeResource(context.getResources(),R.drawable.ic_stockup),null,dst,null);
                    break;
                case DELIVERED:
                    canvas.drawBitmap(BitmapFactory.decodeResource(context.getResources(),R.drawable.ic_diliver),null,dst,null);
                    break;
                case TRANSPORTING:
                    canvas.drawBitmap(BitmapFactory.decodeResource(context.getResources(),R.drawable.ic_transporting),null,dst,null);
                    break;
                case RECEIVING:
                    canvas.drawBitmap(BitmapFactory.decodeResource(context.getResources(),R.drawable.ic_receive),null,dst,null);
                    break;
                default:
                    canvas.drawCircle(startX,lineStopY+circleRadius,circleRadius,paint);
                    break;
            }
            //畫下半部分豎線
            if (positon != dataBeanList.size() -1){
                if (bean.getStatus() == LogisticsStatus.TIPS){
                    canvas.drawLine(startX,lineStopY+2*circleRadius,startX,child.getBottom(),paint);
                }else {
                    canvas.drawLine(startX,lineStopY+iconWidth,startX,child.getBottom(),paint);
                }
            }
            //3.畫日期
            canvas.drawText(bean.getDate(),startX-iconWidth/2-10,lineStopY+iconWidth/2,paint);
            canvas.drawText(bean.getTime(),startX-iconWidth/2-10,lineStopY+iconWidth/2+20,paint);

        }


        canvas.restore();
    }
           

4.設定資料源

讓我們來造點假資料

@Override
    protected void initData() {
        dataBeanList.add(new LogisticsInfoBean("[收貨位址,xxxxxxx]",LogisticsStatus.RECEIVING,"02-11","10:00"));
        dataBeanList.add(new LogisticsInfoBean("小主,運輸中x1",LogisticsStatus.TRANSPORTING,"02-10","12:00"));
        dataBeanList.add(new LogisticsInfoBean("小主,\n運輸中x2",LogisticsStatus.TRANSPORTING,"02-10","12:10"));
        dataBeanList.add(new LogisticsInfoBean("小主,\n\n運輸中x3",LogisticsStatus.TRANSPORTING,"02-10","12:20"));
        dataBeanList.add(new LogisticsInfoBean("小主,\n運輸中x4",LogisticsStatus.TRANSPORTING,"02-10","12:30"));
        dataBeanList.add(new LogisticsInfoBean("小主,已發貨",LogisticsStatus.DELIVERED,"02-10","10:00"));
        dataBeanList.add(new LogisticsInfoBean("小主,備貨中",LogisticsStatus.STOCK_UP,"02-09","12:00"));
        dataBeanList.add(new LogisticsInfoBean("訂單支付成功,系統正在處理",LogisticsStatus.ORDERED,"02-09","10:10"));
        dataBeanList.add(new LogisticsInfoBean("訂單建立成功,等待支付",LogisticsStatus.TIPS,"02-09","10:00"));

        adapter.notifyDataSetChanged();

    }
           

其他一些設定這裡就不在說了,然後運作看一下效果:

RecycleView 系列(3)--利用 ItemDecoration 實作時光軸(物流時間)樣式

是以,實作起來也很簡單有沒有。

完整代碼看這裡:demo 傳送門

總結

關于分割線的實作,不管是什麼樣式的,按照之前說的步驟一步步來就好:

  • 通過

    getItemOffset()

    方法設定 item 的偏移量**
  • onDraw()

    onDrawOver()

    方法中完成繪制**
  • 周遊 item,計算分割線的位置**
  • 通過

    draw()

    方法完成繪制**

其中需要注意的是各個繪制内容坐标的計算。

參考

Android 自定義View實戰系列 :時間軸

https://developer.android.com/reference/android/support/v7/widget/RecyclerView.ItemDecoration