日歷控件MaterialCalendarView的自定義樣式、每日不同樣式

一配紫、介紹

MaterialCalendarView是一個日歷控件径密。

GitHub地址

作者對它封裝的很深,如果你要加入自己的一些東西比較難躺孝,他給出的接口感覺太少了享扔,網(wǎng)上一些文章基本都是直接拉源碼下來修改的,個人不太喜歡那樣做植袍。所以自己閱讀源碼和框架惧眠,在他給出的接口上完成了自定義樣式,基本需要改的東西都可以實現(xiàn)了于个。先上一個效果圖氛魁。

完成效果圖.jpg

主要分兩部分:

  • 一個是添加了價格的文本顯示。

  • 一個是添加了自定義的選中樣式。

二秀存、添加價格

切入點

首先框架并沒有提供設(shè)置每日的布局的地方捶码,所以直接用Xml來改變布局是行不通的。

然后通過閱讀介紹或链,他有提供一個名為DotSpan的類宙项,它的效果是在DayViewDecorator里面設(shè)置一個背景樣式,在文字下面加了兩個點株扛。顯然這就是作者留給我們的添加自定義樣式的地方。

我們就可以從這里入手汇荐,模仿這個文件來添加自定義樣式洞就。

實現(xiàn)

public class BackgroundSpan implements LineBackgroundSpan {
    @Override
    public void drawBackground(Canvas canvas, Paint paint,
                               int left, int right, int top, int baseline, int bottom,
                               CharSequence charSequence,
                               int start, int end, int lineNum) {
        canvas.save();
        canvas.drawColor(Color.parseColor("#ffd6ba"));
        canvas.restore();
    }
}

添加流程是:

  1. 設(shè)置裝飾器

    calendarView.addDecorators(priceDecorator);

    priceDecorator是一個實現(xiàn)DayViewDecorator的類。

  2. 在Decorator設(shè)置Span

    @Override
    public void decorate(DayViewFacade view) {
        view.addSpan(new PriceSpan());
    }
    

這里分為兩種情況:

  1. 需要添加的樣式是固定的掀淘。也就是和具體某一天無關(guān)旬蟋,只是這天需要加上這個標(biāo)簽,那么建議直接使用DayViewDecorator的方法

    @Override
    public boolean shouldDecorate(CalendarDay day) {
        return true;
    }
    

    利用這個方法作為判斷條件革娄,需要的地方繪制上即可倾贰。

  2. 需要根據(jù)不同的日子顯示不同的數(shù)據(jù)的,即是我上圖的需求一樣拦惋。那么就按這個思路來:

    因為在繪制的方法drawBackground里面 拿不到Y(jié)ear和Month匆浙,這就導(dǎo)致了無法和數(shù)據(jù)進行關(guān)聯(lián),我的解決方案是利用控件的OnMonthChangedListener監(jiān)聽厕妖,在他翻頁結(jié)束后獲取當(dāng)前頁的year和month首尼,根據(jù)兩個再加上傳遞的Day參數(shù)來取數(shù)據(jù)。

注意:這個方法會有一個弊端言秸,就是他是一個ViewPager的setOnPageChangeListener软能,但是他的回調(diào)只給了onPageSelected的回調(diào),也就是說它無法在一開始拖動的時候就改變year和month举畸,所以2榕拧!抄沮!你在拖動至一半的時候看到兩邊的數(shù)據(jù)會是一樣的0虾恕! 這就是我這個方法的坑點合是,但是我還沒找到方法解決了罪,我只能將比較顯眼的背景色部分分離出來 按照第一種情況來做,這樣一般情況看不出來問題聪全。

貼一個效果圖感受一下

問題.jpg

可以看見價格兩邊是一樣的泊藕,但是背景色被我處理了不會出現(xiàn)這種情況,還是可以接受的。

Span

 public class PriceSpan implements LineBackgroundSpan {
        private String format = "%s-%s-%s";
        private String priceFormat = "$%s";
        private String singleDayFormat = "0%s";
        private int height;
        private int width;
        private int dayTextSize;
        private int priceTextSize;
        private int marginOne;
        private int marginTwo;
        // 用來讓選中的那個日期變白色字體
        private boolean keepWhite;

        PriceSpan() {
            height = Utils.dip2px(GotoBusApplication.getAppContext(), 54);
            width = Utils.dip2px(GotoBusApplication.getAppContext(), 40);
            dayTextSize = UIUtil.sp2px(GotoBusApplication.getAppContext(), 17);
            priceTextSize = UIUtil.sp2px(GotoBusApplication.getAppContext(), 11);
            marginOne = Utils.dip2px(GotoBusApplication.getAppContext(), 6);
            marginTwo = Utils.dip2px(GotoBusApplication.getAppContext(), 10);
        }

        @Override
        public void drawBackground(Canvas canvas, Paint paint,
                                   int left, int right, int top, int baseline, int bottom,
                                   CharSequence charSequence,
                                   int start, int end, int lineNum) {
            canvas.save();
            int oldColor = paint.getColor();
            float oldTextSize = paint.getTextSize();
            keepWhite = false;
            int Y = (height - bottom) / 2;
            canvas.translate(0, -Y);
            String day = charSequence.toString();
            Date date = null;
            try {
                date = DateUtils.sdf3.parse(String.format(format, year, month, day));
                if (dates != null && dates.size() > 0) {
                    if (dates.get(0).getDate().getTime() == date.getTime() ||
                            dates.get(dates.size() - 1).getDate().getTime() == date.getTime()) {
                        keepWhite = true;
                    }
                }
            } catch (ParseException e) {
                e.printStackTrace();
            }
            canvas.translate(0, Y);
            // 繪制日期
            paint.setTextSize(dayTextSize);
            Rect rectDay = new Rect();
            paint.getTextBounds(day, 0, charSequence.length(), rectDay);
            // 判斷日期在當(dāng)前時間之前就變灰色
            if (keepWhite) {
                paint.setColor(Color.WHITE);
            } else if (currentDate.before(date)) {
                paint.setColor(Color.BLACK);
            } else {
                paint.setColor(Color.GRAY);
            }
            canvas.drawText(day, (width - rectDay.width()) / 2, marginOne, paint);
            // 繪制價格
            if (hasPrice) {
                //個位的日期前面加上一個0
                if (day.length() == 1) {
                    day = String.format(singleDayFormat, day);
                }
                String currentDay = String.format(format, year, month, day);
                String price = priceMap.get(currentDay);
                if (price != null) {
                    String priceString = String.format(priceFormat, price);
                    if (keepWhite) {
                        paint.setColor(Color.WHITE);
                    } else if (price.equals(lowestData)) {
                        paint.setColor(Color.parseColor("#34b03d"));
                    } else {
                        paint.setColor(Color.parseColor("#999999"));
                    }
                    paint.setTextSize(priceTextSize);
                    Rect rect = new Rect();
                    paint.getTextBounds(priceString, 0, priceString.length(), rect);
                    canvas.drawText(priceString, (width - rect.width()) / 2,
                            rectDay.height() + marginOne + marginTwo, paint);
                }
            }

            paint.setColor(oldColor);
            paint.setTextSize(oldTextSize);
            canvas.restore();
        }
    }
  • Canvas canvas:畫布 通過這個來繪制自己的樣式娃圆。但是要注意它是已經(jīng)移動過的玫锋,移動的距離可以通過(height (整個item的高度)- bottom(參數(shù))) / 2來計算;

  • paint:注意保存之前的屬性。不然會引起繪制位置不準(zhǔn)確讼呢。

  • int left, int right, int top, int baseline, int bottom 這些參數(shù)都是框架內(nèi)繪制日期的位置參數(shù)撩鹿。canvas位置就是移動到了這里,所以left和top都是0悦屏,right就是整體的寬度节沦,bottom就是Day日期文本的高度。

  • CharSequence charSequence:當(dāng)前Day日期础爬。(只有Day 沒有year和month甫贯,我覺得這里比較坑。)

  • 54dp是我的item高度 40是寬度看蚜。

