【原创,转载请注明出处】

【使用效果】

未点击.jpg

点击.jpg

注释很详细,因此直接上代码:

CalendarScheduleView.java

/**

* 日历型排班表

* 注意:只能显示本月的日期

* 功能1:对两种日期进行圆圈背景显示:可排班日期-mSelectableDayColor、已预约日期-mSelectedDayColor

* 注意:这些日期必须在今日及今日之后,以及,“已预约日期应该包含于可预约日期等逻辑”应该由外部判断

* 功能2;对点击的日期进行回调,返回点击日期值给外部调用者,日期格式为 年-月-日 分别返回

*/

public class CalendarScheduleView extends View {

// 画笔

private Paint paint;

// 列数

private static final int NUMS_COLUMN = 7;

// 行数(星期一行加日期六行)

private static final int NUMS_ROW = 7;

// 周日到周六的颜色

private int mWeekColor = Color.parseColor("#8B8B8B");

// 本月日期的颜色

private int mMonthDateColor = Color.parseColor("#000000");

// 非本月日期的颜色

private int mOtherDateColor = Color.parseColor("#AEAEAE");

// 可选日期的背景颜色

private int mSelectableDayColor = Color.parseColor("#9EB7B5");

// 选中日期的背景颜色

private int mSelectedDayColor = Color.parseColor("#E40F57");

// 星期字体大小

private int mWeekSize = 18;

// 日期字体大小

private int mDateSize = 15;

// 可选、选择日期的圆圈半径

private float mCircleR;

// 当前年

private int mCurrentYear;

// 当前月

private int mCurrentMonth;

// 可选的日期(排班日期)

private List mSelectableDates = new ArrayList();

// 已选日期(已预约日期)

private List mSelectedDates = new ArrayList();

// 本月日期-在绘图时将数据储存在此,点击日历时做出判断

// 7行7列(第一行没有数据,为了计算位置方便,将星期那一行考虑进去)

private int[][] days = new int[NUMS_ROW][NUMS_COLUMN];

// 列宽

private int mColumnWidth;

// 行高

private int mRowHeight;

// DisplayMetrics对象

private DisplayMetrics displayMetrics;

// 点击事件接口

private OnDateClick onDateClick;

/**

* 构造函数

*

* @param context

* @param attrs

* @description 初始化

*/

public CalendarScheduleView(Context context, AttributeSet attrs) {

super(context, attrs);

// 获取DisplayMetrics实例

displayMetrics = getResources().getDisplayMetrics();

// 获取日历实例

Calendar calendar = Calendar.getInstance();

// new一个Paint实例(抗锯齿)

paint = new Paint(Paint.ANTI_ALIAS_FLAG);

// 获取当前年份

mCurrentYear = calendar.get(Calendar.YEAR);

// 获取当前月份

mCurrentMonth = calendar.get(Calendar.MONTH);

// 假数据,测试用,TODO:记得删除

mSelectableDates.add(7);

mSelectableDates.add(8);

mSelectedDates.add(8);

}

@Override

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

int widthSize = MeasureSpec.getSize(widthMeasureSpec);

int widthMode = MeasureSpec.getMode(widthMeasureSpec);

if (widthMode == MeasureSpec.AT_MOST) {

widthSize = displayMetrics.densityDpi * 100;

}

int heightSize = MeasureSpec.getSize(widthMeasureSpec);

int heightMode = MeasureSpec.getMode(widthMeasureSpec);

if (heightMode == MeasureSpec.AT_MOST) {

heightSize = displayMetrics.densityDpi * 120;

}

setMeasuredDimension(widthSize, heightSize);

}

@Override

protected void onDraw(Canvas canvas) {

// 计算每一列宽度

mColumnWidth = getWidth() / NUMS_COLUMN;

// 计算每一行高度

mRowHeight = getHeight() / NUMS_ROW;

// 绘制星期

drawDayOfWeekText(canvas);

// 绘制本月日期

drawDateText(canvas);

// 绘制前一个月的日期

drawDateTextOfLastMonth(canvas);

// 绘制下一个月的日期

drawDateTextOfNextMonth(canvas);

}

private int downX = 0, downY = 0, upX = 0, upY = 0;

@Override

public boolean onTouchEvent(MotionEvent event) {

switch (event.getAction()) {

// 若是按下,则获取坐标

case MotionEvent.ACTION_DOWN:

downX = (int) event.getX();

downY = (int) event.getY();

break;

// 若是点击后放开

case MotionEvent.ACTION_UP:

upX = (int) event.getX();

upY = (int) event.getY();

if(Math.abs(downX - upX) < 10 && Math.abs(downY - upY) < 10) {

// (点击事件)因为这里返回true,导致事件不会往上传,因此“手动”上传

performClick();

// 处理点击事件

handleClick((upX + downX) / 2, (upY + downY) / 2);

}

break;

default:

break;

}

// 返回true表示已经消费此事件,不上传了(这样才能监听所有动作,而不是只有ACTION_DOWN)

return true;

}

@Override

public boolean performClick() {

return super.performClick();

}

/**

* 处理点击事件

* @param x

* @param y

*/

private void handleClick(int x, int y) {

// 获取行

int row = y / mRowHeight;

// 获取列

int column = x / mColumnWidth;

// 获取点击的日期

int clickedDay = days[row][column];

// 将点击的日期传给接口

onDateClick.onClick(mCurrentYear, mCurrentMonth, clickedDay);

}

/**

* 绘制星期

*

* @param canvas

*/

private void drawDayOfWeekText(Canvas canvas) {

for (int dayOfWeek = 0; dayOfWeek < 7; dayOfWeek++) {

// 星期在第0行

int row = 0;

// 列数

int column = dayOfWeek;

// 填写星期

String day[] = {"日", "一", "二", "三", "四", "五", "六"};

// 设置字体大小

paint.setTextSize(mWeekSize * displayMetrics.scaledDensity);

// 设置画笔颜色

paint.setColor(mWeekColor);

// 左边坐标(居中显示)

int left = (int) (mColumnWidth * column + (mColumnWidth - paint.getTextSize()) / 2);

// 顶部坐标 (注意,竖直方向上是以baseline为基准写字的,因此要 - (paint.ascent() + paint.descent()) / 2)

int top = (int) (mRowHeight * row + mRowHeight / 2 - (paint.ascent() + paint.descent()) / 2);

// 绘制文字

canvas.drawText(day[dayOfWeek] + "", left, top, paint);

}

}

/**

* 绘制本月日期(若有背景,需要先绘制背景,否则会覆盖文字)

*

* @param canvas

*/

private void drawDateText(Canvas canvas) {

// 获取日历实例

Calendar calendar = Calendar.getInstance();

// 获取当前月份天数

int daysOfMonth = calendar.getActualMaximum(Calendar.DATE);

// 获取当月第一天是一周中的第几天(周日为第一天)

int firstDayOfWeekInMonth = TimeUtils.getFirstDayOfWeekInMonth(mCurrentYear, mCurrentMonth);

// 写入文字

for (int date = 1; date <= daysOfMonth; date++) {

// 当前日期所在行数(第一行为1:由于第一行显示星期,故日期行数加一)

int row = (date + firstDayOfWeekInMonth - 1 - 1) / 7 + 1;

// 当前日期所在列数(第一列为0)

int column = (date + firstDayOfWeekInMonth - 1 - 1) % 7;

if (firstDayOfWeekInMonth == 1) {

// 如果第一天是周日,那么,根据日历习惯,日期行数应该加一

row = row + 1;

}

// 储存日期信息

days[row][column] = date;

// 若是可选日期,绘制背景

for (Integer day : mSelectableDates) {

if (day.intValue() == date) {

drawSelectableBackground(canvas, row, column);

}

}

// 若是已选择日期,绘制背景

for (Integer day : mSelectedDates) {

if (day.intValue() == date) {

drawSelectedBackground(canvas, row, column);

}

}

// 设置字体大小

paint.setTextSize(mDateSize * displayMetrics.scaledDensity);

// 设置画笔颜色

paint.setColor(mMonthDateColor);

// 日期左边坐标(居中显示)

int left = (int) (mColumnWidth * column + (mColumnWidth - paint.measureText(date + "")) / 2);

// 日期顶部坐标 (注意,竖直方向上是以baseline为基准写字的,因此要 - (paint.ascent() + paint.descent()) / 2)

int top = (int) (mRowHeight * row + mRowHeight / 2 - (paint.ascent() + paint.descent()) / 2);

// 绘制文字

canvas.drawText(date + "", left, top, paint);

}

}

/**

* 绘制可选日期背景

*

* @param canvas

* @param row

* @param column

*/

private void drawSelectableBackground(Canvas canvas, int row, int column) {

// 画笔颜色

paint.setColor(mSelectableDayColor);

// 圆心位置

float cX = (float) (mColumnWidth * column + mColumnWidth / 2);

float cY = (float) (mRowHeight * row + mRowHeight / 2);

// 圆形半径

mCircleR = (float) (mColumnWidth / 2 * 0.8);

// 绘制圆形背景

canvas.drawCircle(cX, cY, mCircleR, paint);

}

/**

* 绘制已选日期背景

*

* @param canvas

* @param row

* @param column

*/

private void drawSelectedBackground(Canvas canvas, int row, int column) {

// 画笔颜色

paint.setColor(mSelectedDayColor);

// 圆心位置

float cX = (float) (mColumnWidth * column + mColumnWidth / 2);

float cY = (float) (mRowHeight * row + mRowHeight / 2);

// 圆形半径

mCircleR = (float) (mColumnWidth / 2 * 0.8);

// 绘制圆形背景

canvas.drawCircle(cX, cY, mCircleR, paint);

}

/**

* 绘制前一个月的日期

*

* @param canvas

*/

private void drawDateTextOfLastMonth(Canvas canvas) {

// 获取上一个月的最后一天是一周内的第几天

int lastDayOfWeek = TimeUtils.getLastDayOfWeekInLastMonth(mCurrentYear, mCurrentMonth);

// 获取上一个月的最后一天

int lastDay = TimeUtils.getLastDayOfLastMonth(mCurrentYear, mCurrentMonth);

// 写入文字

for (int column = lastDayOfWeek - 1, i = 0; column >= 0; column--, i++) {

// 设置字体大小

paint.setTextSize(mDateSize * displayMetrics.scaledDensity);

// 设置画笔颜色

paint.setColor(mOtherDateColor);

// 日期左边坐标

int left = (int) (mColumnWidth * column + (mColumnWidth - paint.measureText((lastDay - i) + "")) / 2);

// 日期顶部坐标 (所在行数为第二行)

int top = (int) (mRowHeight * 1 + mRowHeight / 2 - (paint.ascent() + paint.descent()) / 2);

// 绘制文字

canvas.drawText((lastDay - i) + "", left, top, paint);

}

}

/**

* 绘制下一个月的日期

*

* @param canvas

*/

private void drawDateTextOfNextMonth(Canvas canvas) {

// 获取下一个月的第一天是一周内的第几天

int firstDayOfWeekInNextMonth = TimeUtils.getFirstDayOfWeekInNextMonth(mCurrentYear, mCurrentMonth);

// 获取本月的第一天是一周内的第几天

int firstDayOfWeek = TimeUtils.getFirstDayOfWeekInMonth(mCurrentYear, mCurrentMonth);

// 下个月的第一天

int firstDay = 1;

// 所在行数

int row;

// 所在列数

int column = firstDayOfWeekInNextMonth - 1;

// 如果本月第一天是周五、周六或者周日,下个月从最后一行开始绘制(第6行),否则,从倒数第二行开始绘制(第5行)

if (firstDayOfWeek == 6 || firstDayOfWeek == 7 || firstDayOfWeek == 1) {

row = 6;

} else {

row = 5;

}

// 写入文字

for (; row <= 6; row++) {

for (; column <= 6; column++) {

// 设置字体大小

paint.setTextSize(mDateSize * displayMetrics.scaledDensity);

// 设置画笔颜色

paint.setColor(mOtherDateColor);

// 日期左边坐标

int left = (int) (mColumnWidth * column + (mColumnWidth - paint.measureText(firstDay + "")) / 2);

// 日期顶部坐标 (所在行数为第七行即最后一行)

int top = (int) (mRowHeight * row + mRowHeight / 2 - (paint.ascent() + paint.descent()) / 2);

// 绘制文字

canvas.drawText(firstDay + "", left, top, paint);

// 日期加一

firstDay += 1;

}

// 若还有一行要绘制,则column置为0

column = 0;

}

}

public int getmWeekColor() {

return mWeekColor;

}

public void setmWeekColor(int mWeekColor) {

this.mWeekColor = mWeekColor;

}

public int getmMonthDateColor() {

return mMonthDateColor;

}

public void setmMonthDateColor(int mMonthDateColor) {

this.mMonthDateColor = mMonthDateColor;

}

public int getmOtherDateColor() {

return mOtherDateColor;

}

public void setmOtherDateColor(int mOtherDateColor) {

this.mOtherDateColor = mOtherDateColor;

}

public int getmSelectableDayColor() {

return mSelectableDayColor;

}

public void setmSelectableDayColor(int mSelectableDayColor) {

this.mSelectableDayColor = mSelectableDayColor;

}

public int getmSelectedDayColor() {

return mSelectedDayColor;

}

public void setmSelectedDayColor(int mSelectedDayColor) {

this.mSelectedDayColor = mSelectedDayColor;

}

public int getmWeekSize() {

return mWeekSize;

}

public void setmWeekSize(int mWeekSize) {

this.mWeekSize = mWeekSize;

}

