本文主要是介绍android自定义日历并添加事件,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
前几天闲来无事,变想做一些小工具玩玩。花了一天多的时间,弄出一个简单日历的View。分为月份模式和星期模式。滚动查看,先上图看看:
上面的是显示的是月份的模式。下面是星期的模式:
CalendarView是一个自定义View,然后通过Viewpager的OnpageChangeListener进行刷新View的数据。Viewpager通过轮回使用View。我默认设置是5个。可以左右无限切换。后面因为我在ViewPager下面加了一个slidingDrawer。打开抽屉就可以直接切换成week的模式。为了使Adapter和OnPageChangeListener两个类可以和CalendarView减少耦合,我增加了一个CalendarViewBuilder类。
CalendarView:
- package com.example.calendar.widget;
- import com.example.caledar.util.DateUtil;
- import com.example.calendar.doim.CustomDate;
- import android.content.Context;
- import android.graphics.Canvas;
- import android.graphics.Color;
- import android.graphics.Paint;
- import android.util.AttributeSet;
- import android.util.Log;
- import android.view.MotionEvent;
- import android.view.View;
- import android.view.ViewConfiguration;
- public class CalendarView extends View {
- private static final String TAG = "CalendarView";
- /**
- * 两种模式 (月份和星期)
- */
- public static final int MONTH_STYLE = 0;
- public static final int WEEK_STYLE = 1;
- private static final int TOTAL_COL = 7;
- private static final int TOTAL_ROW = 6;
- private Paint mCirclePaint;
- private Paint mTextPaint;
- private int mViewWidth;
- private int mViewHight;
- private int mCellSpace;
- private Row rows[] = new Row[TOTAL_ROW];
- private static CustomDate mShowDate;//自定义的日期 包括year month day
- public static int style = MONTH_STYLE;
- private static final int WEEK = 7;
- private CallBack mCallBack;//回调
- private int touchSlop;
- private boolean callBackCellSpace;
- public interface CallBack {
- void clickDate(CustomDate date);//回调点击的日期
- void onMesureCellHeight(int cellSpace);//回调cell的高度确定slidingDrawer高度
- void changeDate(CustomDate date);//回调滑动viewPager改变的日期
- }
- public CalendarView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- init(context);
- }
- public CalendarView(Context context, AttributeSet attrs) {
- super(context, attrs);
- init(context);
- }
- public CalendarView(Context context) {
- super(context);
- init(context);
- }
- public CalendarView(Context context, int style, CallBack mCallBack) {
- super(context);
- CalendarView.style = style;
- this.mCallBack = mCallBack;
- init(context);
- }
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
- for (int i = 0; i < TOTAL_ROW; i++) {
- if (rows[i] != null)
- rows[i].drawCells(canvas);
- }
- }
- private void init(Context context) {
- mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- mCirclePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- mCirclePaint.setStyle(Paint.Style.FILL);
- mCirclePaint.setColor(Color.parseColor("#F24949"));
- touchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
- initDate();
- }
- private void initDate() {
- if (style == MONTH_STYLE) {
- mShowDate = new CustomDate();
- } else if(style == WEEK_STYLE ) {
- mShowDate = DateUtil.getNextSunday();
- }
- fillDate();
- }
- @Override
- protected void onSizeChanged(int w, int h, int oldw, int oldh) {
- super.onSizeChanged(w, h, oldw, oldh);
- mViewWidth = w;
- mViewHight = h;
- mCellSpace = Math.min(mViewHight / TOTAL_ROW, mViewWidth / TOTAL_COL);
- if (!callBackCellSpace) {
- mCallBack.onMesureCellHeight(mCellSpace);
- callBackCellSpace = true;
- }
- mTextPaint.setTextSize(mCellSpace / 3);
- }
- private Cell mClickCell;
- private float mDownX;
- private float mDownY;
- /*
- *
- * 触摸事件为了确定点击的位置日期
- */
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- switch (event.getAction()) {
- case MotionEvent.ACTION_DOWN:
- mDownX = event.getX();
- mDownY = event.getY();
- break;
- case MotionEvent.ACTION_UP:
- float disX = event.getX() - mDownX;
- float disY = event.getY() - mDownY;
- if (Math.abs(disX) < touchSlop && Math.abs(disY) < touchSlop) {
- int col = (int) (mDownX / mCellSpace);
- int row = (int) (mDownY / mCellSpace);
- measureClickCell(col, row);
- }
- break;
- }
- return true;
- }
- 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);
- rows[row].cells[col].state = State.CLICK_DAY;
- CustomDate date = rows[row].cells[col].date;
- date.week = col;
- mCallBack.clickDate(date);
- invalidate();
- }
- }
- // 组
- class Row {
- public int j;
- Row(int j) {
- this.j = j;
- }
- public Cell[] cells = new Cell[TOTAL_COL];
- public void drawCells(Canvas canvas) {
- for (int i = 0; i < cells.length; i++) {
- if (cells[i] != null)
- cells[i].drawSelf(canvas);
- }
- }
- }
- // 单元格
- class Cell {
- public CustomDate date;
- public State state;
- public int i;
- public int j;
- public Cell(CustomDate date, State state, int i, int j) {
- super();
- this.date = date;
- this.state = state;
- this.i = i;
- this.j = j;
- }
- // 绘制一个单元格 如果颜色需要自定义可以修改
- public void drawSelf(Canvas canvas) {
- switch (state) {
- case CURRENT_MONTH_DAY:
- mTextPaint.setColor(Color.parseColor("#80000000"));
- break;
- case NEXT_MONTH_DAY:
- case PAST_MONTH_DAY:
- mTextPaint.setColor(Color.parseColor("#40000000"));
- break;
- case TODAY:
- mTextPaint.setColor(Color.parseColor("#F24949"));
- break;
- case CLICK_DAY:
- mTextPaint.setColor(Color.parseColor("#fffffe"));
- canvas.drawCircle((float) (mCellSpace * (i + 0.5)),
- (float) ((j + 0.5) * mCellSpace), mCellSpace / 2,
- mCirclePaint);
- break;
- }
- // 绘制文字
- String content = date.day+"";
- canvas.drawText(content,
- (float) ((i+0.5) * mCellSpace - mTextPaint.measureText(content)/2),
- (float) ((j + 0.7) * mCellSpace - mTextPaint.measureText(
- content, 0, 1) / 2), mTextPaint);
- }
- }
- /**
- *
- * @author huang
- * cell的state
- *当前月日期,过去的月的日期,下个月的日期,今天,点击的日期
- *
- */
- enum State {
- CURRENT_MONTH_DAY, PAST_MONTH_DAY, NEXT_MONTH_DAY, TODAY, CLICK_DAY;
- }
- /**
- * 填充日期的数据
- */
- private void fillDate() {
- if (style == MONTH_STYLE) {
- fillMonthDate();
- } else if(style == WEEK_STYLE) {
- fillWeekDate();
- }
- mCallBack.changeDate(mShowDate);
- }
- /**
- * 填充星期模式下的数据 默认通过当前日期得到所在星期天的日期,然后依次填充日期
- */
- private void fillWeekDate() {
- int lastMonthDays = DateUtil.getMonthDays(mShowDate.year, mShowDate.month-1);
- rows[0] = new Row(0);
- int day = mShowDate.day;
- for (int i = TOTAL_COL -1; i >= 0 ; i--) {
- day -= 1;
- if (day < 1) {
- day = lastMonthDays;
- }
- CustomDate date = CustomDate.modifiDayForObject(mShowDate, day);
- if (DateUtil.isToday(date)) {
- mClickCell = new Cell(date, State.TODAY, i, 0);
- date.week = i;
- mCallBack.clickDate(date);
- rows[0].cells[i] = new Cell(date, State.CLICK_DAY, i, 0);
- continue;
- }
- rows[0].cells[i] = new Cell(date, State.CURRENT_MONTH_DAY,i, 0);
- }
- }
- /**
- * 填充月份模式下数据 通过getWeekDayFromDate得到一个月第一天是星期几就可以算出所有的日期的位置 然后依次填充
- * 这里最好重构一下
- */
- private void fillMonthDate() {
- int monthDay = DateUtil.getCurrentMonthDay();
- int lastMonthDays = DateUtil.getMonthDays(mShowDate.year, mShowDate.month - 1);
- int currentMonthDays = DateUtil.getMonthDays(mShowDate.year, mShowDate.month);
- int firstDayWeek = DateUtil.getWeekDayFromDate(mShowDate.year, mShowDate.month);
- boolean isCurrentMonth = false;
- if (DateUtil.isCurrentMonth(mShowDate)) {
- isCurrentMonth = true;
- }
- int day = 0;
- for (int j = 0; j < TOTAL_ROW; j++) {
- rows[j] = new Row(j);
- for (int i = 0; i < TOTAL_COL; i++) {
- int postion = i + j * TOTAL_COL;
- if (postion >= firstDayWeek
- && postion < firstDayWeek + currentMonthDays) {
- day++;
- if (isCurrentMonth && day == monthDay) {
- CustomDate date = CustomDate.modifiDayForObject(mShowDate, day);
- mClickCell = new Cell(date,State.TODAY, i,j);
- date.week = i;
- mCallBack.clickDate(date);
- rows[j].cells[i] = new Cell(date,State.CLICK_DAY, i,j);
- continue;
- }
- rows[j].cells[i] = new Cell(CustomDate.modifiDayForObject(mShowDate, day),
- State.CURRENT_MONTH_DAY, i, j);
- } else if (postion < firstDayWeek) {
- rows[j].cells[i] = new Cell(new CustomDate(mShowDate.year, mShowDate.month-1, lastMonthDays - (firstDayWeek- postion - 1)), State.PAST_MONTH_DAY, i, j);
- } else if (postion >= firstDayWeek + currentMonthDays) {
- rows[j].cells[i] = new Cell((new CustomDate(mShowDate.year, mShowDate.month+1, postion - firstDayWeek - currentMonthDays + 1)), State.NEXT_MONTH_DAY, i, j);
- }
- }
- }
- }
- public void update() {
- fillDate();
- invalidate();
- }
- public void backToday(){
- initDate();
- invalidate();
- }
- //切换style
- public void switchStyle(int style) {
- CalendarView.style = style;
- if (style == MONTH_STYLE) {
- update();
- } else if (style == WEEK_STYLE) {
- int firstDayWeek = DateUtil.getWeekDayFromDate(mShowDate.year,
- mShowDate.month);
- int day = 1 + WEEK - firstDayWeek;
- mShowDate.day = day;
- update();
- }
- }
- //向右滑动
- public void rightSilde() {
- if (style == MONTH_STYLE) {
- if (mShowDate.month == 12) {
- mShowDate.month = 1;
- mShowDate.year += 1;
- } else {
- mShowDate.month += 1;
- }
- } else if (style == WEEK_STYLE) {
- int currentMonthDays = DateUtil.getMonthDays(mShowDate.year, mShowDate.month);
- if (mShowDate.day + WEEK > currentMonthDays) {
- if (mShowDate.month == 12) {
- mShowDate.month = 1;
- mShowDate.year += 1;
- } else {
- mShowDate.month += 1;
- }
- mShowDate.day = WEEK - currentMonthDays + mShowDate.day;
- }else{
- mShowDate.day += WEEK;
- }
- }
- update();
- }
- //向左滑动
- public void leftSilde() {
- if (style == MONTH_STYLE) {
- if (mShowDate.month == 1) {
- mShowDate.month = 12;
- mShowDate.year -= 1;
- } else {
- mShowDate.month -= 1;
- }
- } else if (style == WEEK_STYLE) {
- int lastMonthDays = DateUtil.getMonthDays(mShowDate.year, mShowDate.month);
- if (mShowDate.day - WEEK < 1) {
- if (mShowDate.month == 1) {
- mShowDate.month = 12;
- mShowDate.year -= 1;
- } else {
- mShowDate.month -= 1;
- }
- mShowDate.day = lastMonthDays - WEEK + mShowDate.day;
- }else{
- mShowDate.day -= WEEK;
- }
- Log.i(TAG, "leftSilde"+mShowDate.toString());
- }
- update();
- }
- }
CalendarViewBuilder:
- package com.example.calendar.doim;
- import android.content.Context;
- import com.example.calendar.widget.CalendarView;
- import com.example.calendar.widget.CalendarView.CallBack;
- /**
- * CalendarView的辅助类
- * @author huang
- *
- */
- public class CalendarViewBuilder {
- private CalendarView[] calendarViews;
- /**
- * 生产多个CalendarView
- * @param context
- * @param count
- * @param style
- * @param callBack
- * @return
- */
- public CalendarView[] createMassCalendarViews(Context context,int count,int style,CallBack callBack){
- calendarViews = new CalendarView[count];
- for(int i = 0; i < count;i++){
- calendarViews[i] = new CalendarView(context, style,callBack);
- }
- return calendarViews;
- }
- public CalendarView[] createMassCalendarViews(Context context,int count,CallBack callBack){
- return createMassCalendarViews(context, count, CalendarView.MONTH_STYLE,callBack);
- }
- /**
- * 切换CandlendarView的样式
- * @param style
- */
- public void swtichCalendarViewsStyle(int style){
- if(calendarViews != null)
- for(int i = 0 ;i < calendarViews.length;i++){
- calendarViews[i].switchStyle(style);
- }
- }
- /**
- * CandlendarView回到当前日期
- */
- public void backTodayCalendarViews(){
- if(calendarViews != null)
- for(int i = 0 ;i < calendarViews.length;i++){
- calendarViews[i].backToday();
- }
- }
- }
为了Viewpager可以双向无限滑动,我重写了ViewPagerAdapter。
CustomViewPagerAdapter:
- package com.example.calendar.widget;
- import android.os.Parcelable;
- import android.support.v4.view.PagerAdapter;
- import android.support.v4.view.ViewPager;
- import android.view.View;
- public class CustomViewPagerAdapter<V extends View> extends PagerAdapter {
- private V[] views;
- public CustomViewPagerAdapter(V[] views) {
- super();
- this.views = views;
- }
- @Override
- public void finishUpdate(View arg0) {
- }
- @Override
- public void notifyDataSetChanged() {
- super.notifyDataSetChanged();
- }
- @Override
- public int getCount() {
- return Integer.MAX_VALUE;
- }
- @Override
- public Object instantiateItem(View arg0, int arg1) {
- if (((ViewPager) arg0).getChildCount() == views.length) {
- ((ViewPager) arg0).removeView(views[arg1 % views.length]);
- }
- ((ViewPager) arg0).addView(views[arg1 % views.length], 0);
- return views[arg1 % views.length];
- }
- @Override
- public boolean isViewFromObject(View arg0, Object arg1) {
- return arg0 == (arg1);
- }
- @Override
- public Parcelable saveState() {
- return null;
- }
- @Override
- public void destroyItem(View arg0, int arg1, Object arg2) {
- // TODO Auto-generated method stub
- }
- @Override
- public void startUpdate(View arg0) {
- }
- public V[] getAllItems() {
- return views;
- }
- }
然后为了实现对CalendarView的滑动时的数据更新,我重写了OnPageChangeListener的方法。这个类还是有一定的耦合,但是如果是项目中大量使用ViewPager。可以增加泛型进行复用。
CalendarViewPagerLisenter:
- package com.example.calendar.widget;
- import android.support.v4.view.ViewPager.OnPageChangeListener;
- public class CalendarViewPagerLisenter implements OnPageChangeListener {
- private SildeDirection mDirection = SildeDirection.NO_SILDE;
- int mCurrIndex = 498;
- private CalendarView[] mShowViews;
- public CalendarViewPagerLisenter(CustomViewPagerAdapter<CalendarView> viewAdapter) {
- super();
- this.mShowViews = viewAdapter.getAllItems();
- }
- @Override
- public void onPageSelected(int arg0) {
- measureDirection(arg0);
- updateCalendarView(arg0);
- }
- private void updateCalendarView(int arg0) {
- if(mDirection == SildeDirection.RIGHT){
- mShowViews[arg0 % mShowViews.length].rightSilde();
- }else if(mDirection == SildeDirection.LEFT){
- mShowViews[arg0 % mShowViews.length].leftSilde();
- }
- mDirection = SildeDirection.NO_SILDE;
- }
- /**
- * 判断滑动方向
- * @param arg0
- */
- private void measureDirection(int arg0) {
- if (arg0 > mCurrIndex) {
- mDirection = SildeDirection.RIGHT;
- } else if (arg0 < mCurrIndex) {
- mDirection = SildeDirection.LEFT;
- }
- mCurrIndex = arg0;
- }
- @Override
- public void onPageScrolled(int arg0, float arg1, int arg2) {
- }
- @Override
- public void onPageScrollStateChanged(int arg0) {
- }
- enum SildeDirection {
- RIGHT, LEFT, NO_SILDE;
- }
- }
这是个简单的demo,但是我把他封装起来,直接调用就可以了。如果有需要的同学,可以直接下载就可以了。已经修改版:支持日期点击事件。最后这个页面功能没有实现。
在这个小demo中我遇到一些问题和思考:
1.如何尽量的减少类之间耦合和增加类内聚。
2.如何使代码易读,好多方法extract Method出来比较烦,涉及参数太多。
3.检查错误。因为少了一个break的原因,我debug了半个小时。(一开始就debug到了,不相信咋会调用这个方法)。
4.如何平衡为了减少new对象使用一些int,还是为了以后增加功能增加一些对象的产生。
5.感觉这只是个demo的命名就感觉很随意,没有约束。
转载网址:http://blog.csdn.net/huangyanbin123/article/details/38350213
这篇关于android自定义日历并添加事件的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!