撸一个Android高性能日历控件,高仿魅族

Android原生的CalendarView根本无法满足我们日常开发的需要,在开发吾记APP的过程中,我觉得需要来一款高性能且美观简洁的日历控件,觉得魅族的日历风格十分适合,于是打算撸一款。

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

compile ‘com.haibin:calendarview:1.0.2‘

先上效果图:

动手之前我们需要分析一下魅族是怎么设计如此高性能的日历的,我们打开开发者选项中的显示布局边界:

好吧,一开始我以为日历界面是ViewPager+RecyclerView的,但是这么一看明显就不是了,如果是RecyclerView,那么我们假设每个月的卡片都有5*7=35个item,每个item根布局是RelativeLayout+3个TextView,我们大概估算一下日历初始化时要加载的控件:

3个ViewPager的item * 35个RecyclerView的Item * 4(每个item的控件数) + 8 (星期栏)= 420+

我的天,这可不能这么干,明显性能大打折扣,我们再来看看月份控件:

好吧,这里看上去就是ViewPager+RecyclerView来做的,每个RecyclerView的item都只是一个控件,里面绘制了文本 ,这里大概就分析清楚了。

我们采取折中的方式,日历界面和月份卡界面均采用ViewPager+RecyclerView的方式,不同的是所有的item我们都采用自定义ViewCanvas绘制的方式来做,这样性能虽然比不上魅族,但速度体验基本差不多,下面先看日历界面的item代码:只需要绘制3个文本即可

public class CellView extends View {

    private int mDay = 20;
    private String mLunar;
    private String mScheme;
    private Paint mDayPaint = new Paint();
    private Paint mLunarPaint = new Paint();
    private Paint mSchemePaint = new Paint();
    private Paint mCirclePaint = new Paint();
    private int mRadius;
    private int mCirclePadding;
    private int mCircleColor;

    public CellView(Context context) {
        this(context, null);
    }

    public CellView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);

        mDayPaint.setAntiAlias(true);
        mDayPaint.setColor(Color.BLACK);
        mDayPaint.setFakeBoldText(true);
        mDayPaint.setTextAlign(Paint.Align.CENTER);

        mLunarPaint.setAntiAlias(true);
        mLunarPaint.setColor(Color.GRAY);
        mLunarPaint.setTextAlign(Paint.Align.CENTER);

        mSchemePaint.setAntiAlias(true);
        mSchemePaint.setColor(Color.WHITE);
        mSchemePaint.setFakeBoldText(true);
        mSchemePaint.setTextAlign(Paint.Align.CENTER);

        mCirclePaint.setAntiAlias(true);
        mCirclePaint.setStyle(Paint.Style.FILL);

        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.CellView);
        mDayPaint.setTextSize(array.getDimensionPixelSize(R.styleable.CellView_cell_day_text_size, 18));
        mLunarPaint.setTextSize(array.getDimensionPixelSize(R.styleable.CellView_cell_lunar_text_size, 12));
        mRadius = (int) array.getDimension(R.styleable.CellView_cell_scheme_radius, 8);
        mSchemePaint.setTextSize(array.getDimensionPixelSize(R.styleable.CellView_cell_scheme_text_size, 6));
        mCirclePadding = array.getDimensionPixelSize(R.styleable.CellView_cell_circle_padding, 4);
        mCirclePaint.setColor(array.getColor(R.styleable.CellView_cell_circle_color, 0xff16BB7F));
        array.recycle();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        int width = getWidth();
        int height = getHeight();
        int w = (width - getPaddingLeft() - getPaddingRight());
        int h = (height - getPaddingTop() - getPaddingBottom()) / 4;
        canvas.drawText(String.valueOf(mDay), w / 2, 2 * h + getPaddingTop(), mDayPaint);
        canvas.drawText(mLunar, w / 2, 4 * h + getPaddingTop(), mLunarPaint);
        if (!TextUtils.isEmpty(mScheme)) {
            canvas.drawCircle(w / 2 + mCirclePadding + mDayPaint.getTextSize(), getPaddingTop() + h, mRadius, mCirclePaint);
            canvas.drawText(mScheme, w / 2 + mCirclePadding + mDayPaint.getTextSize(), getPaddingTop() + mRadius / 2 + h, mSchemePaint);
        }
    }

    /**
     * 初始化日历
     * @param day 天
     * @param lunar 农历
     * @param scheme 事件标记
     */
    void init(int day, String lunar, String scheme) {
        this.mDay = day;
        this.mLunar = lunar;
        this.mScheme = scheme;
    }

    void setTextColor(int textColor) {
        mDayPaint.setColor(textColor);
        mLunarPaint.setColor(textColor);
    }

    void setCircleColor(int circleColor) {
        mCirclePaint.setColor(circleColor);
        invalidate();
    }
}