這段代碼是包含了一個日期的繪制叫搁,因為它框架內(nèi)的日期是居中的,導(dǎo)致留給我顯示價格的地方太短了 不美觀供炎,所以我把原來的文本設(shè)置為透明色(calendarView.setDateTextAppearance(R.style.calendar_date_text);)隱藏了起來渴逻,然后自己在合適的位置繪制日期。這么做就會讓框架原來的一些效果消失(例如之前的日期變灰色等)音诫,所以還需要另外自己處理這些問題惨奕。

OK 價格部分就介紹完了。

二纽竣、自定義背景色

首先自定義背景色有兩種方法:

  1. 就是固定的選中背景色墓贿,也就是大家都一樣,沒有什么頭尾的蜓氨。
  2. 就是我這種設(shè)計的背景色聋袋,頭尾不一樣的樣式。

第一種

建議就使用

@Override
public void decorate(DayViewFacade view) {
    view.setSelectionDrawable(drawable);
}

來設(shè)置一個Drawable來替換掉原來的背景圈穴吹,其他的部分框架已經(jīng)幫你處理好了幽勒。

第二種

就比較麻煩了,

  1. 首先你需要將原本框架的背景色那個圓的去掉港令,就是按第一種方法給它設(shè)置一個透明的drawable啥容。

  2. 為了避免上文提到的拖動坑的情況,我們要寫多個文件來達到效果顷霹。

    這是我左邊開始的背景文件咪惠,以此為參考。

    /**
     * Created by Vito on ${date}
     */
    public class LeftSelectedDecorator implements DayViewDecorator, OnDateSelectedListener, OnRangeSelectedListener {
        private List<CalendarDay> dates;
    
        public LeftSelectedDecorator(List<CalendarDay> dates) {
            if (dates != null && dates.size() > 0) {
                this.dates = dates;
            } else {
                this.dates = new ArrayList<>();
            }
        }
    
        @Override
        public boolean shouldDecorate(CalendarDay day) {
            if (dates.size() > 1) {
                if (day.getDate().getTime() == dates.get(0).getDate().getTime()) {
                    return true;
                } else {
                    return false;
                }
            } else {
                return false;
            }
        }
    
        @Override
        public void decorate(DayViewFacade view) {
            view.addSpan(new BackgroundSpan());
        }
    
        @Override
        public void onRangeSelected(@NonNull MaterialCalendarView widget, @NonNull List<CalendarDay> dates) {
            this.dates = dates;
        }
    
        @Override
        public void onDateSelected(@NonNull MaterialCalendarView widget, @NonNull CalendarDay date, boolean selected) {
            dates.clear();
            if (selected) {
                dates.add(date);
            }
        }
    
        public class BackgroundSpan implements LineBackgroundSpan {
            private int round;
            private int height;
            private int width;
    
            BackgroundSpan() {
                height = Utils.dip2px(GotoBusApplication.getAppContext(), 54);
                width = Utils.dip2px(GotoBusApplication.getAppContext(), 40);
                round = Utils.dip2px(GotoBusApplication.getAppContext(), 6);
            }
    
            @Override
            public void drawBackground(Canvas canvas, Paint paint,
                                       int left, int right, int top, int baseline, int bottom,
                                       CharSequence charSequence,
                                       int start, int end, int lineNum) {
                canvas.save();
                int oldColor = paint.getColor();
                float oldTextSize = paint.getTextSize();
                canvas.translate(0, -(height - bottom) / 2);
                paint.setColor(Color.parseColor("#ff8945"));
                RectF rect = new RectF(0, 0, width + round, height);
                canvas.drawRoundRect(rect, round, round, paint);
                paint.setColor(oldColor);
                paint.setTextSize(oldTextSize);
                canvas.restore();
            }
        }
    }
    

注意:多個Decorator添加要注意順序淋淀,會蓋住遥昧,一般是在文本最后。

calendarView.addDecorators(leftSelectedDecorator, rightSelectedDecorator,
        singleSelectedDecorator, selectedDecorator, priceDecorator);

注意:監(jiān)聽需要分發(fā)一下

