Android開源的精美日歷控件,熱插拔設(shè)計(jì)的萬能自定義UI

UI框架應(yīng)該邏輯與界面實(shí)現(xiàn)分離,該日歷控件使用了熱插拔的設(shè)計(jì) ,簡(jiǎn)單幾步即可實(shí)現(xiàn)你需要的UI效果,熱插拔的思想是你提供你的實(shí)現(xiàn),我提供我的插座接口,與自定義Behavior是一樣的思想。

聽說第一頁(yè)無效果圖就看不下去了?先上個(gè)高仿魅族日歷界面

魅族界面.png
魅族收縮.png

項(xiàng)目開源地址

https://github.com/huanghaibin-dev/CalendarView

CalendarView的優(yōu)勢(shì):

1、熱插拔設(shè)計(jì),根據(jù)不同的UI需求完全自定義UI,簡(jiǎn)單幾步即可實(shí)現(xiàn),自定義事件日歷標(biāo)記、顏色、農(nóng)歷等

2、完全Canvas繪制,性能和速度都很不錯(cuò),相比大多數(shù)基于GridView或RecyclerView實(shí)現(xiàn)的占用內(nèi)存更低,啟動(dòng)速度更快

3、支持收縮、展開、快速年月份選擇等

4、簡(jiǎn)潔易懂的源碼,易學(xué)習(xí)。

先看看控件的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" /> <!--天數(shù)文本大小-->
        <attr name="lunar_text_size" format="string" /> <!--農(nóng)歷文本大小-->

        <attr name="scheme_text" format="string" /> <!--標(biāo)記文本-->
        <attr name="scheme_text_color" format="color" /> <!--標(biāo)記文本顏色-->
        <attr name="scheme_month_text_color" format="color" /> <!--標(biāo)記天數(shù)文本顏色-->
        <attr name="scheme_lunar_text_color" format="color" /> <!--標(biāo)記農(nóng)歷文本顏色-->

        <attr name="scheme_theme_color" format="color" /> <!--標(biāo)記的顏色-->

        <attr name="selected_theme_color" format="color" /> <!--選中顏色-->
        <attr name="selected_text_color" format="color" /> <!--選中文本顏色-->
        <attr name="selected_lunar_text_color" format="color" /> <!--選中農(nóng)歷文本顏色-->

        <attr name="current_month_text_color" format="color" /> <!--當(dāng)前月份的字體顏色-->
        <attr name="other_month_text_color" format="color" /> <!--其它月份的字體顏色-->

        <attr name="current_month_lunar_text_color" format="color" /> <!--當(dāng)前月份農(nóng)歷節(jié)假日顏色-->
        <attr name="other_month_lunar_text_color" format="color" /> <!--其它月份農(nóng)歷節(jié)假日顏色-->

        <attr name="min_year" format="integer" />  <!--最小年份1900-->
        <attr name="max_year" format="integer" /> <!--最大年份2099-->
        
</declare-styleable>

XML用法

如果需要在日歷控件下方使用其它控件,使用CalendarLayout控件即可,calendar_content_view_id為其它控件的id,支持任意控件,如RecyclerView、ListView。CalendarView的calendar_card_view為任意自定義實(shí)現(xiàn)的日歷繪制控件路徑。

<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>

熟悉一下這幾個(gè)簡(jiǎn)單的特性,看看日歷內(nèi)容界面的繪制BaseCalendarCardView,根據(jù)需求實(shí)現(xiàn)以下部分方法即可

   /**
     * 開始繪制前的回調(diào)鉤子,這里做一些初始化的操作,每次繪制只調(diào)用一次,性能高效
     * 沒有需要可忽略不實(shí)現(xiàn)
     * 例如:
     * 1、需要繪制圓形標(biāo)記事件背景,可以在這里計(jì)算半徑
     * 2、繪制矩形選中效果,也可以在這里計(jì)算矩形寬和高
     */
    protected void onPreviewHook() {
        // TODO: 2017/11/16
    }


    /**
     * 循環(huán)繪制開始的回調(diào),不需要可忽略
     * 繪制每個(gè)日歷項(xiàng)的循環(huán),用來計(jì)算baseLine、圓心坐標(biāo)等都可以在這里實(shí)現(xiàn)
     *
     * @param x 日歷Card x起點(diǎn)坐標(biāo)
     * @param y 日歷Card y起點(diǎn)坐標(biāo)
     */
    protected void onLoopStart(int x, int y) {
        // TODO: 2017/11/16  
    }

    /**
     * 繪制選中的日期
     *
     * @param canvas    canvas
     * @param calendar  日歷日歷calendar
     * @param x         日歷Card x起點(diǎn)坐標(biāo)
     * @param y         日歷Card y起點(diǎn)坐標(biāo)
     * @param hasScheme hasScheme 非標(biāo)記的日期
     */
    protected abstract void onDrawSelected(Canvas canvas, Calendar calendar, int x, int y, boolean hasScheme);

    /**
     * 繪制標(biāo)記的日期UI
     *
     * @param canvas   canvas
     * @param calendar 日歷calendar
     * @param x        日歷Card x起點(diǎn)坐標(biāo)
     * @param y        日歷Card y起點(diǎn)坐標(biāo)
     */
    protected abstract void onDrawScheme(Canvas canvas, Calendar calendar, int x, int y);


    /**
     * 繪制日歷文本
     *
     * @param canvas     canvas
     * @param calendar   日歷calendar
     * @param x          日歷Card x起點(diǎn)坐標(biāo)
     * @param y          日歷Card y起點(diǎn)坐標(biāo)
     * @param hasScheme  是否是標(biāo)記的日期
     * @param isSelected 是否選中
     */
    protected abstract void onDrawText(Canvas canvas, Calendar calendar, int x, int y, boolean hasScheme, boolean isSelected);

