Android開源的精美日曆控件,熱插拔設計的萬能自定義UIandroid
UI框架應該邏輯與界面實現分離,該日曆控件使用了熱插拔的設計 ,簡單幾步便可實現你須要的UI效果,熱插拔的思想是你提供你的實現,我提供個人插座接口,與自定義Behavior是同樣的思想。git
CalendarView的優點:github
一、熱插拔設計,根據不一樣的UI需求徹底自定義UI,簡單幾步便可實現,自定義事件日曆標記、顏色、農曆等canvas
二、徹底Canvas繪製,性能和速度都很不錯,相比大多數基於GridView或RecyclerView實現的佔用內存更低,啓動速度更快app
三、支持收縮、展開、快速年月份選擇等框架
四、簡潔易懂的源碼,易學習。ide
先看看控件的attr函數
<declare-styleable name="CalendarView"> <attr name="calendar_card_view" format="color" /> <!--熱插拔自定義類路徑--> <attr name="week_background" format="color" /> <!--星期欄的背景--> <attr name="week_text_color" format="color" /> <!--星期欄文本顏色--> <attr name="current_day_text_color" format="color" /> <!--今天的文本顏色--> <attr name="day_text_size" format="string" /> <!--天數文本大小--> <attr name="lunar_text_size" format="string" /> <!--農曆文本大小--> <attr name="scheme_text" format="string" /> <!--標記文本--> <attr name="scheme_text_color" format="color" /> <!--標記文本顏色--> <attr name="scheme_month_text_color" format="color" /> <!--標記天數文本顏色--> <attr name="scheme_lunar_text_color" format="color" /> <!--標記農曆文本顏色--> <attr name="scheme_theme_color" format="color" /> <!--標記的顏色--> <attr name="selected_theme_color" format="color" /> <!--選中顏色--> <attr name="selected_text_color" format="color" /> <!--選中文本顏色--> <attr name="selected_lunar_text_color" format="color" /> <!--選中農曆文本顏色--> <attr name="current_month_text_color" format="color" /> <!--當前月份的字體顏色--> <attr name="other_month_text_color" format="color" /> <!--其它月份的字體顏色--> <attr name="current_month_lunar_text_color" format="color" /> <!--當前月份農曆節假日顏色--> <attr name="other_month_lunar_text_color" format="color" /> <!--其它月份農曆節假日顏色--> <attr name="min_year" format="integer" /> <!--最小年份1900--> <attr name="max_year" format="integer" /> <!--最大年份2099--> </declare-styleable>
XML用法oop
若是須要在日曆控件下方使用其它控件,使用CalendarLayout控件便可,calendar_content_view_id爲其它控件的id,支持任意控件,如RecyclerView、ListView。CalendarView的calendar_card_view爲任意自定義實現的日曆繪製控件路徑。性能
<com.haibin.calendarview.CalendarLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:background="#fff" app:calendar_content_view_id="@+id/linearView"> <com.haibin.calendarview.CalendarView android:id="@+id/calendarView" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="#fff" app:current_month_text_color="#333333" app:current_month_lunar_text_color="#CFCFCF" app:min_year="2004" app:other_month_text_color="#e1e1e1" app:scheme_text_color="#333" app:scheme_theme_color="#128c4b" app:selected_lunar_text_color="#CFCFCF" app:calendar_card_view="com.haibin.calendarviewproject.meizu.MeiZuCalendarCardView" app:selected_text_color="#333" app:selected_theme_color="#108cd4" app:week_background="#fff" app:week_text_color="#111" /> <LinearLayout android:id="@+id/linearView" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/content_background" android:clickable="true" android:orientation="vertical" tools:ignore="KeyboardInaccessibleWidget"/> </LinearLayout> </com.haibin.calendarview.CalendarLayout>
熟悉一下這幾個簡單的特性,看看日曆內容界面的繪製BaseCalendarCardView,根據需求實現如下部分方法便可
/** * 開始繪製前的回調鉤子,這裏作一些初始化的操做,每次繪製只調用一次,性能高效 * 沒有須要可忽略不實現 * 例如: * 一、須要繪製圓形標記事件背景,能夠在這裏計算半徑 * 二、繪製矩形選中效果,也能夠在這裏計算矩形寬和高 */ protected void onPreviewHook() { // TODO: 2017/11/16 } /** * 循環繪製開始的回調,不須要可忽略 * 繪製每一個日曆項的循環,用來計算baseLine、圓心座標等均可以在這裏實現 * * @param x 日曆Card x起點座標 * @param y 日曆Card y起點座標 */ protected void onLoopStart(int x, int y) { // TODO: 2017/11/16 } /** * 繪製選中的日期 * * @param canvas canvas * @param calendar 日曆日曆calendar * @param x 日曆Card x起點座標 * @param y 日曆Card y起點座標 * @param hasScheme hasScheme 非標記的日期 */ protected abstract void onDrawSelected(Canvas canvas, Calendar calendar, int x, int y, boolean hasScheme); /** * 繪製標記的日期UI * * @param canvas canvas * @param calendar 日曆calendar * @param x 日曆Card x起點座標 * @param y 日曆Card y起點座標 */ protected abstract void onDrawScheme(Canvas canvas, Calendar calendar, int x, int y); /** * 繪製日曆文本 * * @param canvas canvas * @param calendar 日曆calendar * @param x 日曆Card x起點座標 * @param y 日曆Card y起點座標 * @param hasScheme 是不是標記的日期 * @param isSelected 是否選中 */ protected abstract void onDrawText(Canvas canvas, Calendar calendar, int x, int y, boolean hasScheme, boolean isSelected);
舉個例子:若是你的需求是相似魅族日曆的UI,那麼第一步,繼承BaseCalendarCardView,而後實現onDrawSelected、onDrawScheme、onDrawText三個回調函數便可
public class MeiZuCalendarCardView extends BaseCalendarCardView { private Paint mTextPaint = new Paint(); private Paint mSchemeBasicPaint = new Paint(); private float mRadio; private int mPadding; private float mSchemeBaseLine; public MeiZuCalendarCardView(Context context) { super(context); mTextPaint.setTextSize(dipToPx(context, 8)); mTextPaint.setColor(0xff111111); mTextPaint.setAntiAlias(true); mTextPaint.setFakeBoldText(true); mSchemeBasicPaint.setAntiAlias(true); mSchemeBasicPaint.setStyle(Paint.Style.FILL); mSchemeBasicPaint.setTextAlign(Paint.Align.CENTER); mSchemeBasicPaint.setColor(0xffed5353); mSchemeBasicPaint.setFakeBoldText(true); mRadio = dipToPx(getContext(), 7); mPadding = dipToPx(getContext(), 4); Paint.FontMetrics metrics = mSchemeBasicPaint.getFontMetrics(); mSchemeBaseLine = mRadio - metrics.descent + (metrics.bottom - metrics.top) / 2 + dipToPx(getContext(), 1); } /** * 繪製選中的日期 * * @param canvas canvas * @param calendar 日曆日曆calendar * @param x 日曆Card x起點座標 * @param y 日曆Card y起點座標 * @param hasScheme hasScheme 非標記的日期 */ @Override protected void onDrawSelected(Canvas canvas, Calendar calendar, int x, int y, boolean hasScheme) { mSelectedPaint.setStyle(Paint.Style.FILL); mSelectedPaint.setColor(0x80cfcfcf); canvas.drawRect(x + mPadding, y + mPadding, x + mItemWidth - mPadding, y + mItemHeight - mPadding, mSelectedPaint); } /** * 繪製標記的日期UI 這裏魅族界面不須要繪製多彩風格,忽略便可 * * @param canvas canvas * @param calendar 日曆calendar * @param x 日曆Card x起點座標 * @param y 日曆Card y起點座標 */ @Override protected void onDrawScheme(Canvas canvas, Calendar calendar, int x, int y) { } /** * 繪製日曆文本 * * @param canvas canvas * @param calendar 日曆calendar * @param x 日曆Card x起點座標 * @param y 日曆Card y起點座標 * @param hasScheme 是不是標記的日期 * @param isSelected 是否選中 */ @Override protected void onDrawText(Canvas canvas, Calendar calendar, int x, int y, boolean hasScheme, boolean isSelected) { int cx = x + mItemWidth / 2; int top = y - mItemHeight / 6; if (hasScheme) { //繪製日期 canvas.drawText(String.valueOf(calendar.getDay()), cx, mTextBaseLine + top, calendar.isCurrentDay() ? mCurDayTextPaint : calendar.isCurrentMonth() ? mSchemeTextPaint : mOtherMonthTextPaint); //繪製農曆 canvas.drawText(calendar.getLunar(), cx, mTextBaseLine + y + mItemHeight / 10, mCurMonthLunarTextPaint); mTextPaint.setColor(Color.WHITE); mSchemeBasicPaint.setColor(calendar.getSchemeColor()); //繪製圓圈 canvas.drawCircle(x + mItemWidth - mPadding - mRadio / 2, y + mPadding + mRadio, mRadio, mSchemeBasicPaint); //繪製事件文本 canvas.drawText(calendar.getScheme(), x + mItemWidth - mPadding - mRadio, y + mPadding + mSchemeBaseLine, mTextPaint); } else { canvas.drawText(String.valueOf(calendar.getDay()), cx, mTextBaseLine + top, calendar.isCurrentDay() ? mCurDayTextPaint : calendar.isCurrentMonth() ? mCurMonthTextPaint : mOtherMonthTextPaint); canvas.drawText(calendar.getLunar(), cx, mTextBaseLine + y + mItemHeight / 10, mCurMonthLunarTextPaint); } } /** * dp轉px * * @param context context * @param dpValue dp * @return px */ private static int dipToPx(Context context, float dpValue) { final float scale = context.getResources().getDisplayMetrics().density; return (int) (dpValue * scale + 0.5f); } }
第二步:使用方法、app:calendar_card_view="xxx.xx.MeiZuCalendarCardView"
魅族風格日曆效果預覽
快速年月份選擇
其它做者實現的幾個UI效果預覽,簡單源碼都在demo能夠看到
多彩風格界面
下標風格界面
簡單沒有農曆界面
更多參考用法移步APP Demo,裏面做者實現了幾種類型的風格,能夠參考實現
項目開源地址
https://github.com/huanghaibin-dev/CalendarView
若是以爲源碼能夠請給個star,源碼註釋完善,簡單易懂,容易學習。