修改一份自定义日历代码

写在前面

不好意思,我这篇博客写的时间有点长了,那个时候刚实习,没有保存源码的意识,只是把改后的代码放上来,所以我这里没有源码可以发给你们,抱歉了。

---------------------------------------------------------------------

原文:


最近在做一个功能,UI给出来的效果图里面是一个日历,刚一到手我就有点慌了。一个是我没有接触过Android 的日历,一个是那种效果我不知道我能不能实现。一开始我找到了Android里有一个系统控件Calendar,但当引用的时候,发现简直就是不能用。所以只能上网自己去找了,Google就是强大,第一个结果就跟我要的效果比较像,作者应该也是个大神,提供了Demo源码,我就下载后开始尝试了。Android UI-自定义日历控件  毕竟是第一次尝试这种东西,所以想把这个过程记录下来。


这是Demo里的:


这是UI:



首先要修改几个点:

1.头部的时间应该要加上年份

2.今天应该是显示文字“今天”,而不是日期

3.选择某一天的时候画出黄色的圆形

4.因为Demo里其实是前后都有把上个月和下个月给画出来,只是用了白色隐藏,所以我要让点击的时候,让已选日期不显示内容。


看代码:

1.这里作者通过 观察者模式 写了个监听方法,用于设置头部的显示,所以只要在这里加上date.year的显示即可

@Override
	public void changeDate(CustomDate date) {
		monthText.setText(date.year + "年" + date.month + "月");
	}


2.这里我学到了一个东西 enum,就是在状态的选择这方面,作者是这样用的:

/**
	 * 
	 * @author wuwenjie 单元格的状态 当前月日期,过去的月的日期,下个月的日期
	 */
	enum State {
		TODAY, CURRENT_MONTH_DAY, PAST_MONTH_DAY, NEXT_MONTH_DAY, UNREACH_DAY, REACHED_DAY, SELECT_DAY;
	}

当时我在想那他是怎么设置值的,然后就看到了这个,他在这里为状态设值

for (int j = 0; j < TOTAL_ROW; j++) {
			rows[j] = new Row(j);
			for (int i = 0; i < TOTAL_COL; i++) {
				position = i + j * TOTAL_COL; // 单元格位置
				// 这个月的
				if (position >= firstDayWeek && position < firstDayWeek + currentMonthDays) {
					day++;
					rows[j].cells[i] = new Cell(CustomDate.modifiDayForObject(mShowDate, day), State.CURRENT_MONTH_DAY,
							i, j);
					// 今天
					if (isCurrentMonth && day == monthDay) {
						CustomDate date = CustomDate.modifiDayForObject(mShowDate, day);
						rows[j].cells[i] = new Cell(date, State.TODAY, i, j);
					}

					if (isCurrentMonth && day > monthDay) { // 如果比这个月的今天要大,表示还没到
						rows[j].cells[i] = new Cell(CustomDate.modifiDayForObject(mShowDate, day), State.UNREACH_DAY, i,
								j);
					}
					if (isCurrentMonth && day < monthDay) {
						rows[j].cells[i] = new Cell(CustomDate.modifiDayForObject(mShowDate, day), State.REACHED_DAY, i,
								j);
					}

				}
				// 过去一个月
				else if (position < firstDayWeek) {
					rows[j].cells[i] = new Cell(new CustomDate(mShowDate.year, mShowDate.month - 1,
							lastMonthDays - (firstDayWeek - position - 1)), State.PAST_MONTH_DAY, i, j);

				}
				// 下个月
				else if (position >= firstDayWeek + currentMonthDays) {
					rows[j].cells[i] = new Cell((new CustomDate(mShowDate.year, mShowDate.month + 1,
							position - firstDayWeek - currentMonthDays + 1)), State.NEXT_MONTH_DAY, i, j);

				}

			}

		}

在这里对状态进行处理,字体颜色,画出来等。所以我在这里将“今天”进行替换,然后调整一下高度,这样它画出来的时候能跟原来的数字的高度差不多。

3.这里Demo里没有设置被选中的天的状态,所以我就往里添加了个状态,并对其进行设置

