最終效果
專案地址一、無限滾動實現
在RecyclerView.Adapter的getItemCount()方法中返回Integer.MAX_VALUE,使用RecycleView的scrollToPosition()方法滾動到一個足夠大的位置,這樣無限滾動效果就實現好了
二、日期顯示
使用RecycleView來實現,需要做的就是填充ItemView,這裡為了獲取資料集方便,以“月”檢視作為ItemView
- 獲取日期演算法實現
- 第一步:需要建立一個變基(包含年份、月份,以當前日期為變基)
- 第二步:根據滑動的deltaPosition計算出年份、月份,得到日期後繫結ItemView就很簡單了
public class DateSet {
private static final String TAG = "DateSet";
DateCell baseDateCell;
int basePosition;
public DateCell getDateCellByPosition(int position, DateCell dc) {
if (baseDateCell == null) {
baseDateCell = new DateCell();
basePosition = position;
baseDateCell.toCurrentDate();
}
int deltaYear = (position - basePosition) / 12;
int deltaMonth = (position - basePosition) % 12;
int month = baseDateCell.month + deltaMonth;
int year = baseDateCell.year + deltaYear;
if (month <= 0) {
--year;
month += 12;
} else if (month >= 13) {
++year;
month -= 12;
}
dc.setDate(year, month);
LogUtils.d(TAG, "baseDateCell:" + baseDateCell + "\tdc:" + dc);
return dc;
}
}
複製程式碼
三、月份檢視實現 這裡實現月份檢視的方法很多種,在這裡我採取的是繼承View,然後使用canvas畫出來 比較煩人的點就是在計算每一天的位置(left,top,right,bottom),這裡為了方便描述就把這個“天”的Cell描述為“DayView”
- 演算法實現
- 第一步:上面已經得到年份,月份後,就可以計算出這個月的第一天是星期幾、這個月有幾周、這個月一共有多少天,畫圖的時候主要用到這幾個資訊;
- 第二步:DayView的寬剛好佔據月檢視的1/7,DayView的高度自己可以隨便給一個預設值 結合第一步得到的資訊計算出這一天的(left,top,right,bottom),然後使用Canvas.drawText()方法畫出日期,這裡需要注意drawText的第三個引數是baseLine,如果要使用字型劇中的話,basLine一定要算準確
關於baseLine的相關資訊(圖片來源於網路侵刪)
計算DayView的(left,right,top,bottom)關鍵程式碼完整程式碼MonthView.java
for (int week = 0; week < weeks/*這個月的總週數*/; week++) {
int count = (week == 0) ? 7 - firstDayOfWeek/*1號是一個星期的第幾天*/ + 1 : ((week == weeks - 1) ? dateCell.getSumDays() - ((weeks - 2) * 7 + 7 - firstDayOfWeek + 1) : 7);
for (int index = 0; index < count; index++) {
String day = String.valueOf(((week == 0) ? index : (week > 1 ? (week - 1) * 7 + 7 - firstDayOfWeek + 1 + index : 7 - firstDayOfWeek + 1 + index)) + 1);
float l;
if (week == 0 && firstDayOfWeek > 1) {
l = getPaddingLeft() + (firstDayOfWeek - 1 + index) * (getMeasuredWidth() / 7.0F/*day_view_width*/);
} else {
l = getPaddingLeft() + index * (getMeasuredWidth() / 7.0F);
}
float t = getPaddingTop() + week * (getMeasuredHeight() / (float) weeks);
float r = l + getMeasuredWidth() / 7.0F;
float b = t + getMeasuredHeight() / (float) weeks;
if (dateCell.isCurMonth() && Integer.parseInt(day) == dateCell.getCurDay()) {
canvas.drawCircle(l + (r - l) / 2.0F, t + (b - t) / 2.0F, (r - l) / 4.0F, curDayBgPaint);
dayTextPaint.setColor(Color.WHITE);
} else {
dayTextPaint.setColor(Color.BLACK);
}
if (week < weeks - 1) {
canvas.drawLine(l, b, r, b, bottomLinePaint);
}
canvas.drawText(day, l + (r - l) / 2 - dayTextPaint.measureText(day) / 2, t + (b - t) / 2 + (b - t) / 4 - dayTextPaint.getFontMetrics().bottom, dayTextPaint);
map.put(day, new float[]{l, r, t, b});//儲存位置資訊,用於獲取所點選的具體日期
}
}
複製程式碼
- 月份檢視點選事件的監聽
由於月份檢視我們是直接繼承View,然後使用Canvas話出來的,所以不能簡單的setOnXXXListener;這兒我是Override了View的onTouchEvent方法來;對於獲取點選的具體日期,方法也很簡單,由於月份是一個網格狀的,最簡單的方法就是先橫向遍歷確定點選的點在X軸上落在周幾,然後就是以步長為7來遍歷是在第幾周,這樣也就確定了點選的具體日期
- 獲取點選的具體日期的演算法
private int getDayByPosition(float x/*點選事件的X座標*/, float y/*點選事件的Y座標*/) {
if (map.size() == 0) return 0;
float[] rectInfo;//[left,right,top,bottom]
for (int day = 1; day < 8; day++) {//先確定點選的點在周幾
rectInfo = map.get(String.valueOf(day));
if (x >= rectInfo[0] && x <= rectInfo[1]) {
while (rectInfo != null && (y < rectInfo[2] || y > rectInfo[3])) {
rectInfo = map.get(String.valueOf(day += 7));
}
return day > sumDays/*這個月的總天數*/ ? 0 : day;
}
}
return 0;
}
複製程式碼
到這裡所有基本工作基本完成,接下來就是組裝RecycleView的過程,這裡就不細說了;關於RecycleView吸頂效果實現可以參考其他人的實現方式,只要知道ItemDecoration的三個關鍵方法(getItemOffsets,onDraw,onDrawOver)基本沒問題;
- 專案中用到的一些獲取日期的方法完整程式碼:
public int getSumWeeksOfMonth() {//獲取一個月的總週數
Calendar calendar = Calendar.getInstance();
calendar.set(year, month - 1, sumDays);
return calendar.get(Calendar.WEEK_OF_MONTH);
}
public int getFirstDayOfWeek() {// 這個月的一號是這周的第幾天,星期天為第一天
Calendar calendar = Calendar.getInstance();
calendar.set(year, month - 1, 1);
return calendar.get(Calendar.DAY_OF_WEEK);
}
private static int getDaysOfMonth(int year, int moth) {//獲取這個月的總天數
Calendar c = Calendar.getInstance();
c.set(Calendar.YEAR, year);
c.set(Calendar.MONTH, moth - 1);
c.set(Calendar.DATE, 1);
c.roll(Calendar.DATE, -1);
return c.get(Calendar.DATE);
}
複製程式碼
缺陷: 目前還不支援陰曆、節假日顯示