美文网首页定义控件功能专区UI效果仿写
Android开源的精美日历控件,热插拔设计的万能自定义UI

Android开源的精美日历控件,热插拔设计的万能自定义UI

作者: 黄海彬 | 来源:发表于2017-11-17 13:46 被阅读333次

    UI框架应该逻辑与界面实现分离,该日历控件使用了热插拔的设计 ,简单几步即可实现你需要的UI效果,热插拔的思想是你提供你的实现,我提供我的插座接口,与自定义Behavior是一样的思想。

    听说第一页无效果图就看不下去了?先上个高仿魅族日历界面

    魅族界面.png 魅族收缩.png

    项目开源地址

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

    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"

    效果预览

    高仿魅族日历界面

    魅族界面.png 魅族收缩.png

    快速年月份选择

    月份界面.png

    其它作者实现的几个UI效果预览,简单源码都在demo可以看到

    多彩风格界面

    多彩界面.png 多彩收缩.png

    下标风格界面

    下标界面.png 下标收缩.png

    简单没有农历界面

    简单界面.png 简单收缩.png

    更多参考用法移步APP Demo,里面作者实现了几种类型的风格,可以参考实现

    项目开源地址

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

    如果觉得源码可以请给个star,源码注释完善,简单易懂,容易学习。

    相关文章

      网友评论

      本文标题:Android开源的精美日历控件,热插拔设计的万能自定义UI

      本文链接:https://www.haomeiwen.com/subject/hpcjvxtx.html