This article has authorized the wechat public account: Hongyang (hongyangAndroid) in the wechat public account platform original launch

The old rule is to post renderings first

Github address, if you feel helpful, you can give a star

Github.com/idic779/mon…

Add the dependent

The compile ‘com. Making. Idic779: monthweekmaterialcalendarview: 1.7’

How do you use it here

What can this library do?

  • You can control whether to allow sliding left and right, sliding up and down, switch months

  • Smooth mode switch from last month to next month

  • Custom calendar styles

  • Based on the material-calendarview this library implementation, can be customized according to the needs of the effect

    The previous development task involved the switching effect of calendar of year, month and day. Due to the need for linkage, there are about three directions in mind, either by handling the touch event of view, or by custom behavior, or by the magic device ViewDragHelper. On the Internet, most of them are realized through customized behaviors. This paper uses the third method to realize a highly customized and freely switched weekly and monthly calendar view, which provides an idea to realize the page linkage effect. Because the focus is to achieve the effect of month and year switch, ORIGINALLY thought that you can write a calendar component and then add ViewDragHelper, should be able to achieve the effect of month and week linkage, right? After thinking about it, the key is to switch that simply find an open source library stability better calendar component, so use github.com/prolificint… Fast 4000Start library, ViewDragHelper, as a magic device can do a lot of things, the official DrawerLayout, BottomSheetBehavior with him to achieve, why use it? For dragging a View, if you rewrite the touch event by yourself, calculating the sliding distance and then moving the View will need to deal with more tedious code to achieve. We can easily do this if we use ViewDragHelper. A quick introduction to ViewDragHelper

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, floatyvel) { super.onViewReleased(releasedChild, xvel, yvel); }});Copy the code
  • tryCaptureView(): If true is returned, the view can be captured, and we can set the capture condition here
  • clampViewPositionHorizontal ()``clampViewPositionVertical(): to respectivelychildHorizontal and vertical movement of the boundary control, such as limiting the distance between the week and the month can be handled here
  • onViewPositionChanged(): whenchildThis method is called back when the position of the
  • onViewReleased(): Callback when finger is released
  • getViewHorizontalDragRange()``getViewVerticalDragRange()Returns thechildHorizontal or vertical movement range, greater than 0 to capture.

More can refer to Hongyang Android ViewDragHelper fully resolve custom ViewGroup artifact

How to implement

Since we choose ViewDragHelper to realize week-to-month linkage, let’s explain the effect to achieve, in the month view, can drag the recyclerView below up to the height of the week view, if the upshift process exceeds a certain distance, the default scrolling to the week view. In the week view and can drag recyclerView down to the height of the month view position, down the process if more than a certain distance on the default scrolling to the month view.

The overall analysis

The whole page is composed of the View of the week name at the top, the MaterialCalendarView of the week mode, the MaterialCalendarView of the month mode and the recyclerView at the bottom This library original names and the date at the top of the display is a week, it is important to note here made slightly modify give these to hide away, specific can see MaterialCalendarView. SetTopbarVisible (). And do the modify MaterialCalendarView. The way to increase the height of the obtained special getItemHeight (), the pattern for weeks when the height of the display.

The specific implementation

  • Drag before processing entire page onlyrecyclerView, if drag up in month mode ifrecyclerViewIf you’re not scrolling to the top then you’re not allowed to drag
        @Override
        public boolean tryCaptureView(View child, int pointerId) {
            return! mDragHelper.continueSettling(true) &&child == mRecyclerView && ! animatStart && isAtTop(mRecyclerView) && ! ViewCompat.canScrollVertically(mRecyclerView, -1); }Copy the code
  • limitrecyclerViewThe height of movement is between weekly and monthly patterns
@Override public int clampViewPositionVertical(View child, int top, Int dy) {// Determine the vertical distance to move finalWeekModeHeight to finalMonthModeHeight int topBound = finalWeekModeHeight; int bottomBound = finalMonthModeHeight; int newTop = Math.min(Math.max(top, topBound), bottomBound);return newTop;
       }
Copy the code
  • inonMeasureGet some initial data values, including the height of the weekly mode, the height of the monthly mode, the maximum distance traveled, and the height of the 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;
 }
Copy the code
  • Then onLayout () draws the views in the layout to their positions

  • DefaultStopHeight When the date is selected the getCurrentItemPosition() will calculate the number of rows it clicked on and setStopItemPosition() will get the height to stop,

  • Now, the most important thing is that since the recyclerView is linked week to month, we found that when we used recyclerView we would always call back onViewPositionChanged(), so we could change the month of the recyclerView based on how far it moved.

Private void HandlerOffset(View changedView, int left, int top, int dx, TransY = transY + dy; transY = transY + dy;if (transY > 0) {
               transY = 0;
           }
           if (transY < -calendarMonthHight - calendarItemHight) {
               transY = -calendarMonthHight - calendarItemHight;
           }

           float abstransY = Math.abs(transY);
           if(dy < 0) {// If you want to calendarHight-defaultStopHeight // if you want to calendarHight-calendarItemHightif (abstransY >= (calendarMonthHight - defaultStopHeight) && abstransY < 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(abstransY < maxOffset) { mCalendarViewMonth.setTranslationY(getOffset((int) mCalendarViewMonth.getTranslationY() + dy, 0)); }}}Copy the code

The moon view is moved using setTranslationY to limit the maximum distance it can slide by getOffset().

  • At the time of the release of the finger we areonViewReleased()Change the current view to monthly or weekly mode if the slide distance exceeds a certain value
       @Override
          public void onViewReleased(View releasedChild, float xvel, floatyvel) { int moveY = finalMonthModeHeight - mRecyclerView.getTop(); Int weekDistance = calendarItemHight; Int maxDistance = calendarMonthHight;if(currentMode == mode.month) {// If the sliding distance exceeds the maximum sliding distance between the currently selected itemif(moveY > weekDistance && moveY < maxDistance) {// Change to weekly modesetMode(Mode.WEEK);
                  } else if(moveY <= weekdistance) {// Change to monthly modesetMode(Mode.MONTH); }}else{// In weekly mode, if the distance from the top selected date is less than -10, it will be changed to monthly modeif(moveY > maxoffset-10) {// Changes to weekly modesetMode(Mode.WEEK);
                  } else if(moveY <= maxoffset-10) {// Change to monthly modesetMode(Mode.MONTH); }}}Copy the code

Note that when onInterceptTouchEvent() is in monthly mode and can be dragged, the recyclerView at the bottom is not allowed to slide

if (currentMode == Mode.MONTH&& canDrag) {
               setRecyclerViewCanScroll(false);
}
Copy the code

How else can you use it

Next, how can you customize it? If you want to replace the month and week views in your project and don’t want to use the Material- calendarView, it’s easy, Only need your week month view must have a method to obtain the height of the single calendar (for example in my library MaterialCalendarView. GetItemHeight ()), then the week view and view this month, In MonthWeekMaterialCalendarView respectively according to the order into the corresponding location. Then setListener() sets up the related callback processing, such as the callback for date selection or month switch, etc.

Good work done.