基于RecyclerView實現的課程表View(ScheduleView)

zjz668 8年前發布 | 10K 次閱讀 Android開發 移動開發 RecyclerView

最近打算依托學校教務系統做一個Android平臺的客戶端,需要一個課程表控件,在網上找了幾個都不是很滿意,感覺用起來靈活性不是很好,于是自己動手碼了一個,貼出來記錄一下,有問題歡迎Issue

效果圖

 

 

自定義View——ScheduleView

結構

LinearLayout

-- LinearLayout

-- RecyclerView

層次很簡單,最外層是一個LinearLayout,星期欄(header)是一個LinearLayout,下面整個是一個RecyclerView

思路:

  • 利用 GridLayoutManager.setSpanSizeLookUp(SpanSizeLookup) 方法,在Adapter中通過 getItemViewType() 區分兩種不同類型item,實現不同的item(index和course)
  • 從而控制最左側的item(index)的寬度和正常的item(course)的寬度比例為1:TIMES(其中TIMES在Constant中定義)
  • 同樣的,在Header(星期欄)中使用 addView(View child, ViewGroup.LayoutParams params) ,通過設置weight來控制每個item的大小和位置,從而保證,header中的item和下面RecyclerView中的item位置上是對應的
  • 將Header中一周顯示幾天交給調用者決定(大多數學校的周末是沒有課的,所以可以一周只顯示5天)
  • 調用者傳入的是課程信息,但是RecyclerView的item中包含了index類型的item,所以需要將傳入的 List<CourseBean> 做處理成 List<Data4RvItem> ,將index信息插入到合適位置,具體實現往下看
  • RecyclerView在獲取到數據之后才開始初始化加載,因此等待用戶調用 fillData(...) 方法后才開始進行初始化操作
  • 在CourseViewHolder中對itemView添加點擊事件

實現

說了這么多,不如直接擼代碼:

public ScheduleView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        this.context = context;
        root = inflate(context, R.layout.schedule_view, this);
        scheduleHeadLL = (LinearLayout) root.findViewById(R.id.ll_schedule_head);
        scheduleContentRV = (RecyclerView) root.findViewById(R.id.rv_schedule_content);
    }
...
在獲取到用戶數據后調用者調用fillData(...)之后對RecyclerView的初始化:
private void initRecyclerView(ScheduleBean mScheduleBean, int dayInWeek) {
        final List<Data4RvItem> datas = mScheduleBean.getDatas4show();
        //+1加上最左邊的課程號
        GridLayoutManager manager = new GridLayoutManager(context, TIMES * dayInWeek + 1);
        manager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
            @Override
            public int getSpanSize(int position) {
                Data4RvItem data = datas.get(position);
                switch (data.getType()) {
                    case TYPE_COURSE_INDEX:
                        return 1;//index這個item的寬度的整個寬度的1/TIMES*daysInWeek+1
                    case TYPE_COURSE:
                        return TIMES;
                }
                return 0;
            }
        });
        scheduleContentRV.setLayoutManager(manager);
        scheduleContentRV.addItemDecoration(new RecyclerView.ItemDecoration() {
            @Override
            public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
                outRect.set(1, 1, 1, 1);
            }

        });
        scheduleContentRV.setAdapter(new ScheduleContentRvAdapter(context
                , mScheduleBean, dayInWeek));
    }

fillData(...)方法中的實現:

public void fillData(int[] startYMD, int daysInWeek, List<CourseBean> courses) {
        if (startYMD.length != 3)
            throw new RuntimeException("參數startYMD:   本周起始年月日-->數組長度為3,分別為year,month,day,傳入參數異常");
        if (daysInWeek < 0 || daysInWeek > 7)
            throw new RuntimeException("daysInWeek參數異常\nFunction fillData(List,int) got an incorrect param(daysInWeek)");
        this.daysInWeek = daysInWeek;
*       ScheduleBean mScheduleBean = new ScheduleBean(courses, daysInWeek);
        initHeader(startYMD, daysInWeek);
        initRecyclerView(mScheduleBean, daysInWeek);

    }

