縱享絲滑滑動切換的周月日歷,水滴效果硼啤,可隨意自定義日歷樣式议经,仿小米日歷(ViewDragHelper實(shí)現(xiàn))

本文已授權(quán)微信公眾號:鴻洋(hongyangAndroid)在微信公眾號平臺原創(chuàng)首發(fā)

老規(guī)矩先貼效果圖

樣式.gif

水滴效果.gif

p1.jpg

p2.jpg

github地址,覺得有幫助的可以給個 star 唄

https://github.com/idic779/monthweekmaterialcalendarview

添加依賴

compile 'com.github.idic779:monthweekmaterialcalendarview:1.7'

具體如何使用看這里

這個庫可以做什么谴返?

  • 可以控制是否允許左右滑動煞肾,上下滑動,切換年月

  • 流暢的上下周月模式切換

  • 自定義日歷樣式

  • 基于material-calendarview 這個庫實(shí)現(xiàn)嗓袱,可以根據(jù)需求定制效果

之前開發(fā)任務(wù)中有涉及到年月日日歷的切換效果籍救,由于是需要聯(lián)動,想到的方向大概有3種渠抹,要么通過處理viewtouch事件蝙昙,要么是通過自定義behavior去實(shí)現(xiàn)闪萄,要么是通過ViewDragHelper這個神器去實(shí)現(xiàn),網(wǎng)上比較多的是通過自定義behavior去實(shí)現(xiàn)奇颠,本文使用的是第三種方法败去,實(shí)現(xiàn)的是一個可高度定制自由切換的周月日歷視圖,提供一種思路去實(shí)現(xiàn)頁面聯(lián)動效果。

準(zhǔn)備

由于重點(diǎn)實(shí)現(xiàn)的是年月切換的效果烈拒,本來想著說可以自己寫一個日歷組件然后再加上ViewDragHelper,應(yīng)該可以實(shí)現(xiàn)周月聯(lián)動的效果吧为迈?后面想了想,重點(diǎn)在切換那就干脆直接找個開源庫穩(wěn)定性好點(diǎn)的日歷組件缺菌,所以用https://github.com/prolificinteractive/material-calendarview快4000start的庫吧葫辐,
ViewDragHelper,作為一個神器可以做很多的事情伴郁,官方的DrawerLayout 耿战,BottomSheetBehavior用他來實(shí)現(xiàn),為什么用它焊傅?對于拖動某個View,如果是自己去重寫touch事件的剂陡,計算滑動距離再去移動View會需要處理比較多繁瑣的代碼去實(shí)現(xiàn)。如果我們用ViewDragHelper的話能很輕易的實(shí)現(xiàn)這樣的效果狐胎。
簡單的介紹下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, float yvel) {
            super.onViewReleased(releasedChild, xvel, yvel);
        }
    });
  • tryCaptureView():如果返回true鸭栖,則說明可以捕獲該view,我們可以在這里設(shè)置捕獲的條件
  • clampViewPositionHorizontal ()``clampViewPositionVertical()
    分別對child水平和豎直方向移動的邊界進(jìn)行控制握巢,例如限制周月移動的距離可以在這里做處理
  • onViewPositionChanged() : 當(dāng)child的位置發(fā)生移動時候會回調(diào)這個方法
  • onViewReleased():手指釋放時候的回調(diào)
  • getViewHorizontalDragRange()``getViewVerticalDragRange():返回child橫向或者縱向移動的范圍晕鹊,大于0才能捕獲。

更多的可以參考鴻洋的Android ViewDragHelper完全解析 自定義ViewGroup神器

如何實(shí)現(xiàn)

既然選擇ViewDragHelper要實(shí)現(xiàn)周月聯(lián)動呢暴浦,我們來理一理要實(shí)現(xiàn)的效果溅话,在月視圖的時候,能夠把下面的recyclerView上移拖到到周視圖的高度歌焦,上移過程如果超過一定距離就默認(rèn)滾動到周視圖飞几。
在周視圖的的時候又能把recyclerView下移拖動到月視圖的高度位置,下移過程如果超過一定距離就默認(rèn)滾動到月視圖独撇。

整體分析

整個頁面是由頂部的周名字的View屑墨、周模式的MaterialCalendarView、月模式的MaterialCalendarView和最下面的recyclerView組成
需要注意的是MaterialCalendarView 這個庫原來是有周名字還有頂部顯示日期的纷铣,
需要注意的是這里稍微做了下修改把這些給隱藏掉了卵史,具體可以看MaterialCalendarView.setTopbarVisible()。并且做了下修改增加了獲得單行的高度方法MaterialCalendarView.getItemHeight() 关炼,即為周模式時顯示的高度程腹。

具體實(shí)現(xiàn)

  • 拖動前處理
    整個頁面只有recyclerView ,月模式下如果向上拖動時候如果recyclerView不是滾動到了頂部的話那么就不允許拖動,相關(guān)代碼
        @Override
        public boolean tryCaptureView(View child, int pointerId) {
            return !mDragHelper.continueSettling(true)
                    &&child == mRecyclerView && !animatStart
                    && isAtTop(mRecyclerView) &&                              
                          !ViewCompat.canScrollVertically(mRecyclerView, -1);
        }

  • 限制recyclerView移動的高度在周模式和月模式之間
       @Override
       public int clampViewPositionVertical(View child, int top, int dy) {
           //決定豎直方向上能移動的距離為 finalWeekModeHeight到finalMonthModeHeight
           int topBound = finalWeekModeHeight;
           int bottomBound = finalMonthModeHeight;
           int newTop = Math.min(Math.max(top, topBound), bottomBound);
           return newTop;
       }
  • onMeasure獲得初始的一些數(shù)據(jù)值儒拂,包括周模式的高度,月模式的高度寸潦,最大移動的距離,單行的高度
 @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;
 }
  • 然后在onlayout()把布局里的View繪制到對應(yīng)的位置上面

  • 最大移動的距離defaultStopHeight在選中日期時候就會通過 getCurrentItemPosition()計算出它點(diǎn)擊所在的行數(shù)再調(diào)用setStopItemPosition()就可以得到要停止下來的高度社痛,

  • 接下來說下最關(guān)鍵的地方
    既然是周月聯(lián)動我們發(fā)現(xiàn)在拖動recyclerView視圖的時候我們會不图回調(diào)onViewPositionChanged()這個方法,我們在這個方法里面就可以根據(jù)recyclerView移動的距離來移動對應(yīng)的月視圖蒜哀,