public int getmDateSize() {

return mDateSize;

}

public void setmDateSize(int mDateSize) {

this.mDateSize = mDateSize;

}

public float getmCircleR() {

return mCircleR;

}

public void setmCircleR(float mCircleR) {

this.mCircleR = mCircleR;

}

public List getmSelectableDates() {

return mSelectableDates;

}

public void setmSelectableDates(List mSelectableDates) {

this.mSelectableDates.clear();

this.mSelectableDates = mSelectableDates;

}

public List getmSelectedDates() {

return mSelectedDates;

}

public void setmSelectedDates(List mSelectedDates) {

this.mSelectedDates = mSelectedDates;

}

public void setOnDateClick(OnDateClick onDateClick) {

this.onDateClick = onDateClick;

}

}

点击事件接口OnDataClick.java:

public interface OnDateClick {

// 点击的日期为可选

void onClick(int year, int month, int data);

}

时间工具TimeUtils.java:

public class TimeUtils {

static Calendar calendar = Calendar.getInstance();

/**

* 获取指定年份、月份的第一天是一周中的第几天(周日为第一天)

* @param year 年

* @param month 月

* @return year年-month月的第一天是一周中的第几天(周日为第一天)

*/

public static int getFirstDayOfWeekInMonth(int year, int month) {

// 设置当前日期为:year年-month月-1号

calendar.set(year, month, 1);

return calendar.get(Calendar.DAY_OF_WEEK);

}

/**

* 获取指定年份、月份的上一月份的最后一天

* @param year 年

* @param month 月

* @return year年-month月的上一月的最后一天

*/

public static int getLastDayOfLastMonth(int year, int month) {

calendar.set(year, month, 1);

// 向前滚动一个月

calendar.add(Calendar.MONTH, -1);

// 返回最后一天

return calendar.getActualMaximum(Calendar.DATE);

}

/**

* 获取指定年份、月份的上个月的最后一天是一周中的第几天

*

* @param year

* @param month

* @return year年-month月的上一月的最后一天是一周中的第几天(周日为第一天)

*/

public static int getLastDayOfWeekInLastMonth(int year, int month) {

calendar.set(year, month, 1);

// 向前滚动一天,即到前一月最后一天

calendar.add(Calendar.DATE, -1);

// 返回最后一天是一周的第几天

return calendar.get(Calendar.DAY_OF_WEEK);

}

/**

* 获取指定年份、月份的下个月的第一天是一周中的第几天

*

* @param year

* @param month

* @return year年-month月的上一月的第一天是一周中的第几天(周日为第一天)

*/

public static int getFirstDayOfWeekInNextMonth(int year, int month) {

calendar.set(year, month, 1);

// 向后滚动一天,即到后一月

calendar.add(Calendar.MONTH, 1);

// 返回第一天是一周的第几天

return calendar.get(Calendar.DAY_OF_WEEK);

}

}

测试TestActivity.java

public class TestActivity extends AppCompatActivity {

private CalendarScheduleView mCsvThis;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_test);

// 控件初始化

initView();

}

private void initView() {

List list = new ArrayList();

list.add(8);

list.add(9);

mCsvThis = (CalendarScheduleView) findViewById(R.id.csv_doctor_schedule);

mCsvThis.setmSelectableDates(list);

mCsvThis.setOnDateClick(new OnDateClick() {

@Override

public void onClick(int year, int month, int data) {

Toast.makeText(TestActivity.this, year + "年" + month + "月" + data + "日", Toast.LENGTH_SHORT).show();

Log.e("TestActivity", "日历onClick点击事件可用");

}

});

mCsvThis.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

Log.e("TestActivity", "普通onClick点击事件可用");

}

});

}

}

主要使用方法:

①设置可选日期

setmSelectableDates(List mSelectableDates)

②设置已选日期

setmSelectedDates(List mSelectedDates)

③获取点击事件

view.setOnDateClick(new OnDateClick() {

@Override

public void onClick(int year, int month, int data) {

}

});

注意:控件本身不负责“当前日期”与“可选日期”、“已选日期”先后关系的判断,所以此部分逻辑交由外部使用者进行判断。

