基于RecyclerView實現的課程表View(ScheduleView)
最近打算依托學校教務系統做一個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 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!