//滑動處理
       private void HandlerOffset(View changedView, int left, int top, int dx, int dy) {
           //獲取日歷相對手指移動的相對距離 dy向上移動小于0
           transY = transY + dy;
           if (transY > 0) {
               transY = 0;
           }
           if (transY < -calendarMonthHight - calendarItemHight) {
               transY = -calendarMonthHight - calendarItemHight;
           }

           float abstransY = Math.abs(transY);
           if (dy < 0) {
               //如果上滑動斩箫,并且滑向動的絕對值距離在超過calendarHight-defaultStopHeight
               // 并且小于可以滑動的距離calendarHight-calendarItemHight之間的話
               if (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));
               }

           }

       }

月視圖的移動我們是通過setTranslationY來移動的,為了防止滑動時候過快通過getOffset()限制一下它滑動的最大距離撵儿。

  • 在松開手指的時候我們在onViewReleased()做相關(guān)狀態(tài)的改變乘客,如果滑動的距離超過一定的值就把當(dāng)前視圖置為月模式還是周模式
       @Override
          public void onViewReleased(View releasedChild, float xvel, float yvel) {
              int moveY = finalMonthModeHeight - mRecyclerView.getTop();
              //周模式距離滑動為一行的高度,超過就滑動到周位置
              int weekdistance = calendarItemHight;
              //最大滑動距離
              int maxDistance = calendarMonthHight;
              if (currentMode == Mode.MONTH) {
                  //如果滑動距離超過當(dāng)前選中項(xiàng)和最大滑動距離之間的距離
                  if (moveY > weekdistance && moveY < maxDistance) {
                      //變?yōu)橹苣J?                  setMode(Mode.WEEK);
                  } else if (moveY <= weekdistance) {
                      //變?yōu)樵履J?                  setMode(Mode.MONTH);
                  }
              } else {
                  //周模式下距離頂部選中日期的距離小于最大滑動距離-10的話就讓它變?yōu)樵履J?              if (moveY > maxOffset - 10) {
                      //變?yōu)橹苣J?                  setMode(Mode.WEEK);
                  } else if (moveY <= maxOffset - 10) {
                      //變?yōu)樵履J?                  setMode(Mode.MONTH);
                  }
              }
          }
    

需要注意的是在onInterceptTouchEvent()如果是月模式并且可以拖動的時候淀歇,
底部的recyclerView是不允許滑動的

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

還可以怎么用

接下來說下你可以怎么去定制易核?如果你想替換項(xiàng)目中的月和周視圖的話,不想用Material-calendarview 浪默,很簡單牡直,只需要你自己的周月視圖必須有一個方法獲得單行日歷的高度(例如我的庫中的MaterialCalendarView.getItemHeight() ),然后把這個月視圖和周視圖纳决,分別在MonthWeekMaterialCalendarView里面按照順序放到對應(yīng)位置即可碰逸。然后再setListener()里面設(shè)置相關(guān)的回調(diào)處理,例如日期選中或者月份切換的回調(diào)等阔加。

好的大工告成饵史。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市胜榔,隨后出現(xiàn)的幾起案子约急,更是在濱河造成了極大的恐慌,老刑警劉巖苗分,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件厌蔽,死亡現(xiàn)場離奇詭異,居然都是意外死亡摔癣,警方通過查閱死者的電腦和手機(jī)奴饮,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來择浊,“玉大人戴卜,你說我怎么就攤上這事∽裂遥” “怎么了投剥?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長担孔。 經(jīng)常有香客問我江锨,道長吃警,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任啄育,我火速辦了婚禮酌心,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘挑豌。我一直安慰自己安券,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布氓英。 她就那樣靜靜地躺著侯勉,像睡著了一般。 火紅的嫁衣襯著肌膚如雪铝阐。 梳的紋絲不亂的頭發(fā)上址貌,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天,我揣著相機(jī)與錄音饰迹,去河邊找鬼芳誓。 笑死,一個胖子當(dāng)著我的面吹牛啊鸭,可吹牛的內(nèi)容都是我干的锹淌。 我是一名探鬼主播,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼赠制,長吁一口氣:“原來是場噩夢啊……” “哼赂摆!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起钟些,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤烟号,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后政恍,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體汪拥,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年篙耗,在試婚紗的時候發(fā)現(xiàn)自己被綠了迫筑。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡宗弯,死狀恐怖脯燃,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情蒙保,我是刑警寧澤辕棚,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望刹前。 院中可真熱鬧,春花似錦爸舒、人聲如沸浇雹。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽屿储。三九已至,卻和暖如春渐逃,著一層夾襖步出監(jiān)牢的瞬間够掠,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工茄菊, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留疯潭,地道東北人。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓面殖,卻偏偏與公主長得像竖哩,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子脊僚,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評論 2 355

推薦閱讀更多精彩內(nèi)容