在ScheduleBean的構造函數中對List<CourseBean>進行了操作,轉換成了List<Data4RvItem>

/**
 * Created by GG on 2016/11/22.
 * Email:gu.yuepeng@foxmail.com
 * <p>
 * 課程表數據bean
 */
public class ScheduleBean {
    /**
     * 傳進來的本周課程列表,要求橫向添加CourseBean
     * 即周一的1,2節課,周二的1,2節……周五的1,2節;
     * 周一的3,4節,周二的3,4節……
     */
    private List<CourseBean> courses;
    /**
     * 課程表每周顯示幾天
     */
    private int dayInWeek;

    /**
     * 實際填充在recyclerview中的數據源
     * 包含已經處理好的index item
     */
    private List<Data4RvItem> datas4show;

    public ScheduleBean(List<CourseBean> courses, int dayInWeek) {
        this.courses = courses;
        this.dayInWeek = dayInWeek;
        prepareData4show();
    }



    public List<Data4RvItem> getDatas4show() {
        if (datas4show == null)
            Log.e(TAG, "getDatas4show: datas4show==null");
        return datas4show;
    }

    /**
     * 將index和課程整合到一起
     * 準備用于填充布局的數據源
     */
    private void prepareData4show() {
        datas4show = new ArrayList();
        for (int i = 0; i < courses.size(); i++) {
            //每次應該添加周一的課程之前先將view左側的課程節數添加進去
            if (i % dayInWeek == 0) {
                datas4show.add(new Data4RvItem(i / dayInWeek));
            }
            datas4show.add(new Data4RvItem(courses.get(i)));
        }
    }
}

Adapter中的通過處理好的List<Data4RvItem>中的數據type來區分item的類型:

@Override
    public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View mView;
        switch (viewType) {
            case TYPE_COURSE_INDEX:
                mView = LayoutInflater.from(context)
                        .inflate(R.layout.item_schedule_rv_course_index, null);
                mView.setClickable(false);//作為index的item不可接受點擊事件
                return new IndexViewHolder(context,mView);
            case TYPE_COURSE:
                mView = LayoutInflater.from(context)
                        .inflate(R.layout.item_schedule_rv_course, null);

                return new CourseViewHolder(context,mView);
        }
        return null;
    }

    @Override
    public void onBindViewHolder(BaseViewHolder holder, int position) {
        Data4RvItem mData = datas4show.get(position);
        //此處不做處理,交給各自的Holder做處理
        holder.fillData(mData);
    }

    @Override
    public int getItemViewType(int position) {
        return datas4show.get(position).getType();
    }

    @Override
    public int getItemCount() {
        return datas4show.size();
    }

BaseViewHolder:

public abstract class BaseViewHolder extends RecyclerView.ViewHolder {
    protected View root;
    protected Context context;

    public BaseViewHolder(Context context,View itemView) {
        super(itemView);
        this.context=context;
        root=itemView;
    }

    /**
     * 在此方法中將根據各自類型將傳入的data填充到布局中
     * @param data4RvItem
     */
    public abstract void fillData(Data4RvItem data4RvItem);
}

參數說明

public void fillData(int[] startYMD, int daysInWeek, List<CourseBean> courses)
  • startYMD:一個length為3的int數組,用來指定課程表的周一對應的日期,數組中分別應該是year,month,day.
  • daysInWeek:Header中預期顯示到周幾,即一周有幾天
  • courses:課程表信息,即使沒有課也需要占位,courses應和daysInWeek對應,以便后續操作轉換成Data4RvItem

總結

  • 中間曾想過使用StaggeredGridLayoutManager實現一個橫向的瀑布流效果,最終還是用GridLayoutManager來做的,寫完之后感覺還不錯,不過bug應該不少,待發掘
  • 感覺使用RecyclerView來做整個層次結構簡單清晰了很多

 

來自:http://www.jianshu.com/p/ca7d97c6e074

 

 本文由用戶 zjz668 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!