Android開源的精美日歷控件,熱插拔設計的萬能自定義UI


Android開源的精美日歷控件,熱插拔設計的萬能自定義UI

UI框架應該邏輯與界面實現分離,該日歷控件使用了熱插拔的設計 ,簡單幾步即可實現你需要的UI效果,熱插拔的思想是你提供你的實現,我提供我的插座接口,與自定義Behavior是一樣的思想。

CalendarView的優勢:

1、熱插拔設計,根據不同的UI需求完全自定義UI,簡單幾步即可實現,自定義事件日歷標記、顏色、農歷等

2、完全Canvas繪制,性能和速度都很不錯,相比大多數基於GridView或RecyclerView實現的占用內存更低,啟動速度更快

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

4、簡潔易懂的源碼,易學習。

先看看控件的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用法

如果需要在日歷控件下方使用其它控件,使用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,根據需求實現以下部分方法即可

/**
     * 開始繪制前的回調鈎子,這里做一些初始化的操作,每次繪制只調用一次,性能高效
     * 沒有需要可忽略不實現
     * 例如:
     * 1、需要繪制圓形標記事件背景,可以在這里計算半徑
     * 2、繪制矩形選中效果,也可以在這里計算矩形寬和高
     */
    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,源碼注釋完善,簡單易懂,容易學習。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM