擼一個Android高性能日曆控件,高仿魅族

Android原生的CalendarView根本沒法知足咱們平常開發的須要,在開發吾記APP的過程當中,我以爲須要來一款高性能且美觀簡潔的日曆控件,以爲魅族的日曆風格十分適合,因而打算擼一款。java

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

 

compile 'com.haibin:calendarview:1.0.2'

 

先上效果圖:github

 

動手以前咱們須要分析一下魅族是怎麼設計如此高性能的日曆的,咱們打開開發者選項中的顯示佈局邊界:算法

 

好吧,一開始我覺得日曆界面是ViewPager+RecyclerView的,可是這麼一看明顯就不是了,若是是RecyclerView,那麼咱們假設每月的卡片都有5*7=35個item,每一個item根佈局是RelativeLayout+3個TextView,咱們大概估算一下日曆初始化時要加載的控件:canvas

3個ViewPager的item * 35個RecyclerView的Item * 4(每一個item的控件數) + 8 (星期欄)= 420+ ide

個人天,這可不能這麼幹,明顯性能大打折扣,咱們再來看看月份控件:佈局

好吧,這裏看上去就是ViewPager+RecyclerView來作的,每一個RecyclerView的item都只是一個控件,裏面繪製了文本 ,這裏大概就分析清楚了。性能

咱們採起折中的方式,日曆界面和月份卡界面均採用ViewPager+RecyclerView的方式,不一樣的是全部的item咱們都採用自定義ViewCanvas繪製的方式來作,這樣性能雖然比不上魅族,但速度體驗基本差很少,下面先看日曆界面的item代碼:只須要繪製3個文本便可this

 

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

相關文章
相關標籤/搜索