java如何实排班表时间算法_安卓自定义View-日历型排班表-CalendarScheduleView相关推荐

  1. mysql表连接算法_如何在MySQL中连接多个表

    我需要FULL OUTER JOIN多个表.我知道如何从 here开始加入两个表.但我有几个表,我不能将它们应用于它们.我怎样才能实现它? 我的SQL代码,如下: INSERT INTO table ...

  2. java如何实排班表时间算法_java如何实排班表时间算法

    java如何实排班表时间算法 [2021-02-09 22:33:43]  简介: php去除nbsp的方法:首先创建一个PHP代码示例文件:然后通过"preg_replace(" ...

  3. 日出时间 算法_如何便宜地建立日出闹钟

    日出时间 算法 Sunrise-simulating alarm clocks are a great way to wake yourself up in the mornings, but com ...

  4. ezdml 支付mysql 吗_EZDML数据库表结构设计器_设计sql、oracle、mysql数据库表结构 V2.39 免费版...

    很多程序员或者网站站长在设计网站数据库的时候都要进行表结构设计,如果您不想操作原始的数据库工具之想简单设计一下数据库表结构,那么你不妨试试这款EZDML数据库表结构设计器,可以快速设计sql.orac ...

  5. mysql多字段分库分表基因码_一文学会常用 MySQL 分库分表方案

    来源 | https://www.cnblogs.com/littlecharacter/p/9342129.html 一.数据库瓶颈 不管是IO瓶颈,还是CPU瓶颈,最终都会导致数据库的活跃连接数增 ...

  6. 安卓自定义view之——可滑动时间轴(时间刻度尺)

    如果需要解决在viewpager,horizontalscrollview中滑动事件的冲突,可注入viewpager,horizontalscrollview对象,在down move的时候调用req ...

  7. 达梦两个表模糊查询_一文彻底搞定哈希表

    哈希表是个啥? 小白: 庆哥,什么是哈希表?这个哈希好熟悉,记得好像有HashMap和HashTable之类的吧,这是一样的嘛? 庆哥: 这个哈希确实经常见,足以说明它是个使用非常频繁的玩意儿,而且像 ...

  8. java雪花纷飞_分析自定义view的实现过程-实现雪花飞舞效果(转载有改动)

    声明:本文源码出自实现雪花飞舞效果(有改动)主要通过这篇文来分析自定义view的实现过程. 没事时,比较喜欢上网看看一些新的东西,泡在网上的日子就是一个很不错的网站. 下面开始了,哈哈.^_^ 大家都 ...

  9. 学海无涯!算法题+JVM+自定义View,原理+实战+视频+源码

    阿里面试 自我介绍 首先要介绍自己的项目经验和个人的擅长点,因为面试官主要考察你的表达能力和语言精简能力. 简历的编写其实这里可能需要注意几点: 1.标题的直接按照姓名-几年工作经验-应聘岗位格式来填 ...

最新文章

  1. 欧拉函数求一个数倒数的循环节长度
  2. 一种嵌套滑动冲突的解决方案
  3. 1.9 Java 9增强的自动资源管理
  4. 用python简单处理图片(2):图像通道\几何变换\裁剪
  5. 前端学习(1797):前端调试之html/css介绍
  6. 石头剪刀布程序流程图_石头剪刀布!我要与电脑决战到天明!(14天)
  7. OpenCV 入门级一
  8. iOS6、7、8、9新特性汇总和适配说明
  9. java jdbc jar包_大数据从入门到深入:JavaEE 之 数据库技术 JDBC(1)
  10. 机器学习和深度学习资料汇总【01】
  11. GIS | 坐标系统与地图投影
  12. 基于OMAPL138的Linux字符驱动_GPIO驱动AD9833(二)之cdev与read、write
  13. Android通话录音
  14. Linux平台开源浏览器
  15. 微信公众平台消息接口开发 小黄鸡(小贱鸡)机器人 微信公众平台 公众号聊天机器人 ,消息,接口,小黄鸡,小贱鸡,机器人...
  16. PDO介绍[不包括具体使用方法]
  17. 笔记本外接显示器之后扩展屏分辨率问题
  18. Android解决Could not find manifest-merger.jar问题
  19. ARMv8 Cortex-a 编程向导手册学习_2.ARMv8-A 寄存器
  20. 树莓派系统+arduino单片机+双目摄像头 搭载小车

热门文章

  1. 读《枪炮,病菌和钢铁》
  2. 网站服务器备案有什么危害,域名备案对服务器有影响吗
  3. matlab仿真培训班,Matlab/Simulink进行微网系统仿真建模培训
  4. 人工智能帮助千万用户完成「隐形征信」计算 1
  5. 【日常记录】win10打印机打印不出来,打印队列里有文档无法删除
  6. 一文讲清:对象存储、文件存储、块存储。绝对好文
  7. android网络传输唤醒系统,Android手机唤醒群晖NAS系统
  8. turf:计算面积、折线长度和两点距离
  9. java jit技术_如何理解JIT编译技术?
  10. 将vue-admin-template组件默认英文语言改成中文