背景
Android开发,经常会使用到日历开发,实现方式有自定义或者第三方框架基础上修改。这里推荐使用第三方框架基础上修改。com.haibin.calendarview.CalendarView,为此本demo亦是在此框架基础上自定义样式,实现理想的日历。实现的功能有:
1、自定义星期标题
2、标记对应日程提醒,如图日期提醒标记---铃铛
3、节假日与节气文字自定义
4、自定义日历两边文字显色,例如星期日与星期六那两列的公历日期文字为淡红色
5、自定义日历网格线
效果如下图
一、日历框架属性基本介绍
1.依赖
//日历 android 引入
implementation 'com.haibin:calendarview:3.6.8'
//日历 androidx 引入
implementation 'com.haibin:calendarview:3.7.1'
2.属性介绍
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="CalendarView">
<attr name="calendar_padding" format="dimension" /><!--日历内部左右padding-->
<attr name="calendar_padding_left" format="dimension" /><!--日历内部左padding-->
<attr name="calendar_padding_right" format="dimension" /><!--日历内部右padding-->
<attr name="week_background" format="color" /><!--周背景-->
<attr name="week_line_background" format="color" /><!--线条颜色-->
<attr name="week_text_color" format="color" /><!--周栏字体颜色-->
<attr name="week_text_size" format="dimension" /><!--周栏字体大小-->
<attr name="week_line_margin" format="dimension" /><!--线条margin-->
<attr name="month_view" format="string" /><!--完全自定义月视图-->
<attr name="week_view" format="string" /> <!--完全自定义周视图-->
<attr name="year_view" format="string" /> <!--完全自定义年视图-->
<attr name="week_bar_height" format="dimension" /> <!--星期栏的高度-->
<attr name="week_bar_view" format="string" /> <!--如果需要的话使用自定义星期栏-->
<attr name="scheme_text" format="string" />
<attr name="day_text_size" format="dimension" />
<attr name="lunar_text_size" format="dimension" />
<attr name="calendar_height" format="dimension" />
<attr name="calendar_match_parent" format="boolean" /> <!-- 全屏日历 -->
<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_day_text_color" format="color" />
<attr name="current_day_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="year_view_month_text_size" format="dimension" />
<attr name="year_view_day_text_size" format="dimension" />
<attr name="year_view_month_text_color" format="color" />
<attr name="year_view_current_day_text_color" format="color" />
<attr name="year_view_day_text_color" format="color" />
<attr name="year_view_select_text_color" format="color" />
<attr name="year_view_scheme_color" format="color" />
<attr name="year_view_background" format="color" />
<attr name="year_view_month_height" format="dimension" />
<attr name="year_view_week_height" format="dimension" />
<attr name="year_view_week_text_size" format="dimension" />
<attr name="year_view_week_text_color" format="color" />
<attr name="year_view_padding" format="dimension" />
<attr name="year_view_padding_left" format="dimension" />
<attr name="year_view_padding_right" format="dimension" />
<attr name="year_view_month_padding_top" format="dimension" />
<attr name="year_view_month_padding_left" format="dimension" />
<attr name="year_view_month_padding_right" format="dimension" />
<attr name="year_view_month_padding_bottom" format="dimension" />
<!--日期范围-->
<attr name="min_year" format="integer" />
<attr name="max_year" format="integer" />
<attr name="min_year_month" format="integer" />
<attr name="max_year_month" format="integer" />
<attr name="min_year_day" format="integer" />
<attr name="max_year_day" format="integer" />
<!--月视图是否可滚动-->
<attr name="month_view_scrollable" format="boolean" />
<!--周视图是否可滚动-->
<attr name="week_view_scrollable" format="boolean" />
<!--年视图是否可滚动-->
<attr name="year_view_scrollable" format="boolean" />
<!-- 月份显示模式 -->
<attr name="month_view_show_mode">
<enum name="mode_all" value="0" />
<enum name="mode_only_current" value="1" />
<enum name="mode_fix" value="2" />
</attr>
<!-- 自定义周起始 -->
<attr name="week_start_with">
<enum name="sun" value="1" />
<enum name="mon" value="2" />
<enum name="sat" value="7" />
</attr>
<!-- 自定义选择模式 -->
<attr name="select_mode">
<enum name="default_mode" value="0" />
<enum name="single_mode" value="1" />
<enum name="range_mode" value="2" />
<enum name="multi_mode" value="3" />
</attr>
<!-- when select_mode = multi_mode -->
<attr name="max_multi_select_size" format="integer" />
<!-- when select_mode = range_mode -->
<attr name="min_select_range" format="integer" />
<attr name="max_select_range" format="integer" />
<!-- auto select day -->
<attr name="month_view_auto_select_day">
<enum name="first_day_of_month" value="0" />
<enum name="last_select_day" value="1" />
<enum name="last_select_day_ignore_current" value="2" />
</attr>
</declare-styleable>
<declare-styleable name="CalendarLayout">
<attr name="default_status">
<enum name="expand" value="0" />
<enum name="shrink" value="1" />
</attr>
<!-- 手势模式 -->
<attr name="gesture_mode">
<enum name="default_mode" value="0" />
<!--<enum name="only_calendar" value="1" />-->
<enum name="disabled" value="2" />
</attr>
<attr name="calendar_show_mode">
<enum name="both_month_week_view" value="0" />
<enum name="only_week_view" value="1" />
<enum name="only_month_view" value="2" />
</attr>
<attr name="calendar_content_view_id" format="integer" />
</declare-styleable>
</resources>
二、本demo实现步骤
1.依赖
//日历 android 引入
implementation 'com.haibin:calendarview:3.6.8'
//日历 androidx 引入
implementation 'com.haibin:calendarview:3.7.1'
2.xml布局
<com.haibin.calendarview.CalendarView
android:id="@+id/mCalendarView"
android:layout_width="416dp"
android:layout_height="@dimen/dimen_322"
android:layout_marginLeft="@dimen/dp_2"
android:layout_marginRight="@dimen/dp_2"
android:layout_marginBottom="@dimen/dp_2"
android:background="@drawable/data_cl_bg"
app:calendar_height="@dimen/dp_50"
app:current_day_lunar_text_color="#2856AC"
app:current_day_text_color="#2856AC"
app:min_year="2023"
app:month_view="csu.xiaoya.robotApp.ui.activity.homepage.calendar.CustomMonthView"
app:other_month_lunar_text_color="@color/white"
app:other_month_text_color="@color/white"
app:scheme_lunar_text_color="@color/color_grey_9"
app:scheme_text_color="@color/color333"
app:select_mode="default_mode"
app:selected_lunar_text_color="@color/white"
app:selected_text_color="@color/white"
app:selected_theme_color="#4868C3"
app:week_background="#172B7C"
app:week_bar_height="@dimen/dp_24"
app:week_bar_view="csu.xiaoya.robotApp.ui.activity.homepage.calendar.CustomWeekBar"
app:week_text_color="@color/white" />
由于自定义view跟日历界面在同一目录下
故引用如下:
app:month_view="csu.xiaoya.robotApp.ui.activity.homepage.calendar.CustomMonthView"
app:week_bar_view="csu.xiaoya.robotApp.ui.activity.homepage.calendar.CustomWeekBar"
3.自定义month_view ,类名CustomMonthView
/**
* 标记
* 节假日与节气
*/
public class CustomMonthView extends MonthView {
private int mRadius;
/**
* 自定义魅族标记的文本画笔
*/
private Paint mTextPaint = new Paint();
/**
* 24节气与
* 节假日画笔
*/
private Paint mSolarTermTextPaint = new Paint();
/**
* 网格画笔
*/
private Paint mRectPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
/**
* 背景圆点
*/
private Paint mPointPaint = new Paint();
/**
* 今天的背景色
*/
private Paint mCurrentDayPaint = new Paint();
/**
* 圆点半径
*/
private float mPointRadius;
private int mPadding;
private float mCircleRadius;
/**
* 自定义背景
*/
private Paint mSchemeBasicPaint = new Paint();
private float mSchemeBaseLine;
private int currentMonth = 0;//当前月份
public CustomMonthView(Context context) {
super(context);
mTextPaint.setTextSize(dipToPx(context, 8));
mTextPaint.setColor(0xffffffff);
mTextPaint.setAntiAlias(true);
mTextPaint.setFakeBoldText(true);
//* Typeface.BOLD //粗体
//* Typeface.BOLD_ITALIC //粗斜体
//* Typeface.ITALIC //斜体
//* Typeface.NORMAL //常规
mSolarTermTextPaint.setColor(0xffE92F3A);
Typeface font = Typeface.create(Typeface.SANS_SERIF, Typeface.BOLD);
mSolarTermTextPaint.setTypeface(font);
mSolarTermTextPaint.setAntiAlias(true);
mSolarTermTextPaint.setTextAlign(Paint.Align.CENTER);
mSchemeBasicPaint.setAntiAlias(true);
mSchemeBasicPaint.setStyle(Paint.Style.FILL);
mSchemeBasicPaint.setTextAlign(Paint.Align.CENTER);
mSchemeBasicPaint.setFakeBoldText(true);
mSchemeBasicPaint.setColor(Color.parseColor("#00ffffff"));
mCurrentDayPaint.setAntiAlias(true);
mCurrentDayPaint.setStyle(Paint.Style.FILL);
mCurrentDayPaint.setColor(0xFFffffff);
mPointPaint.setAntiAlias(true);
mPointPaint.setStyle(Paint.Style.FILL);
mPointPaint.setTextAlign(Paint.Align.CENTER);
mPointPaint.setColor(Color.RED);
mRectPaint.setStyle(Paint.Style.STROKE);
mRectPaint.setStrokeWidth(dipToPx(context, 0.5f));
mRectPaint.setColor(0x88DBDDE4);
mCircleRadius = dipToPx(getContext(), 7);
mPadding = dipToPx(getContext(), 8);
mPointRadius = dipToPx(context, 2);
Paint.FontMetrics metrics = mSchemeBasicPaint.getFontMetrics();
mSchemeBaseLine = mCircleRadius - metrics.descent + (metrics.bottom - metrics.top) / 2 + dipToPx(getContext(), 1);
}
@Override
protected void onPreviewHook() {
mSolarTermTextPaint.setTextSize(mCurMonthLunarTextPaint.getTextSize());
mRadius = Math.min(mItemWidth, mItemHeight) / 11 * 5;
}
@Override
protected boolean onDrawSelected(Canvas canvas, Calendar calendar, int x, int y, boolean hasScheme) {
int cx = x + mItemWidth * 1;
int cy = y + mItemHeight * 1;
canvas.drawRect(x, y, cx, cy, mSelectedPaint);
// canvas.drawCircle(cx, cy, mRadius, mSelectedPaint);
return true;
}
@Override
protected void onDrawScheme(Canvas canvas, Calendar calendar, int x, int y) {
// boolean isSelected = isSelected(calendar);
// if (isSelected) {
// mPointPaint.setColor(Color.WHITE);
// } else {
// mPointPaint.setColor(Color.BLUE);
// }
// canvas.drawCircle(x + mItemWidth / 2, y + mItemHeight - 3 * mPadding, mPointRadius, mPointPaint);
}
@SuppressWarnings("IntegerDivisionInFloatingPointContext")
@Override
protected void onDrawText(Canvas canvas, Calendar calendar, int x, int y, boolean hasScheme, boolean isSelected) {
int cx = x + mItemWidth / 2;
int cy = y + mItemHeight / 2;
int top = y - mItemHeight / 6;
canvas.drawRect(x, y, x + mItemWidth, y + mItemHeight, mRectPaint);
if (hasScheme) {
canvas.drawCircle(x + mItemWidth - mPadding - mCircleRadius / 2, y + mPadding + mCircleRadius, mCircleRadius, mSchemeBasicPaint);
mTextPaint.setColor(calendar.getSchemeColor());
//可通过修改坐标值调整tag位置
canvas.drawText(calendar.getScheme(), x + mItemWidth - mPadding - mCircleRadius, y + mPadding + mSchemeBaseLine-16, mTextPaint);
}
//当然可以换成其它对应的画笔就不麻烦,
if (calendar.isWeekend() && calendar.isCurrentMonth()) {
mCurMonthTextPaint.setColor(0xFFE30003);
mCurMonthLunarTextPaint.setColor(0xFF999999);
mSchemeTextPaint.setColor(0xFFE30003);
mSchemeLunarTextPaint.setColor(0xFF999999);
// mOtherMonthLunarTextPaint.setColor(0xFF489dff);
// mOtherMonthTextPaint.setColor(0xFF489dff);
} else {
mCurMonthTextPaint.setColor(0xff333333);
mCurMonthLunarTextPaint.setColor(0xFF999999);
mSchemeTextPaint.setColor(0xff333333);
mSchemeLunarTextPaint.setColor(0xFF999999);
// mOtherMonthTextPaint.setColor(0xFFe1e1e1);
// mOtherMonthLunarTextPaint.setColor(0xFFe1e1e1);
}
// Log.d("节假日", "======onDrawText: =======" + calendar.getTraditionFestival());
if (isSelected) {
canvas.drawText(String.valueOf(calendar.getDay()), cx, mTextBaseLine + top, mSelectTextPaint);
canvas.drawText(calendar.getLunar(), cx, mTextBaseLine + y + mItemHeight / 10, mSelectedLunarTextPaint);
} else if (hasScheme) {
canvas.drawText(String.valueOf(calendar.getDay()), cx, mTextBaseLine + top, calendar.isCurrentMonth() ? mSchemeTextPaint : mOtherMonthTextPaint);
canvas.drawText(calendar.getLunar(), cx, mTextBaseLine + y + mItemHeight / 10, !TextUtils.isEmpty(calendar.getSolarTerm() + calendar.getTraditionFestival() + calendar.getGregorianFestival()) ? mSolarTermTextPaint : mSchemeLunarTextPaint);
} 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, calendar.isCurrentDay() ? mCurDayLunarTextPaint : calendar.isCurrentMonth() ? !TextUtils.isEmpty(calendar.getSolarTerm() + calendar.getTraditionFestival() + calendar.getGregorianFestival()) ? mSolarTermTextPaint : mCurMonthLunarTextPaint : mOtherMonthLunarTextPaint);
}
//当日
if (calendar.isCurrentDay() && !calendar.isCurrentMonth()) {
canvas.drawCircle(cx, cy, mRadius, mCurrentDayPaint);
canvas.drawText(String.valueOf(calendar.getDay()), cx, cy, mCurrentDayPaint);
}
}
/**
* 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);
}
}
4.自定义周标题week_bar_view,类名CustomWeekBar
/**
* 自定义日历
* 周布局
*/
public class CustomWeekBar extends WeekBar {
private int mPreSelectedIndex;
public CustomWeekBar(Context context) {
super(context);
LayoutInflater.from(context).inflate(R.layout.custom_week_bar, this, true);
setBackgroundColor(Color.WHITE);
}
@Override
protected void onDateSelected(Calendar calendar, int weekStart, boolean isClick) {
getChildAt(mPreSelectedIndex).setSelected(false);
int viewIndex = getViewIndexByCalendar(calendar, weekStart);
getChildAt(viewIndex).setSelected(true);
mPreSelectedIndex = viewIndex;
}
/**
* 当周起始发生变化,使用自定义布局需要重写这个方法,避免出问题
*
* @param weekStart 周起始
*/
@Override
protected void onWeekStartChange(int weekStart) {
for (int i = 0; i < getChildCount(); i++) {
((TextView) getChildAt(i)).setText(getWeekString(i, weekStart));
}
}
/**
* 或者周文本,这个方法仅供父类使用
*
* @param index index
* @param weekStart weekStart
* @return 或者周文本
*/
private String getWeekString(int index, int weekStart) {
String[] weeks = getContext().getResources().getStringArray(R.array.chinese_week_string_array);
if (weekStart == 1) {
return weeks[index];
}
if (weekStart == 2) {
return weeks[index == 6 ? 0 : index + 1];
}
return weeks[index == 0 ? 6 : index - 1];
}
}
5. 设置日程消息提醒标记
// 设置日程消息提醒标记
Map<String, Calendar> map = new HashMap<>();
map.put(getSchemeCalendar(2024, 2, 23, 0x00ffffff, "🔔 ").toString(),
getSchemeCalendar(2024, 2, 23, 0xffffffff, "🔔 "));
map.put(getSchemeCalendar(2024, 2, 24, 0x00ffffff, "🔔 ").toString(),
getSchemeCalendar(2024, 2, 24, 0xffffffff, "🔔 "));
map.put(getSchemeCalendar(2024, 2, 20, 0x00ffffff, "🔔 ").toString(),
getSchemeCalendar(2024, 2, 20, 0xffffffff, "🔔 "));
map.put(getSchemeCalendar(2024, 3, 20, 0x00ffffff, "🔔 ").toString(),
getSchemeCalendar(2024, 3, 20, 0xffffffff, "🔔 "));
mDataBinding.mCalendarView.setSchemeDate(map);
/**
* 消息
* 提醒标记
*/
private Calendar getSchemeCalendar(int year, int month, int day, int color, String text) {
Calendar calendar = new Calendar();
calendar.setYear(year);
calendar.setMonth(month);
calendar.setDay(day);
calendar.setSchemeColor(color);//如果单独标记颜色、则会使用这个颜色
calendar.setScheme(text);
return calendar;
}
6.色值代码
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="purple_200">#FFBB86FC</color>
<color name="purple_500">#FF6200EE</color>
<color name="purple_700">#FF3700B3</color>
<color name="teal_200">#FF03DAC5</color>
<color name="teal_700">#FF018786</color>
<color name="black">#FF000000</color>
<color name="white">#FFFFFFFF</color>
<color name="slect">#2e2f43</color>
<color name="add_healthy_moni">#35456B</color>
<color name="manage_healthy_data">#443D4C</color>
<color name="colorPrimary">#455A64</color>
<color name="colorPrimaryDark">#4F636C</color>
<color name="colorAccent">#D81B60</color>
<color name="color_blue_7">#ff007AFF</color>
<!--MD 字体颜色-->
<color name="text_black_87">#D4000000</color>
<color name="text_black_54">#8A000000</color>
<color name="text_black_26">#42000000</color>
<color name="text_black_12">#1E000000</color>
<!-- 标准颜色-->
<color name="color_white">#FFFFFF</color>
<color name="color_black">#000000</color>
<color name="color_red">#F44336</color>
<color name="color_green">#4CAF50</color>
<color name="color_blue">#2196f3</color>
<color name="color_grey">#9E9E9E</color>
<color name="color_grey_dark">#616161</color>
<color name="color_grey_light">#F5F5F5</color>
<color name="color_transparent">#00000000</color>
<color name="color_background">#515151</color>
<color name="color_transparent_bg">#80FFFFFF</color>
<color name="background">#f0eff4</color>
<!--LOGO颜色-->
<color name="color_4a5a64">#4A5A64</color>
<color name="color_4f636c">#4F636C</color>
<color name="color_929ea4">#929EA4</color>
<color name="color_1967d2">#1967D2</color>
<color name="color_1452a8">#1452A8</color>
<color name="color_fbbc04">#FBBC04</color>
<color name="color_e37400">#E37400</color>
<color name="color_6cae4e">#6CAE4E</color>
<color name="color_78c257">#78C257</color>
<color name="color_757575">#757575</color>
<color name="color_c6c6c6">#C6C6C6</color>
<color name="color_9e9e9e">#9E9E9E</color>
<color name="color_5e5e5e">#5E5E5E</color>
<!--QMUI主题色-->
<color name="app_color_theme_1">#EF5362</color> <!-- Grapefruit -->
<color name="app_color_theme_2">#FE6D4B</color> <!-- Bittersweet -->
<color name="app_color_theme_3">#FFCF47</color> <!-- Sunflower -->
<color name="app_color_theme_4">#9FD661</color> <!-- Grass -->
<color name="app_color_theme_5">#3FD0AD</color> <!-- Mint -->
<color name="app_color_theme_6">#2BBDF3</color> <!-- Aqua -->
<color name="app_color_theme_7">#5A9AEF</color> <!-- Blue Jeans -->
<color name="app_color_theme_8">#AC8FEF</color> <!-- Lavender -->
<color name="app_color_theme_9">#EE85C1</color> <!-- Pink Rose -->
<color name="back_gray_light">#0018457c</color>
<!-- <color name="back_gray_light">#8018457c</color>-->
<color name="gray">#008c8c8c</color>
<!-- <color name="gray">#808c8c8c</color>-->
<color name="blue_selected">#039bf6</color>
<color name="green">#00ff00</color>
<color name="color_grey_9">#ff999999</color>
<color name="color_home_list_line">#ffC6C6C6</color>
<color name="rect_color">#cc000000</color>
<!--透明灰-->
<color name="transparent_gray">#C90A0A0A</color>
<color name="color_grey_3">#ff333333</color>
<color name="color_grey_2">#ff222222</color>
<color name="addhealtmonit">#DAD8DB</color>
<!--线条颜色-->
<color name="chart_green">#3BED65</color>
<color name="chart_yellow">#FFCD00</color>
<color name="chart_blue">#007AFF</color>
<color name="chart_red">#F44336</color>
<color name="chart_violet">#E93AFF</color>
<color name="bg_grey">#f2f2f2</color>
<!-- Appy 颜色 #070c22-->
<color name="app_bg_color">#06103A</color>
<!-- 页面背景色 #07103B-->
<color name="pageBgColor">#06103A</color>
<color name="dwupBgColor">#ff2e2f43</color>
<!--底部-->
<color name="botombar_bg_color">#2a2f43</color>
<!-- Ai 医生渐变背景 -->
<color name="ai_bg_color_start">#15DEEA</color>
<color name="ai_bg_color_end">#5787FB</color>
<color name="medication_rmd">#FFF1E7</color>
<color name="msg_rmd">#BAC0F8</color>
<color name="times_color">#3B8AD3</color>
<color name="times_clock_color">#E7F2FF</color>
<color name="monitor_healthy_color">#5F6DF0</color>
<color name="family_edc_color">#E69F60</color>
<color name="family_doctor_color">#1BA9E4</color>
<color name="conntact_family_color">#44CB8D</color>
<color name="calender_divi_color">#9BBAF4</color>
<color name="calender_month_day_color">#2749C5</color>
<color name="calender_cn_month_day_color">#28619A</color>
<color name="calender_suitable_color">#36945B</color>
<color name="in_calender_suitable_color">#D03F3F</color>
<color name="tab_selector_color">#5276B6</color>
<color name="tab_unselector_color">#3460AA</color>
<color name="wealther_recy_color">#FF356CA4</color>
<color name="day_hour_divi_color">#59C4FF</color>
<color name="manage_info_cl">#9E9E9E</color>
<!-- -->
<color name="color_grey_33">#333333</color>
<!-- 字体颜色 -->
<color name="color_txt_adress">#E3803E</color>
<color name="color_canler_bg">#172B7C</color>
</resources>
三、完工
3Q阅读