1 General idea
1) Initialize calendar data and pass it as a list to RecyclerView.Adapter
2) Rewrite the onTouchEvent method of RecyclerView, listen for gesture changes, and then change the list data to re-display the UI
The Last Rendering
2 Key Codes
So the key point of the whole project is how to get the correct date data. This Calendar Tool found on the Internet is really good after testing. It saves a lot of time and can be used directly with a little modification.
public class CalendarTool<T extends BaseDateEntity> {
private final String TAG = CalendarTool.class.getSimpleName();
public static int FLING_MIN_DISTANCE = 100;
private final int[] weekDayRow = {0, 1, 2, 3, 4, 5, 6};
private ArrayList<DateEntity> mDataList = new ArrayList<>();//Date array
private ArrayList<T> mRecordList;//Event Record Array
private DateEntity mDateEntity;
private int mYear;
private int mMonth;
private boolean mEndBelong;
private boolean mStartBelong;
private int mStartDay;
private int mEndDay;
/**
* Current year, month and day
*/
private int mCurrenYear;
private int mCurrenMonth;
private int mCurrenDay;
/**
* Array of Monthly Days in Ordinary Years
*/
int commonYearMonthDay[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
/**
* An array of leap years, months and days
*/
int leapYearMonthDay[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
public CalendarTool() {
/** Date of initialization of the current system */
Calendar calendar = Calendar.getInstance();
mCurrenYear = calendar.get(Calendar.YEAR);
mCurrenMonth = calendar.get(Calendar.MONTH) + 1;
mCurrenDay = calendar.get(Calendar.DAY_OF_MONTH);
this.mYear = mCurrenYear;
this.mMonth = mCurrenMonth;
}
/**
* Get the current calendar year x for the year and y for the month
*/
public Point getNowCalendar() {
Point p = new Point(mYear, mMonth);
return p;
}
/**
* Judging whether the first day belongs to this month or not
*/
public boolean isStartBelong() {
return mStartBelong;
}
/**
* Judging whether the last day belongs to this month or not
*/
public boolean isEndBelong() {
return mEndBelong;
}
/**
* Get the date of the first day of the calendar
*/
public int getStartDay() {
return mStartDay;
}
/**
* Get the date of the last day of the calendar
*/
public int getEndDay() {
return mEndDay;
}
public ArrayList<DateEntity> initDateList() {
return initDateList(mYear, mMonth);
}
public void initRecordList(ArrayList<T> recordList) {
mRecordList = recordList;
}
/**
* Get the date set of the current page by year and month
*/
private ArrayList<DateEntity> initDateList(int year, int month) {
Log.i(TAG, "initDateList: year = " + year + " month = " + month);
mDataList.clear();
/** Amendment */
int endDate = 0;// Get the number of days from the previous month as the end date of the previous month in this calendar
if ((year - 1) == this.mYear || month == 1) {// If the number of days in the last month is the number of days in December of the previous year, or when it turns to January, the number of days in the last month is also the number of days in December of the previous year.
endDate = this.getDays(year - 1, 12);
} else {// Get the number of days from the previous month as the end date of the previous month in this calendar
endDate = this.getDays(year, month - 1);
}
/** End of Modification */
this.mYear = year;// The year on the current calendar
this.mMonth = month;// Months on the current calendar
int days = this.getDays(year, month);// Get the total number of days this month
int dayOfWeek = this.getWeekDay(year, month);//What day is the first day of the current year?
int selfDaysEndWeek = 0;// What day is the last day of the month?
mStartBelong = true;
/** First add the ones that are not part of this month */
if (dayOfWeek != 0) {
int startDate = endDate - dayOfWeek + 1;// The last month of the current month begins on this calendar
for (int i = startDate, j = 0; i <= endDate; i++, j++) {
mDateEntity = new DateEntity(year, month - 1, i);
mDateEntity.date = mDateEntity.year * 10000 + mDateEntity.month * 100 + i;
if (startDate == i) {
mStartBelong = false;
mStartDay = mDateEntity.date;
}
mDateEntity.isSelfMonthDate = false;
mDateEntity.weekDay = weekDayRow[j];
mDateEntity.hasRecord = hasRecord(mDateEntity.date);
mDataList.add(mDateEntity);
}
}
/** Add this month's */
for (int i = 1, j = dayOfWeek; i <= days; i++, j++) {
mDateEntity = new DateEntity(year, month, i);
mDateEntity.date = mDateEntity.year * 10000 + mDateEntity.month * 100 + i;
if (mStartBelong && i == 1) {
mStartDay = mDateEntity.date;
}
if (i == days) {
mEndDay = mDateEntity.date;
}
mDateEntity.isSelfMonthDate = true;
if (j >= 7) {
j = 0;
}
selfDaysEndWeek = j;
mDateEntity.weekDay = weekDayRow[j];
if (year == mCurrenYear && month == mCurrenMonth && i == mCurrenDay) {
mDateEntity.isNowDate = true;
}
mDateEntity.hasRecord = hasRecord(mDateEntity.date);
mDataList.add(mDateEntity);
}
mEndBelong = true;
/*** Add the next month's */
for (int i = 1, j = selfDaysEndWeek + 1; i < 7; i++, j++) {
if (j >= 7) {
break;
}
mEndBelong = false;
mDateEntity = new DateEntity(year, month + 1, i);
if (mDateEntity.month > 12) {
mDateEntity.year = year + 1;
mDateEntity.month = 1;
}
mDateEntity.date = mDateEntity.year * 10000 + mDateEntity.month * 100 + i;
mDateEntity.isSelfMonthDate = false;
mDateEntity.weekDay = weekDayRow[j];
mDateEntity.hasRecord = hasRecord(mDateEntity.date);
mDataList.add(mDateEntity);
mEndDay = mDateEntity.year * 10000 + mDateEntity.month * 100 + i;
}
return mDataList;
}
/**
* How many days are there in a month?
*/
private int getDays(int year, int month) {
int days = 0;
if ((year % 4 == 0 && (year % 100 != 0)) || (year % 400 == 0)) {
if (month > 0 && month <= 12) {
days = leapYearMonthDay[month - 1];
}
} else {
if (month > 0 && month <= 12) {
days = commonYearMonthDay[month - 1];
}
}
return days;
}
private boolean hasRecord(int date) {
if (mRecordList != null) {
for (T baseDateEntity : mRecordList) {
if (baseDateEntity.year * 10000 + baseDateEntity.month * 100 + baseDateEntity.day == date) {
return true;
}
}
}
return false;
}
/**
* Through the year, the first day of month acquisition is the week, return 0 is Sunday, 1 is Monday, and so on.
*/
private int getWeekDay(int year, int month) {
int dayOfWeek;
int goneYearDays = 0;
int thisYearDays = 0;
boolean isLeapYear = false;//Leap year
int commonYearMonthDay[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
int leapYearMonthDay[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
for (int i = 1900; i < year; i++) {// From 1900 onwards, January 1, 1900 was Monday.
if ((i % 4 == 0 && (i % 100 != 0)) || (i % 400 == 0)) {
goneYearDays = goneYearDays + 366;
} else {
goneYearDays = goneYearDays + 365;
}
}
if ((year % 4 == 0 && (year % 100 != 0)) || (year % 400 == 0)) {
isLeapYear = true;
for (int i = 0; i < month - 1; i++) {
thisYearDays = thisYearDays + leapYearMonthDay[i];
}
} else {
isLeapYear = false;
for (int i = 0; i < month - 1; i++) {
thisYearDays = thisYearDays + commonYearMonthDay[i];
}
}
dayOfWeek = (goneYearDays + thisYearDays + 1) % 7;
Log.d(this.getClass().getName(), "From 1990 to the present" + (goneYearDays + thisYearDays + 1) + "day");
Log.d(this.getClass().getName(), year + "year" + month + "month" + 1 + "Days are weeks." + dayOfWeek);
return dayOfWeek;
}
public void flushDate(float distance_x) {
if (distance_x < 0) {// Fling right
if (mMonth + 1 > 12) {
mDataList = initDateList(mYear + 1, 1);
} else {
mDataList = initDateList(mYear, mMonth + 1);
}
} else {// Fling left
if (mMonth - 1 <= 0) {
mDataList = initDateList(mYear - 1, 12);
} else {
mDataList = initDateList(mYear, mMonth - 1);
}
}
}
}
The key part is the initDateList method, which calculates the data to be displayed in the current calendar based on the current incoming annual and monthly data, including the date of the previous month and the date of the next month. So all APP has to do is continue to import year and month information and recalculate calendar data.
. Here, the onTouchEvent of RecyclerView method is rewritten to determine the user's touch direction. Later, to achieve each calendar click event, it conflicts with the monitored sliding event.
So the onInterceptTouchEvent method is also rewritten to intercept click events of child controls.
public class CalendarRecycleView<T extends BaseDateEntity> extends RecyclerView {
private CalendarRecycleViewAdapter mAdapter;
private Context mContext;
private CalendarTool mCalendarTool;
private OnCalendarDateListener mDateListener;
private float motion_x;
public CalendarRecycleView(Context context) {
super(context);
mContext = context;
init();
}
public CalendarRecycleView(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
init();
}
public CalendarRecycleView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mContext = context;
init();
}
private void init() {
setLayoutManager(new StaggeredGridLayoutManager(7, StaggeredGridLayoutManager.VERTICAL));
mCalendarTool = new CalendarTool();
mAdapter = new CalendarRecycleViewAdapter(mContext, mCalendarTool.initDateList());
setAdapter(mAdapter);
mAdapter.setOnItemListener(new CalendarRecycleViewAdapter.OnItemListener() {
@Override
public void onItemClick(DateEntity dateEntity) {
if (mDateListener != null) {
mDateListener.onDateItemClick(dateEntity);
}
}
});
}
public void initRecordList(ArrayList<T> list) {
mCalendarTool.initRecordList(list);
mCalendarTool.initDateList();
mAdapter.notifyDataSetChanged();
}
public void setOnCalendarDateListener(OnCalendarDateListener listener) {
this.mDateListener = listener;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
motion_x = event.getX();
Log.i("onTouchEvent", "ACTION_DOWN: " + event.getX() + " " + motion_x);
break;
case MotionEvent.ACTION_MOVE:
Log.i("onTouchEvent", "ACTION_MOVE: " + event.getX() + " " + motion_x);
float x = event.getX() - motion_x;
if (Math.abs(x) > CalendarTool.FLING_MIN_DISTANCE && motion_x != 0) {
mCalendarTool.flushDate(x);
mAdapter.notifyDataSetChanged();
motion_x = 0;
if (mDateListener != null) {
mDateListener.onDateChange(mCalendarTool.getNowCalendar(),
mCalendarTool.getStartDay(),
mCalendarTool.getEndDay(),
mCalendarTool.isStartBelong(),
mCalendarTool.isEndBelong());
}
return true;
}
break;
case MotionEvent.ACTION_UP:
break;
}
return super.onTouchEvent(event);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
motion_x = event.getX();
break;
case MotionEvent.ACTION_MOVE:
if (Math.abs(motion_x - event.getX()) >= mCalendarTool.FLING_MIN_DISTANCE) {
return true;
}
break;
case MotionEvent.ACTION_UP:
break;
}
return super.onInterceptTouchEvent(event);
}
}
3 Conclusion
It's easy to implement, that is, pay more attention to the details, and then because the ViewPager is not used, there is no animation effect in sliding, and it's also easy to add, the key is to calculate the list data of the ViewPager fragment.