public void drawSelf(Canvas canvas) {

			// 绘制文字
			String contentDate = date.day + "";
			String contentToday = "今天";
			switch (state) {
			case TODAY: // 今天
				mTextPaint.setColor(Color.BLACK);
				drawContentToday(contentToday, canvas);
				break;
			case CURRENT_MONTH_DAY: // 当前月日期
				mTextPaint.setColor(Color.BLACK);
				drawContentDate(contentDate, canvas);
				break;
			case PAST_MONTH_DAY: // 过去一个月
				mTextPaint.setColor(Color.parseColor("#fffffe"));
				drawContentDate(contentDate, canvas);

				break;
			case NEXT_MONTH_DAY: // 下一个月
				mTextPaint.setColor(Color.parseColor("#fffffe"));
				drawContentDate(contentDate, canvas);

				break;
			case UNREACH_DAY: // 还未到的天
				// mTextPaint.setColor(Color.GRAY);
				mTextPaint.setColor(Color.BLACK);
				drawContentDate(contentDate, canvas);
				break;
			case REACHED_DAY: // 已过的天
				mTextPaint.setColor(Color.GRAY);
				drawContentDate(contentDate, canvas);

				break;
			case SELECT_DAY:// 选中的天数
				mTextPaint.setColor(Color.WHITE);
				drawCircle(canvas);
				drawContentDate(contentDate, canvas);
			default:
				break;
			}

		}

		/**
		 * 画出日期
		 * 
		 * @param content
		 * @param canvas
		 */
		public void drawContentDate(String content, Canvas canvas) {
			canvas.drawText(content, (float) ((i + 0.5) * mCellSpace - mTextPaint.measureText(content) / 2),
					(float) ((j + 0.7) * mCellSpace - mTextPaint.measureText(content, 0, 1) / 2), mTextPaint);
		}

		/**
		 * 画出今日
		 * 
		 * @param content
		 * @param canvas
		 */
		public void drawContentToday(String content, Canvas canvas) {
			canvas.drawText(content, (float) ((i + 0.5) * mCellSpace - mTextPaint.measureText(content) / 2),
					(float) ((j + 0.77) * mCellSpace - mTextPaint.measureText(content, 0, 1) / 2), mTextPaint);
		}

		public void drawCircle(Canvas canvas) {
			canvas.drawCircle((float) (mCellSpace * (i + 0.5)), (float) ((j + 0.5) * mCellSpace), mCellSpace / 3,
					mCirclePaint);
		}
	}

在点击里面添加我们要的东西

/**
	 * 计算点击的单元格
	 * 
	 * @param col
	 * @param row
	 */
	private void measureClickCell(int col, int row) {
		if (col >= TOTAL_COL || row >= TOTAL_ROW)
			return;
		if (mClickCell != null) {
			rows[mClickCell.j].cells[mClickCell.i] = mClickCell;
		}
		if (rows[row] != null) {
			mClickCell = new Cell(rows[row].cells[col].date, rows[row].cells[col].state, rows[row].cells[col].i,
					rows[row].cells[col].j);

			CustomDate date = rows[row].cells[col].date;
			date.week = col;

			getClickDate(date);
			// drawClick(mClickCell, mCanvas);
			mCellClickListener.clickDate(date);
                        //新增回调方法,用于获取选中的日期和当前展示的时间
			mCellClickListener.clickDateAndShowDate(date,mShowDate);
			// 设置选中日期
			selectDate = date;
			isSelect = true;
			selectRow = row;
			selectCol = col;
			Log.d("selectWhere", "the real select is row = " + row + "col = " + col);

			// 刷新界面
			update();
		}
	}

将在上方所获得的信息传入到这里,设置该状态为 被选中的天,然后在drawSelf()方法里(上面)添加就行了

// 是否选中
		if (isSelect) {
			//如果选择的时间的月份等于展示的月份,则设为 被选中的天
			if ( selectDate.month == mShowDate.month) {
				rows[selectRow].cells[selectCol] = new Cell(CustomDate.modifiDayForObject(selectDate, selectDate.day),
						State.SELECT_DAY, selectCol, selectRow);
				Log.d("selectWhere", "selectRow is " + selectRow + " selectCol is " + selectCol);
				// Log.d("selectWhere", "i is " + i + " j is" + j);
				isSelect = false;
			}

		

4.在是否选中里我加了个判断,就是必须是选中的月份是当前展示的月份,才能算选中,否则的话你会发现那些用白色隐藏的日期也可以被选择到,所以Demo里其实就像魔术一样,不是它不见了,而是它还在,只是你看不到,所以要做些处理。同样,因为我在日历下方做了个显示选中的日期,所以我也需要处理。前面我自己增加了一个监听方法,当用户点击日期的时候,返回选中的日期和当前的显示时间,所以我要在Activity里做些处理。

@Override
	public void clickDateAndShowDate(CustomDate date, CustomDate showDate) {
		// TODO Auto-generated method stub
		if (date.month == showDate.month) {
			tv_chooseDate.setText(date.year + "/" + date.month + "/" + date.day);
		} else {
			tv_chooseDate.setText("");
		}

	}

基本就是这样,改的东西其实不多,只是在对这份代码的理解和逻辑的处理上我大概是用了两个工作日的时间,这里面还是收获颇丰,毕竟也是第一次尝试这种东西,而且对于自定的View我还是会感到有些胆怯。不过在处理这方面的时候也去看了《Android群英传》,大概有了个底,所以耳濡目染的就对整体有所了解了。尤其是在设置选中的天这一块,从什么都没显示,到一点击的时候,整个日历都变黄,到后来代码位置的调整,一步步地过程,直到后来能显示正常。



有机会自己要尝试写个日历。(附:这里的布局如果嵌套在ViewPager里会出现不显示的情况,我给ViewPager设置了个固定的高度650px,这样才能算显示完整。不过有查据说是可以重写onMeasure(),这个还没尝试)


评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值