How to achieve smooth and slide switching between the weekly and monthly calendars, water drop effects, highly customizable, similar to the Xiaomi calendar, and weekly and monthly Xiaomi
Post rules first
Add dependency
Compile 'com. github. idic779: monthweekmaterialcalendarview: 8080'
For details about how to use it, see here
What can this database do?
Allows you to control whether to allow sliding between the left and right sides, sliding up and down, and switching the year and month.
Smooth switching between the last week and month
Customize calendar styles
Based on the material-calendarview library, you can customize the results as needed
Previously, the development task involved switching between the year, month, and day calendars. As the interaction was required, there were about three possible directions, either through processingViewTouch events, either through customBehavior to implement, or throughViewDragHelper is implemented by means of custom features on the Internet.Bahavior is implemented. This article uses the third method to implement a highly customizable weekly/monthly calendar view and provide a way to achieve page interaction.
Preparation
Since the focus is on the year-to-year switching effect, I was thinking that I could write a calendar component myself and then addViewDragHelper should be able to achieve the effect of weekly/monthly linkage? I thought about it later, but the focus is to switch between them, so I simply find a calendar component with better stability in the open-source library, so I use github.com/prolificint... A library with a speed of start,
ViewDragHelper, as an artifact, can do a lot of things, the officialDrawerLayout,BottomSheetBehavior is implemented by him. Why is it used? ForView, If you rewrite it yourselfTouch event, calculate the sliding distance before movingView needs to process a lot of tedious code for implementation. If we useViewDragHelper can easily achieve this effect.
A Brief IntroductionViewDragHelper
ViewDragHelper helper= ViewDragHelper.create(this, 1.0f, new ViewDragHelper.Callback() { @Override public boolean tryCaptureView(View child, int pointerId) { return true; } @Override public int clampViewPositionHorizontal(View child, int left, int dx) { return left; }
@Override public int clampViewPositionVertical(View child, int top, int dy) { return top; } @Override public int getViewHorizontalDragRange(View child) { return super.getViewHorizontalDragRange(child); } @Override public int getViewVerticalDragRange(View child) { return super.getViewVerticalDragRange(child); } @Override public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) { super.onViewPositionChanged(changedView, left, top, dx, dy); } @Override public void onViewReleased(View releasedChild, float xvel, float yvel) { super.onViewReleased(releasedChild, xvel, yvel); } });
TryCaptureView (): If true is returned, this view can be captured. We can set the capture conditions here.ClampViewPositionHorizontal ()ClampViewPositionVertical ():
PairControls the horizontal and vertical movement boundaries of child. For example, you can handle the restriction on the moving distance between weeks and months.
OnViewPositionChanged (): WhenThis method is called back when the child location is moved.
OnViewReleased (): callback when the finger is released
GetViewHorizontalDragRange () getViewVerticalDragRange (): ReturnIf the range of child's horizontal or vertical movement is greater than 0, it can be captured.
How to Implement
SelectViewDragHelper needs to achieve weekly/monthly interaction. Let's take a look at the effect we want to achieve. In the monthly view, we can take the followingThe recyclerView is moved up to the height of the weekly view. If the distance between the two views is exceeded, the page is rolled to the weekly view by default. In the weekly viewThe recyclerView is dragged down to the height of the monthly view. If the moving process exceeds a certain distance, it is rolled to the monthly view by default.
Overall Analysis
The whole page is named by the week on the top.View and weekly ModeMaterialCalendarViewMaterialCalendarView andNote the following when creating a recyclerView:MaterialCalendarView the library originally had a weekly name and a date displayed on the top,Note thatI made some modifications to hide them. For details, seeMaterialCalendarView. setTopbarVisible (). And added the method for getting the height of a single row.MaterialCalendarView. getItemHeight () is the height displayed in weekly mode.
Implementation
OnlyRecyclerView, if you drag up in monthly mode, ifThe recyclerView cannot be dragged if it is not rolled to the top. Related code
@Override public boolean tryCaptureView(View child, int pointerId) { return !mDragHelper.continueSettling(true) &&child == mRecyclerView && !animatStart && isAtTop(mRecyclerView) && !ViewCompat.canScrollVertically(mRecyclerView, -1); }
RestrictionsThe height of the recyclerView is between the weekly and monthly modes.
@ Override public int cursor (View child, int top, int dy) {// determines the distance from finalWeekModeHeight to finalMonthModeHeight int topBound = finalWeekModeHeight; int bottomBound = finalMonthModeHeight; int newTop = Math. min (Math. max (top, topBound), bottomBound); return newTop ;}
InOnMeasure obtains some initial data values, including the height of the weekly mode, the height of the monthly mode, the maximum moving distance, and the height of a single row.
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); calendarItemHight = mCalendarViewMonth.getItemHeight(); calendarWeekHight = calendarItemHight; if (defaultStopHeight == 0) { defaultStopHeight = getCurrentItemPosition(CalendarDay.today()) * calendarItemHight; } calendarMonthHight = mCalendarViewMonth.getMeasuredHeight(); weekViewHight = mTopWeekView.getMeasuredHeight(); finalMonthModeHeight = weekViewHight + calendarMonthHight; finalWeekModeHeight = calendarItemHight + weekViewHight; maxOffset = calendarMonthHight - calendarItemHight; }
ThenOnlayout ()View to the corresponding position
DefaultStopHeightGetCurrentItemPosition () calculates the number of rows it clicks and callsSetStopItemPosition () to get the height to be stopped,
Next let's talk aboutKey PointsSince it is the link between weeks and months, we find that we are draggingWhen the recyclerView is viewed, we will not stop calling back.OnViewPositionChanged (), we canThe distance of the recyclerView to move the corresponding monthly view,
// Sliding processing private void HandlerOffset (View changedView, int left, int top, int dx, int dy) {// obtain the relative distance of the calendar relative to the finger. dy is less than 0 transY = transY + dy; if (transY> 0) {transY = 0 ;} if (transY <-calendarMonthHight-calendarItemHight) {transY =-calendarMonthHight-calendarItemHight ;}
Float login ansy = Math. abs (transY); if (dy <0) {// if slide, in addition, if (abstransY> = (calendarMonthHight-defaultdefaht-defaultstoht), the absolute value of the slide must be greater than the value of calendarHight-defastostopheight) & amp; languansy <calendarMonthHight-calendarItemHight) {if (! AnimatStart) {mCalendarViewMonth. setTranslationY (getOffset (int) mCalendarViewMonth. getTranslationY () + dy, calendarItemHight-defaultStopHeight); }}if (dy> 0) {if (abstransY <maxOffset & currentMode. equals (Mode. WEEK) {mCalendarViewWeek. setVisibility (INVISIBLE);} if (specified ansy <maxOffset) {mCalendarViewMonth. setTranslationY (getOffset (int) mCalendarViewMonth. getTranslationY () + dy, 0 ));}}}
The month view is moved throughSetTranslationY to move, in order to prevent the sliding time from passing through too fastGetOffset () limits the maximum sliding distance.
When we release our fingersOnViewReleased () changes the status. If the sliding distance exceeds a certain value, the current view is set to monthly or weekly.
@ Override public void onViewReleased (View releasedChild, float xvel, float yvel) {int moveY = finalMonthModeHeight-mRecyclerView. getTop (); // The height of the row to which the weekly mode slides. If it is exceeded, it slides to the weekly position int weekdistance = calendarItemHight; // The maximum sliding distance int maxDistance = calendarMonthHight; if (currentMode = Mode. MONTH) {// if the sliding distance exceeds the distance between the selected item and the maximum sliding distance if (moveY> weekdistance & moveY <maxDistance) {// Changes to the weekly setMode (Mode. WEEK);} else if (moveY <= weekdistance) {// change to monthly Mode setMode (Mode. MONTH) ;}} else {// in weekly mode, if the distance from the selected date on the top is less than the maximum sliding distance-10, it is changed to monthly mode if (moveY> maxOffset-10) {// change to weekly Mode setMode (Mode. WEEK);} else if (moveY <= maxOffset-10) {// change to monthly Mode setMode (Mode. MONTH );}}}
Note:InIf onInterceptTouchEvent () is in monthly mode and can be draggedThe recyclerView cannot be slide.
if (currentMode == Mode.MONTH&& canDrag) { setRecyclerViewCanScroll(false); }
How can I use it?
Next, let's talk about how you can customize it? If you want to replace the month and week views in the project, you don't want to use Material-calendarview. It's very easy, you only need to have a method to get the height of a single calendar (for example, MaterialCalendarView in my library. getItemHeight (), and then set the monthly and weekly views inPut MonthWeekMaterialCalendarView in the corresponding position in sequence. ThenSet the relevant callback processing in setListener (), for example, the callback for date selection or month switching.