Android原生的CalendarView根本無法滿足我們日常開發的需要,在開發吾記APP的過程中,我覺得需要來一款高性能且美觀簡潔的日曆控制項,覺得魅族的日曆風格十分適合,於是打算擼一款。 github地址:https://github.com/huanghaibin-dev/CalendarVi ...
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