前言:

这段时间好久没更新,最近对自定义View感兴趣,先是根据网上的一些例子仿做了一个仿微信通讯录的组合控件,就是那种带字母索引ListView,可惜不知什么原因,用android studio导出的arr包却老是报错,待我继续研究。

后来便搞了这个日历控件,一是对自定义的view感兴趣,二来后来的项目可能需要用到类似的控件,虽说网上也有类似的现成的控件下载,但是觉得自己动手又何尝不是一次联系的机会。

先上图

normal

日期单选

日期多选

由于不会上传视频,只能截图啦

自定义View的过程也不重复太多,毕竟网上这么多教程了

就总结一下流程吧

新建一个类继承于View或者View的子类

实现三个构造方法

可以在value目录下新建attrs.xml文件,再其中声明以及内部的子标签来定义xml属性。

实现onMeasure()方法以及最重要的onDraw()方法

因为是自定义的日历控件,所以我也重写了dispatchTouchEvent()方法来解决响应事件。

思路:

要绘制出一个月的日历,需要下列几个参数

这个月的天数

这个月第一天是星期几

今天是几号

手画的,见谅

贴上最重要的onDraw()方法

protected void onDraw(Canvas canvas) {

//绘制背景色

canvas.drawColor(mBackground);

//绘制左箭头

mNumPaint.setColor(clickLeft ? mNormalButtonColor1 : mClickButtonColor2);

mNumPaint.setStrokeWidth(6);

mNumPaint.setAntiAlias(true);

canvas.drawLine(mViewWidth / 8, mViewHeight / 16, mViewWidth * 3 / 16, mViewHeight / 32, mNumPaint);

canvas.drawLine(mViewWidth / 8, mViewHeight / 16, mViewWidth * 3 / 16, mViewHeight * 3 / 32, mNumPaint);

mNumPaint.reset();

//绘制右箭头

mNumPaint.setColor(clickRight ? mNormalButtonColor1 : mClickButtonColor2);

mNumPaint.setStrokeWidth(6);

mNumPaint.setAntiAlias(true);

canvas.drawLine(mViewWidth * 7 / 8, mViewHeight / 16,

mViewWidth * 13 / 16, mViewHeight / 32, mNumPaint);

canvas.drawLine(mViewWidth * 7 / 8, mViewHeight / 16,

mViewWidth * 13 / 16, mViewHeight * 3 / 32, mNumPaint);

mNumPaint.reset();

//绘制年,月份

mNumPaint.setTextSize(mViewHeight / 16);

mNumPaint.setColor(mNormalTextColor1);

mNumPaint.setAntiAlias(true);

String theYear = year + "";

String theMonth = month + "";

canvas.drawText(theYear, mViewWidth / 2 - getTextWidth(mNumPaint, theYear) / 2,

mViewHeight / 16, mNumPaint);

mNumPaint.setTextSize(mViewHeight / 18);

mNumPaint.setColor(mNormalTextColor2);

canvas.drawText(theMonth, mViewWidth / 2 - getTextWidth(mNumPaint, theMonth) / 2,

mViewHeight / 8, mNumPaint);

mNumPaint.reset();

//绘制日历

xInterval = mViewWidth / 7;

yInterval = mViewHeight / 8;

int day = 0;

float x;

float y;

int theday;

boolean isToday = false;

boolean isCheckDay = false;

float offset = 0;

radius = mViewWidth / 19;

for (int i = 0; i < weekName.length; i++) {

x = i * xInterval + mNormalTextSize / 2;

y = 1 * yInterval + yInterval / 2;

if (i == 0 || i == weekName.length - 1) {

drawNum(weekName[i], mNormalTextSize, mNormalTextColor2, x, y,

canvas, isToday, offset);

} else {

drawNum(weekName[i], mNormalTextSize, mNormalTextColor1, x, y,

canvas, isToday, offset);

}

}

mNumPaint.reset();

String str;

for (int i = 2; i < 8; i++) {

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

if (i == 2 && j == 0) {

j = weekOfFirstDay;

}

if (day > allDays.length - 1) {

theday = -1;

} else {

theday = allDays[day];

}

str = "" + theday;

if (theday == -1) {

str = "";

}

//单个数字的偏移量

if (theday < 10 && theday > 0) {

offset = mNormalTextSize / 4;

}

//计算数字的位置

y = i * yInterval + yInterval / 2;

x = j * xInterval + mNormalTextSize / 2 -

getTextWidth(mNumPaint, str) + offset;

//判断是否为今天

isToday = theday == today;

if (isToday) {

drawACircle(x, y, Color.argb(255, 254, 140, 26),

radius, canvas, offset);

}

//如果数字是checkDay

isCheckDay = theday == firstCheckDay;

if (isCheckDay) {

drawACircle(x, y, 0xffa0c8c8, radius, canvas, offset);

}

if (secondCheckDay != -2) {

if (theday > firstCheckDay && theday <= secondCheckDay) {

drawACircle(x, y, 0xffa0c8c8, radius, canvas, offset);

isCheckDay = true;

}

}

if (j == 0 || j == 6) {

drawNum(str, mNormalTextSize, mNormalTextColor2,

x, y, canvas, isToday || isCheckDay, offset);

} else {

drawNum(str, mNormalTextSize, mNormalTextColor1,

x, y, canvas, isToday || isCheckDay, offset);

}

offset = 0;

day++;

mNumPaint.reset();

}

}

}

