Android UI-自定义日历控件
2014年博客之星,投票地址点击打开链接
本篇博客笔者给大家分享一个日历控件,这里有个需求:要求显示当前月的日期,左右可以切换月份来查看日期。
我们想一想会如何去实现这样的一个控件,有开源的,但可能不太满足我们的特定的需求,这里笔者自定义了一个,读者可以根据自己的需求来修改代码。下面来说一下实现的思路:
首先我们要显示当前月份,自然我们要计算出当前的日期,并且把每一天对应到具体的星期,我们会有以下效果:
我们先想一下这样的效果用什么控件可以实现?很自然可以想到用网格视图GridView,但这里笔者使用的不是GridView, 因为使用GridView可能无法实现那个红色的圈圈,所以笔者决定自定义View,通过绘制来达到这样的效果。
这里我们定于一个日历卡,每一个月代表一个日历卡,我们通过计算每个月的日期,然后根据计算出来的位置绘制我们的数字。
我们知道,一个星期有七天,分别为星期日、星期一、星期二、星期三、星期四、星期五、星期六,这里有7列,一个月至少有28天,最多31天,所以至少应该有6行。组成6*7的方格图。
直接上代码:
- package com.xiaowu.calendar;
-
- import android.content.Context;
- import android.graphics.Canvas;
- import android.graphics.Color;
- import android.graphics.Paint;
- import android.util.AttributeSet;
- import android.view.MotionEvent;
- import android.view.View;
- import android.view.ViewConfiguration;
-
-
-
-
-
-
-
- public class CalendarCard extends View {
-
- 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 mViewHeight;
- private int mCellSpace;
- private Row rows[] = new Row[TOTAL_ROW];
- private static CustomDate mShowDate;
- private OnCellClickListener mCellClickListener;
- private int touchSlop;
- private boolean callBackCellSpace;
-
- private Cell mClickCell;
- private float mDownX;
- private float mDownY;
-
-
-
-
-
-
-
- public interface OnCellClickListener {
- void clickDate(CustomDate date);
-
- void changeDate(CustomDate date);
- }
-
- public CalendarCard(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- init(context);
- }
-
- public CalendarCard(Context context, AttributeSet attrs) {
- super(context, attrs);
- init(context);
- }
-
- public CalendarCard(Context context) {
- super(context);
- init(context);
- }
-
- public CalendarCard(Context context, OnCellClickListener listener) {
- super(context);
- this.mCellClickListener = listener;
- init(context);
- }
-
- 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() {
- mShowDate = new CustomDate();
- fillDate();
- }
-
- private void fillDate() {
- 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 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);
- }
-
-
- } 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);
- }
- }
- }
- mCellClickListener.changeDate(mShowDate);
- }
-
- @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);
- }
- }
- }
-
- @Override
- protected void onSizeChanged(int w, int h, int oldw, int oldh) {
- super.onSizeChanged(w, h, oldw, oldh);
- mViewWidth = w;
- mViewHeight = h;
- mCellSpace = Math.min(mViewHeight / TOTAL_ROW, mViewWidth / TOTAL_COL);
- if (!callBackCellSpace) {
- callBackCellSpace = true;
- }
- mTextPaint.setTextSize(mCellSpace / 3);
- }
-
- @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;
- default:
- 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);
-
- CustomDate date = rows[row].cells[col].date;
- date.week = col;
- mCellClickListener.clickDate(date);
-
-
- update();
- }
- }
-
-
-
-
-
-
-
- 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 TODAY:
- mTextPaint.setColor(Color.parseColor("#fffffe"));
- canvas.drawCircle((float) (mCellSpace * (i + 0.5)),
- (float) ((j + 0.5) * mCellSpace), mCellSpace / 3,
- mCirclePaint);
- break;
- case CURRENT_MONTH_DAY:
- mTextPaint.setColor(Color.BLACK);
- break;
- case PAST_MONTH_DAY:
- case NEXT_MONTH_DAY:
- mTextPaint.setColor(Color.parseColor("#fffffe"));
- break;
- case UNREACH_DAY:
- mTextPaint.setColor(Color.GRAY);
- break;
- default:
- 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);
- }
- }
-
-
-
-
-
- enum State {
- TODAY,CURRENT_MONTH_DAY, PAST_MONTH_DAY, NEXT_MONTH_DAY, UNREACH_DAY;
- }
-
-
- public void leftSlide() {
- if (mShowDate.month == 1) {
- mShowDate.month = 12;
- mShowDate.year -= 1;
- } else {
- mShowDate.month -= 1;
- }
- update();
- }
-
-
- public void rightSlide() {
- if (mShowDate.month == 12) {
- mShowDate.month = 1;
- mShowDate.year += 1;
- } else {
- mShowDate.month += 1;
- }
- update();
- }
-
- public void update() {
- fillDate();
- invalidate();
- }
-
- }
/CustomCalendarView/src/com/xiaowu/calendar/DateUtil.java
- package com.xiaowu.calendar;
-
- import android.annotation.SuppressLint;
- import android.util.Log;
-
- import java.text.ParseException;
- import java.text.SimpleDateFormat;
- import java.util.Calendar;
- import java.util.Date;
- import java.util.GregorianCalendar;
-
-
- public class DateUtil {
-
- public static String[] weekName = { "周日", "周一", "周二", "周三", "周四", "周五","周六" };
-
- public static int getMonthDays(int year, int month) {
- if (month > 12) {
- month = 1;
- year += 1;
- } else if (month < 1) {
- month = 12;
- year -= 1;
- }
- int[] arr = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
- int days = 0;
-
- if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) {
- arr[1] = 29;
- }
-
- try {
- days = arr[month - 1];
- } catch (Exception e) {
- e.getStackTrace();
- }
-
- return days;
- }
-
- public static int getYear() {
- return Calendar.getInstance().get(Calendar.YEAR);
- }
-
- public static int getMonth() {
- return Calendar.getInstance().get(Calendar.MONTH) + 1;
- }
-
- public static int getCurrentMonthDay() {
- return Calendar.getInstance().get(Calendar.DAY_OF_MONTH);
- }
-
- public static int getWeekDay() {
- return Calendar.getInstance().get(Calendar.DAY_OF_WEEK);
- }
-
- public static int getHour() {
- return Calendar.getInstance().get(Calendar.HOUR_OF_DAY);
- }
- public static int getMinute() {
- return Calendar.getInstance().get(Calendar.MINUTE);
- }
- public static CustomDate getNextSunday() {
-
- Calendar c = Calendar.getInstance();
- c.add(Calendar.DATE, 7 - getWeekDay()+1);
- CustomDate date = new CustomDate(c.get(Calendar.YEAR),
- c.get(Calendar.MONTH)+1, c.get(Calendar.DAY_OF_MONTH));
- return date;
- }
-
- public static int[] getWeekSunday(int year, int month, int day, int pervious) {
- int[] time = new int[3];
- Calendar c = Calendar.getInstance();
- c.set(Calendar.YEAR, year);
- c.set(Calendar.MONTH, month);
- c.set(Calendar.DAY_OF_MONTH, day);
- c.add(Calendar.DAY_OF_MONTH, pervious);
- time[0] = c.get(Calendar.YEAR);
- time[1] = c.get(Calendar.MONTH )+1;
- time[2] = c.get(Calendar.DAY_OF_MONTH);
- return time;
-
- }
-
- public static int getWeekDayFromDate(int year, int month) {
- Calendar cal = Calendar.getInstance();
- cal.setTime(getDateFromString(year, month));
- int week_index = cal.get(Calendar.DAY_OF_WEEK) - 1;
- if (week_index < 0) {
- week_index = 0;
- }
- return week_index;
- }
-
- @SuppressLint("SimpleDateFormat")
- public static Date getDateFromString(int year, int month) {
- String dateString = year + "-" + (month > 9 ? month : ("0" + month))
- + "-01";
- Date date = null;
- try {
- SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
- date = sdf.parse(dateString);
- } catch (ParseException e) {
- System.out.println(e.getMessage());
- }
- return date;
- }
- public static boolean isToday(CustomDate date){
- return(date.year == DateUtil.getYear() &&
- date.month == DateUtil.getMonth()
- && date.day == DateUtil.getCurrentMonthDay());
- }
-
- public static boolean isCurrentMonth(CustomDate date){
- return(date.year == DateUtil.getYear() &&
- date.month == DateUtil.getMonth());
- }
- }
/CustomCalendarView/src/com/xiaowu/calendar/CustomDate.java、
- package com.xiaowu.calendar;
-
- import java.io.Serializable;
- public class CustomDate implements Serializable{
-
-
- private static final long serialVersionUID = 1L;
- public int year;
- public int month;
- public int day;
- public int week;
-
- public CustomDate(int year,int month,int day){
- if(month > 12){
- month = 1;
- year++;
- }else if(month <1){
- month = 12;
- year--;
- }
- this.year = year;
- this.month = month;
- this.day = day;
- }
-
- public CustomDate(){
- this.year = DateUtil.getYear();
- this.month = DateUtil.getMonth();
- this.day = DateUtil.getCurrentMonthDay();
- }
-
- public static CustomDate modifiDayForObject(CustomDate date,int day){
- CustomDate modifiDate = new CustomDate(date.year,date.month,day);
- return modifiDate;
- }
- @Override
- public String toString() {
- return year+"-"+month+"-"+day;
- }
-
- public int getYear() {
- return year;
- }
-
- public void setYear(int year) {
- this.year = year;
- }
-
- public int getMonth() {
- return month;
- }
-
- public void setMonth(int month) {
- this.month = month;
- }
-
- public int getDay() {
- return day;
- }
-
- public void setDay(int day) {
- this.day = day;
- }
-
- public int getWeek() {
- return week;
- }
-
- public void setWeek(int week) {
- this.week = week;
- }
-
- }
所有绘制的操作在onDraw方面里实现,我这里定于了一个组对象Row、单元格元素Cell,通过Row[row].cell[col]来确定一个单元格,每次调用invalidate重绘视图。
接着,我们有一个需求需要左右切换,我们选用最熟悉的ViewPager,但这里有个问题,怎么实现无限循环呢,
这里我们传入一个日历卡数组,让ViewPager循环复用这几个日历卡,避免消耗内存。
/CustomCalendarView/src/com/xiaowu/calendar/CalendarViewAdapter.java
- package com.xiaowu.calendar;
-
- import android.support.v4.view.PagerAdapter;
- import android.support.v4.view.ViewPager;
- import android.view.View;
- import android.view.ViewGroup;
-
- public class CalendarViewAdapterextends View> extends PagerAdapter {
- public static final String TAG = "CalendarViewAdapter";
- private V[] views;
-
- public CalendarViewAdapter(V[] views) {
- super();
- this.views = views;
- }
-
-
- @Override
- public Object instantiateItem(ViewGroup container, int position) {
-
- if (((ViewPager) container).getChildCount() == views.length) {
- ((ViewPager) container).removeView(views[position % views.length]);
- }
-
- ((ViewPager) container).addView(views[position % views.length], 0);
- return views[position % views.length];
- }
-
- @Override
- public int getCount() {
- return Integer.MAX_VALUE;
- }
-
- @Override
- public boolean isViewFromObject(View view, Object object) {
- return view == ((View) object);
- }
-
- @Override
- public void destroyItem(ViewGroup container, int position, Object object) {
- ((ViewPager) container).removeView((View) container);
- }
-
- public V[] getAllItems() {
- return views;
- }
-
- }
布局文件:
/CustomCalendarView/src/com/xiaowu/calendar/MainActivity.java
用到的资源:
/CustomCalendarView/res/values/color.xml
- xml version="1.0" encoding="utf-8"?>
- <resources>
- <color name="white">#ffffffcolor>
- <color name="canlendar_text_color">#323232color>
- resources>
/CustomCalendarView/res/values/strings.xml
- xml version="1.0" encoding="utf-8"?>
- <resources>
-
- <string name="app_name">CustomCalendarViewstring>
- <string name="hello_world">Hello world!string>
- <string name="action_settings">Settingsstring>
-
-
- <string name="sunday">日string>
- <string name="monday">一string>
- <string name="thesday">二string>
- <string name="wednesday">三string>
- <string name="thursday">四string>
- <string name="friday">五string>
- <string name="saturday">六string>
-
- resources>
/CustomCalendarView/res/values/styles.xml
- <resources>
-
-
- <style name="AppTheme" parent="AppBaseTheme">
-
- style>
-
- <style name="dateStyle">
- <item name="android:layout_width">fill_parentitem>
- <item name="android:layout_height">fill_parentitem>
- <item name="android:layout_weight">1item>
- <item name="android:gravity">centeritem>
- <item name="android:textSize">16spitem>
- style>
-
- resources>
版权声明:本文为博主原创文章,未经博主允许不得转载。