月份卡自定义View

public class MonthView extends View {
    private int mDiff;//第一天偏离周日多少天
    private int mCount;//总数
    private int mLastCount;//最后一行的天数
    private int mLine;//多少行
    private Paint mPaint = new Paint();
    private Paint mSchemePaint = new Paint();
    private List<Calendar> mSchemes;
    private Calendar mCalendar;

    public MonthView(Context context) {
        this(context, null);
    }

    public MonthView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        mPaint.setAntiAlias(true);
        mPaint.setTextAlign(Paint.Align.CENTER);
        mSchemePaint.setAntiAlias(true);
        mSchemePaint.setTextAlign(Paint.Align.CENTER);
        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.MonthView);
        mPaint.setTextSize(array.getDimensionPixelSize(R.styleable.MonthView_month_view_text_size, 12));
        mSchemePaint.setTextSize(array.getDimensionPixelSize(R.styleable.MonthView_month_view_text_size, 12));
        mPaint.setColor(array.getColor(R.styleable.MonthView_month_view_text_color, Color.BLACK));
        mSchemePaint.setColor(array.getColor(R.styleable.MonthView_month_view_remark_color, Color.RED));
        array.recycle();
        measureLine();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        int width = getWidth();
        int height = getHeight();
        int pLeft = getPaddingLeft();
        int w = (width - getPaddingLeft() - getPaddingRight()) / 7;
        int h = (height - getPaddingTop() - getPaddingBottom()) / 6;
        int d = 0;
        for (int i = 0; i < mLine; i++) {
            if (i == 0) {//第一行
                for (int j = 0; j < (7 - mDiff); j++) {
                    ++d;
                    canvas.drawText(String.valueOf(j + 1), mDiff * w + j * w + pLeft + w / 2, h, isScheme(d) ? mSchemePaint : mPaint);
                }
            } else if (i == mLine - 1 && mLastCount != 0) {
                int first = mCount - mLastCount + 1;
                for (int j = 0; j < mLastCount; j++) {
                    ++d;
                    canvas.drawText(String.valueOf(first), j * w + pLeft + w / 2, (i + 1) * h, isScheme(d) ? mSchemePaint : mPaint);
                    ++first;
                }
            } else {
                int first = i * 7 - mDiff + 1;
                for (int j = 0; j < 7; j++) {
                    ++d;
                    canvas.drawText(String.valueOf(first), j * w + pLeft + w / 2, (i + 1) * h, isScheme(d) ? mSchemePaint : mPaint);
                    ++first;
                }
            }
        }
    }

    /**
     * 计算行数
     */
    private void measureLine() {
        int offset = mCount - (7 - mDiff);
        mLine = 1 + (offset % 7 == 0 ? 0 : 1) + offset / 7;
        mLastCount = offset % 7;
    }

    /**
     * 初始化月份卡
     * @param mDiff 偏离天数
     * @param mCount 当月总天数
     * @param mYear 哪一年
     * @param mMonth 哪一月
     */
     void init(int mDiff, int mCount, int mYear, int mMonth) {
        this.mDiff = mDiff;
        this.mCount = mCount;
        mCalendar = new Calendar();
        mCalendar.setYear(mYear);
        mCalendar.setMonth(mMonth);
        measureLine();
        invalidate();
    }

    void setSchemes(List<Calendar> mSchemes) {
        this.mSchemes = mSchemes;
    }

    void setSchemeColor(int schemeColor) {
        if (schemeColor != 0)
            mSchemePaint.setColor(schemeColor);
        if(schemeColor == 0xff30393E)
            mSchemePaint.setColor(Color.RED);
    }

    private boolean isScheme(int day) {
        if (mSchemes == null || mSchemes.size() == 0)
            return false;
        mCalendar.setDay(day);
        return mSchemes.contains(mCalendar);
    }
}

  

其它代码没有什么难度,日历算法是github上找的,更多详情请看仓库地址:https://github.com/huanghaibin-dev/CalendarView

时间: 2024-08-24 14:02:11

撸一个Android高性能日历控件,高仿魅族的相关文章

android 自定义日历控件