再贴上dispatchTouchEvent()方法

public boolean dispatchTouchEvent(MotionEvent event) {

//获取事件的位置

float touchX = event.getX();

float touchY = event.getY();

if (!canClick) {

return true;

}

switch (event.getAction()) {

case MotionEvent.ACTION_DOWN:

if (touchY < 3 * mViewHeight / 32 && touchY > mViewHeight / 32) {

if (touchX < 3 * mViewWidth / 16 && touchX > mViewWidth / 8) {

clickLeft = true;

//左箭头事件

if (turnPageListener != null) {

turnPageListener.OnLeftDown(today, month, year);

}

}

if (touchX < 7 * mViewWidth / 8 && touchX > 13 * mViewWidth / 16) {

clickRight = true;

//右箭头事件

if (turnPageListener != null) {

turnPageListener.OnRightUp(today, month, year);

}

}

}

//以下是对日历的事件处理

int theX = (int) ((touchX + 0.1 * xInterval) / xInterval);//获取第几列

int theY = (int) ((touchY + 0.2 * yInterval) / yInterval);//获取第几行

if (theY < 2) {

theY = 2;

}

//得到是哪一天

int num = (theY - 2) * 7 + theX - weekOfFirstDay;

int day;

if (num < 0 || num > allDays.length - 1) {

num = -2;

day = 0;

} else {

day = allDays[num];

}

float x = theX * xInterval + mNormalTextSize / 2 -

mNumPaint.measureText("" + day);

float y = theY * yInterval + yInterval / 2;

//判断是否点击在每个数字为中心的圆内

boolean isclick = isClick(x, y, num, touchX, touchY);

//有三种状态 初始状态(00),第一次点击(10),第二次点击(11)

if (!firstClick) {

firstClick = true;

} else if (!secondClick) {

secondClick = true;

} else {

firstClick = false;

secondClick = false;

firstCheckDay = -2;

secondCheckDay = -2;

}

//处理点击在月份天数外所引起的数值问题

if (isclick && num != -2 && firstClick && !secondClick) {

firstCheckDay = allDays[num];

}

if (firstClick && firstCheckDay == -2) {

firstClick = false;

}

if (isclick && num != -2 && secondClick) {

if (allDays[num] < firstCheckDay) {

firstCheckDay = allDays[num];

secondClick = false;

} else {

secondCheckDay = allDays[num];

}

}

if (secondClick && secondCheckDay == -2) {

secondClick = false;

}

//

//调用接口

if (firstClick && !secondClick) {

if (chooseListener != null) {

chooseListener.onSingleChoose(firstCheckDay);

}

} else if (firstClick && secondClick) {

int numO = secondCheckDay - firstCheckDay + 1;

int[] days = new int[numO];

int tday = firstCheckDay;

for (int j = 0; j < numO; j++) {

days[j] = tday++;

}

if (chooseListener != null) {

chooseListener.onDoubleChoose(days);

}

}

break;

case MotionEvent.ACTION_UP:

//左箭头事件

if (clickLeft) {

if (turnPageListener != null) {

turnPageListener.OnLeftUp(today, month, year);

}

clickLeft = !clickLeft;

preMonth();

}

//右箭头事件

if (clickRight) {

if (turnPageListener != null) {

turnPageListener.OnRightUp(today, month, year);

}

clickRight = !clickRight;

nextMonth();

}

break;

}

invalidate();

return true;

}

待完善的地方

日期的多选还没做到跨月

没有做动画

发现在对日期的绘制位置的计算出了偏差,待下次更新!