舉個(gè)例子:如果你的需求是類似魅族日歷的UI,那么第一步,繼承BaseCalendarCardView,然后實(shí)現(xiàn)onDrawSelected、onDrawScheme、onDrawText三個(gè)回調(diào)函數(shù)即可

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起點(diǎn)坐標(biāo)
     * @param y         日歷Card y起點(diǎn)坐標(biāo)
     * @param hasScheme hasScheme 非標(biāo)記的日期
     */
    @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);
    }

    /**
     * 繪制標(biāo)記的日期UI 這里魅族界面不需要繪制多彩風(fēng)格,忽略即可
     *
     * @param canvas   canvas
     * @param calendar 日歷calendar
     * @param x        日歷Card x起點(diǎn)坐標(biāo)
     * @param y        日歷Card y起點(diǎn)坐標(biāo)
     */
    @Override
    protected void onDrawScheme(Canvas canvas, Calendar calendar, int x, int y) {

    }

    /**
     * 繪制日歷文本
     *
     * @param canvas     canvas
     * @param calendar   日歷calendar
     * @param x          日歷Card x起點(diǎn)坐標(biāo)
     * @param y          日歷Card y起點(diǎn)坐標(biāo)
     * @param hasScheme  是否是標(biāo)記的日期
     * @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);
            //繪制農(nóng)歷
            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轉(zhuǎn)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"

效果預(yù)覽

高仿魅族日歷界面

魅族界面.png
魅族收縮.png

快速年月份選擇

月份界面.png

其它作者實(shí)現(xiàn)的幾個(gè)UI效果預(yù)覽,簡(jiǎn)單源碼都在demo可以看到

多彩風(fēng)格界面

多彩界面.png
多彩收縮.png

下標(biāo)風(fēng)格界面

下標(biāo)界面.png
下標(biāo)收縮.png

簡(jiǎn)單沒有農(nóng)歷界面

簡(jiǎn)單界面.png
簡(jiǎn)單收縮.png

更多參考用法移步APP Demo,里面作者實(shí)現(xiàn)了幾種類型的風(fēng)格,可以參考實(shí)現(xiàn)

項(xiàng)目開源地址

https://github.com/huanghaibin-dev/CalendarView

如果覺得源碼可以請(qǐng)給個(gè)star,源碼注釋完善,簡(jiǎn)單易懂,容易學(xué)習(xí)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 178,781評(píng)論 25 709
  • 內(nèi)容抽屜菜單ListViewWebViewSwitchButton按鈕點(diǎn)贊按鈕進(jìn)度條TabLayout圖標(biāo)下拉刷新...
    皇小弟閱讀 47,134評(píng)論 22 665
  • 魔姝奶茶是一款健康安全輕松的減肥產(chǎn)品? 攝入<消耗=健康安全輕松減肥? 飽足感長(zhǎng)達(dá)5小時(shí)? 每杯僅有200大卡,代...
    霞霞姐魔姝創(chuàng)始人閱讀 410評(píng)論 0 0
  • “耍賴”大多分兩種:一種是使用無賴的手段;另一種是抵賴,不承認(rèn)自己的錯(cuò)誤或責(zé)任。 其實(shí)我想寫這篇文章的本意是帶大家...
    Edwin222222閱讀 1,598評(píng)論 1 0
  • 有多少人是深愛著對(duì)方 但是卻不能在一起 不是不能而是根本沒辦法在一起 或許真的是末途了吧 真正愛一個(gè)人 不是擁有 ...
    何處不是歸途閱讀 175評(píng)論 0 0

友情鏈接更多精彩內(nèi)容