日历控件View: [java] view plaincopyprint? /** * 日历控件 功能:获得点选的日期区间 * */ public class CalendarView extends View implements View.OnTouchListener { private final static String TAG = "anCalendar"; private Date selectedStartDate; private Date selectedEndD

在iOS上实现一个简单的日历控件

转自:http://blog.csdn.net/jasonblog/article/details/21977481 近期需要写一个交互有点DT的日历控件,具体交互细节这里略过不表. 不过再怎么复杂的控件,也是由基础的零配件组装起来的,这里最基本的就是日历控件. 先上图: 从图中可以看出日历控件就是由一个个小方块组成的,每一行有7个小方块,分别表示一周的星期天到星期六. 给定一个月份,我们首先需要知道这个月有多少周.那么如何确定一个月有多少周呢? 我是这么想的,在NSDate上做扩展: [cpp

Android图表日历控件组件

1.图表引擎 - AChartEngine AChartEngine是一款基于Android的图表绘制引擎,它为Android开发人员提供了非常多有用的图表绘制工具类,假设你须要在Android应用中加入可视化统计的功能,那么AChartEngine是一个不错的选择. 官方站点:https://code.google.com/p/achartengine/ 2.图表引擎 - MPAndroidChart MPAndroidChart是一款基于Android的开源图表库.MPAndroidChar

Android自己定义控件实战——仿多看阅读平移翻页

转载请声明出处http://blog.csdn.net/zhongkejingwang/article/details/38728119 之前自己做的一个APP须要用到翻页阅读,网上看过立体翻页效果,只是bug太多了还不兼容.看了一下多看阅读翻页是採用平移翻页的,于是就仿写了一个平移翻页的控件.效果例如以下: 在翻页时页面右边缘绘制了阴影.效果还不错.要实现这样的平移翻页控件并不难,仅仅须要定义一个布局管理页面就能够了. 详细实现上有下面难点: 1.循环翻页,页面的反复利用. 2.在翻页时过滤掉

一个android的各种控件库

在这里 https://github.com/Trinea/android-open-project 很多的listview,非常棒

Android自定义控件之日历控件

Android自定义控件之日历控件 2015-10-23 Android开发中文站 三月份学习android,至今也有半年有余,中间也做过两个项目,但是依然感觉自己做的应用不是很有新意,比不上应用市场上那些应用如此绚丽.所以自己仍需继续努力.学习至今,仍感觉自定义控件是一块硬骨头,还没修炼到身后的内功,下面就切入正题,以一次项目的需求,来实现一个自定义的日历控件.效果图先来一发. 我们分析下效果图,然后确定我们的需求. (1).绘制星期的自定义View,用于标识日期的礼拜. (2).绘制日期的自

Android自定义View(CustomCalendar-定制日历控件)

转载请标明出处: http://blog.csdn.net/xmxkf/article/details/54020386 本文出自:[openXu的博客] 目录: 1分析 2自定义属性 3onMeasure 4onDraw 绘制月份 绘制星期 绘制日期及任务 5事件处理 源码下载 ??应项目需求,需要做一个日历控件,效果图如下: ???? ??接到需求后,没有立即查找是否有相关开源日历控件可用.系统日历控件是否能满足 ,第一反应就是这个控件该怎么画?谁叫咱自定义控件技术牛逼呢O(∩_∩)O哈哈~

Android UI-自定义日历控件

Android UI-自定义日历控件 本篇博客笔者给大家分享一个日历控件,这里有个需求:要求显示当前月的日期,左右可以切换月份来查看日期. 我们想一想会如何去实现这样的一个控件,有开源的,但可能不太满足我们的特定的需求,这里笔者自定义了一个,读者可以根据自己的需求来修改代码.下面来说一下实现的思路: 首先我们要显示当前月份,自然我们要计算出当前的日期,并且把每一天对应到具体的星期,我们会有以下效果: 我们先想一下这样的效果用什么控件可以实现?很自然可以想到用网格视图GridView,但这里笔者使

【无私分享】干货!!!一个炫酷的自定义日历控件,摆脱日历时间选择烦恼,纯福利~

最近公司项目中有一个按日期查看信息的功能,楼主本想用之前用的wheelView将就使用的,不过产品经理有个新要求,就是点击按钮弹出的日期选择对话框必须显示农历节假日,周几什么的.这可就难为人了,倘若使用之前的滚动时间选择器,无疑是难以实现的,楼主辗转反侧,冥思苦想,却不得正果. 好吧,去网上下了几个OA系统一用就有了idea,突然想到手机自带的日历~~,oh,year,日历就有这功能,瞧瞧,我靠,这个东西,咋做. 仔细一瞧,似乎用GridView可以实现,额,二话不说就开干.折腾了半天都没弄好,