calendarView.setOnRangeSelectedListener(new OnRangeSelectedListener() {
    @Override
    public void onRangeSelected(@NonNull MaterialCalendarView widget, @NonNull List<CalendarDay> dates) {
        leftSelectedDecorator.onRangeSelected(widget, dates);
        rightSelectedDecorator.onRangeSelected(widget, dates);
        singleSelectedDecorator.onRangeSelected(widget, dates);
        selectedDecorator.onRangeSelected(widget, dates);
        priceDecorator.onRangeSelected(widget, dates);
    }
});
calendarView.setOnDateChangedListener(new OnDateSelectedListener() {
            @Override
            public void onDateSelected(@NonNull MaterialCalendarView widget, @NonNull CalendarDay date, boolean selected) {
                leftSelectedDecorator.onDateSelected(widget, date, selected);
                rightSelectedDecorator.onDateSelected(widget, date, selected);
                singleSelectedDecorator.onDateSelected(widget, date, selected);
                selectedDecorator.onDateSelected(widget, date, selected);
                priceDecorator.onDateSelected(widget, date, selected);
            }
        });

四、完工

這里就是全部內(nèi)容了炭臭,覺得有幫助的話點個贊叭永脓!什么問題的話可以直接評論說啦!如果有更好的解決方案也希望可以教教我鞋仍。特別是可以完美的填掉我那個坑的方案常摧!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市威创,隨后出現(xiàn)的幾起案子落午,更是在濱河造成了極大的恐慌,老刑警劉巖肚豺,帶你破解...
    沈念sama閱讀 219,188評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件板甘,死亡現(xiàn)場離奇詭異,居然都是意外死亡详炬,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評論 3 395
  • 文/潘曉璐 我一進店門寞奸,熙熙樓的掌柜王于貴愁眉苦臉地迎上來呛谜,“玉大人,你說我怎么就攤上這事枪萄∫海” “怎么了?”我有些...
    開封第一講書人閱讀 165,562評論 0 356
  • 文/不壞的土叔 我叫張陵瓷翻,是天一觀的道長聚凹。 經(jīng)常有香客問我,道長齐帚,這世上最難降的妖魔是什么妒牙? 我笑而不...
    開封第一講書人閱讀 58,893評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮对妄,結(jié)果婚禮上湘今,老公的妹妹穿的比我還像新娘。我一直安慰自己剪菱,他們只是感情好摩瞎,可當(dāng)我...
    茶點故事閱讀 67,917評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著孝常,像睡著了一般旗们。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上构灸,一...
    開封第一講書人閱讀 51,708評論 1 305
  • 那天上渴,我揣著相機與錄音,去河邊找鬼。 笑死驰贷,一個胖子當(dāng)著我的面吹牛盛嘿,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播括袒,決...
    沈念sama閱讀 40,430評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼次兆,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了锹锰?” 一聲冷哼從身側(cè)響起芥炭,我...
    開封第一講書人閱讀 39,342評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎恃慧,沒想到半個月后园蝠,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,801評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡痢士,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,976評論 3 337
  • 正文 我和宋清朗相戀三年彪薛,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片怠蹂。...
    茶點故事閱讀 40,115評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡善延,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出城侧,到底是詐尸還是另有隱情易遣,我是刑警寧澤,帶...
    沈念sama閱讀 35,804評論 5 346
  • 正文 年R本政府宣布嫌佑,位于F島的核電站豆茫,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏屋摇。R本人自食惡果不足惜揩魂,卻給世界環(huán)境...
    茶點故事閱讀 41,458評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望炮温。 院中可真熱鬧肤京,春花似錦、人聲如沸茅特。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,008評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽白修。三九已至妒峦,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間兵睛,已是汗流浹背肯骇。 一陣腳步聲響...
    開封第一講書人閱讀 33,135評論 1 272
  • 我被黑心中介騙來泰國打工窥浪, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人笛丙。 一個月前我還...
    沈念sama閱讀 48,365評論 3 373
  • 正文 我出身青樓漾脂,卻偏偏與公主長得像,于是被迫代替她去往敵國和親胚鸯。 傳聞我的和親對象是個殘疾皇子骨稿,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,055評論 2 355