自定义日历控android,android 一个简单的自定义日历控件,让你掌控时间相关推荐

  1. 使用掌控版制作一个简单的抢答器

    前几天老师布置的作业,要求我们做一个简单的抢答器,我用了掌控版,软件用的是mind+. 这个图形化编程软件简单易懂,容易操作. 这里用到三个掌控版,一个模拟主持人(也就是出题方),另外俩个模拟抢答端. ...

  2. android实现计算器功能吗,利用Android实现一个简单的计算器功能

    利用Android实现一个简单的计算器功能 发布时间:2020-11-20 16:25:01 来源:亿速云 阅读:90 作者:Leah 今天就跟大家聊聊有关利用Android实现一个简单的计算器功能, ...

  3. 初识Android 制作一个简单的记账本

    初识Android 制作一个简单的记账本 主要功能 实现一个记账本页面 可以添加数据并更新到页面中 主要步骤 运行截图 主页面 点击红色按钮弹出添加页面 完成后自动更新到目录下 主要功能 实现一个记账 ...

  4. Android 实现一个简单的文件下载工具

    下载应该是每个App都必须的一项功能,不采用第三方框架的话,就需要我们自己去实现下载工具了.如果我们自己实现可以怎么做呢? 首先如果服务器文件支持断点续传,则我们需要实现的主要功能点如下: 多线程.断 ...

  5. php如果实现日历的制作,教你如何制作一个简单的PHP日历

    PHP是一款功能强大的后端编程软件,我们在学习PHP的过程中要经过很多实际的战斗,现在让我们做一个日历来巩固PHP的基础,今天爱站技术频道小编为大家整理了教你如何制作一个简单的PHP日历,希望能帮到大 ...

  6. MQL5:写一个简单的自定义指标

    MQL5:写一个简单的自定义指标 在MT5平台中,交易者可根据自己的习惯.需要,去编写供自己参考的指标. 如何写一个简单的自定义的指标? 在这里将写一个 取当日最高价和最低价中值的折线 的指标 作为演 ...

  7. Android Compose——一个简单的Bilibili APP

    Bilibili移动端APP 简介 依赖 效果 登录 效果 WebView 自定义TobRow的Indicator大小 首页 推荐 LazyGridView使用Paging3 热门 排行榜 搜索 模糊 ...

  8. 用Android做一个简单的音乐播放器(上)

    这一学期要结束了,我用Android写了一个音乐播放器,算是给它画上个完美的句号,里面有登录,注册,这个APP还有很多功能没完善,有时间了在加一些料.效果图如下: 登录.注册直接用的是Android自 ...

  9. 一个简单的自定义多附件上传控件

    在网上提供的都是生成多个上传控件的代码,但大家可以看看21cn,163那里的多附件上传是不是比那些只是生成多个上传控件的界面美观很多呢??      现在就开始做一个类似21CN那样的上传的.先看看我 ...

  10. 基于android新闻阅读器,Readian News是一款基于Android和网络的新闻阅读器,可让您掌控一切...

    主流媒体有其用途,但这些新闻出版物在很大程度上忽略了许多主题和兴趣.想要跟上赛博朋克2077游戏,Mozilla Firefox的Proton设计更新或您最喜欢的足球运动员西蒙·恩格尔曼(Simon ...

最新文章

  1. redis 3.2.1 centos7
  2. Keras 实现 LSTM
  3. redis缓存穿透、缓存击穿、缓存雪崩概念及解决方案
  4. mysql乱码问题_mysql乱码问题
  5. ORA-02266错误处理
  6. SAP License:SAP凭证编号中跳号问题处理
  7. 从Java Web 基础看SSH架构
  8. 微信小程序系列(3)如何用微信小程序写一个论坛?贴心代码详解(一)发帖
  9. 【转】百度网盘高速下载-暴力油猴脚本
  10. (八)、MultipartFile
  11. python mro
  12. 互联网酒店预订系统的业务流程图-2
  13. 骑士cms最新全局SQL注入(官方奇葩修复案例)
  14. 【毕业设计_课程设计】基于机器视觉的智能快递分拣系统
  15. 计算机 无法进入睡眠模式,win10电脑无法进入睡眠模式怎么解决
  16. Auto.js Pro安卓免ROOT引流脚本开发系列教程27网易公开课(5)-UI界面构建
  17. 期刊影响因子的中外算法差别很大
  18. 如何使用WebService服务
  19. 再谈 共轭方向法/Conjugate Direction Method In Optimization
  20. PDFBox打印PDF A4格式文档和定制规格条码实例

热门文章

  1. 地上有一个m行和n列的方格。一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于k的格子。
  2. Kotlin 丢失了一些 JRE 类
  3. 0-1背包问题——回溯法求解
  4. 区块链中心化业务必须基于中心化平台吗?
  5. 具有分散效果的瀑布流
  6. Android 开发中eclipse 下 DDMS 视图中 sdcard 中文件导入的处理
  7. C语言内联函数的作用
  8. Python使用wxpython制作简单文本编辑器
  9. windows32位安装MongoDB
  10. c++中new和delete