1000字范文,内容丰富有趣,学习的好帮手!
1000字范文 > Android自定义组件之日历控件-精美日历实现(内容 样式可扩展)

Android自定义组件之日历控件-精美日历实现(内容 样式可扩展)

时间:2024-03-02 07:52:50

相关推荐

Android自定义组件之日历控件-精美日历实现(内容 样式可扩展)

效果

实现的效果如下

附上源码地址:/detail/daijin888888/9020535

(直接导入Eclipse ADT即可,出现乱码请调整项目编码,笔者的是UTF-8编码)

实现方式

首先说明涉及的主要知识点:

- GridView+Adapter

- 日历算法

项目结构图如下:

com.widget.mycalendar 包下是主要的实现部分:

CalendarGridView.Java: 自定义日历GridView,此处可扩展样式,可实现日历中每个网格(对应某一天)的长按效果。CalendarGridViewAdapter.java: 日历适配器,对具体的网格赋值和选择性控制样式(实现多样化)。CalendarTool.java: 获取日历数据工具类,主要工具类,实现日历算法,包括闰年判断。DateEntity.java: 日历实体类,添加的参数在获取日历实体集合的时候设置,可以控制日期属性(比如某一天对应的年、月、日、星期、是否为当前日期、是否为本月日期)

首先看自定义日历GridView,

CalendarGridView.java:自定义日历GridView,此处可扩展样式,可实现日历中每个网格(对应某一天)的长按效果。

packagecom.widget.mycalendar;importandroid.content.Context;importandroid.util.AttributeSet;importandroid.view.View;importandroid.widget.AdapterView;importandroid.widget.GridView;/** *@authordaij *@version1.0 自定义日历GridView */publicclassCalendarGridViewextendsGridViewimplementsandroid.widget.AdapterView.OnItemLongClickListener{privateContext mContext;publicCalendarGridView(Context context) {super(context);this.mContext = context; initGridView(); }publicCalendarGridView(Context context, AttributeSet attrs) {super(context, attrs);this.mContext = context; initGridView(); }publicCalendarGridView(Context context, AttributeSet attrs,intdefStyle) {super(context, attrs, defStyle);this.mContext = context; initGridView(); }/** * 初始化GirdView * *@param<参数名称> * <参数类型> <参数说明> *@return<返回值类型> *@throws<异常> */publicvoidinitGridView() { }@OverridepublicbooleanonItemLongClick(AdapterView<?> parent, View view,intposition,longid) {returnfalse; } }

CalendarGridViewAdapter.java:日历适配器,这里提供的是模拟数据,根据不同日期的属性控制不同显示,对具体的网格赋值和选择性控制样式(实现多样化)。

packagecom.widget.mycalendar;importjava.util.List;importandroid.content.Context;importandroid.content.res.Resources;importandroid.text.TextUtils;importandroid.view.LayoutInflater;importandroid.view.View;importandroid.view.ViewGroup;importandroid.widget.BaseAdapter;importandroid.widget.LinearLayout;importandroid.widget.TextView;importcom.example.mycalendar.R;/** *@authordaij *@version1.0 日历适配器 */publicclassCalendarGridViewAdapterextendsBaseAdapter{privateResources mRes;/** 上下文 */privateContext mContext;/** 日期实体集合 */privateList<DateEntity> mDataList;/** 因为position是从0开始的,所以用当做一个中间者,用来加1.以达到判断除数时,为哪个星期 */privateinttemp;publicCalendarGridViewAdapter(Context context, Resources res) {this.mContext = context;this.mRes = res; }/** 设置日期数据 */publicvoidsetDateList(List<DateEntity> dataList) {this.mDataList = dataList; }@OverridepublicintgetCount() {if(mDataList ==null) {return0; }returnmDataList.size(); }@OverridepublicObjectgetItem(intposition) {returnnull; }@OverridepubliclonggetItemId(intposition) {return0; }@OverridepublicViewgetView(intposition, View convertView, ViewGroup parent) {// 通过传递过来的MenuItem值给每一个item设置数据LinearLayout layout = (LinearLayout) LayoutInflater.from(mContext) .inflate(R.layout.calendar_item_layout,null); TextView textView = (TextView) layout .findViewById(R.id.calendar_item_tv_day); TextView tv_tip = (TextView) layout.findViewById(R.id.calendar_tip);if(mDataList !=null) { textView.setText(mDataList.get(position).day +"");if((TextUtils.equals(CalendarTool.SATURDAY, mDataList.get(position).weekDay)) || TextUtils.equals(CalendarTool.SUNDAY, mDataList.get(position).weekDay)) {// 周末背景为白,字体为灰色textView.setBackgroundColor(mRes.getColor(R.color.white)); textView.setTextColor(mRes.getColor(R.color.weekend_day_txt)); }// TODO 在非周末时候设置颜色else{if(((mDataList.get(position).year ==&& mDataList .get(position).month <=8) || (mDataList.get(position).year <&& mDataList .get(position).month <=12)) && mDataList.get(position).isSelfMonthDate) {// .8以前,且日期在当月if(mDataList.get(position).day >0&& mDataList.get(position).day <=20|| mDataList.get(position).day ==25) { tv_tip.setBackgroundColor(mRes .getColor(R.color.tip_normal)); }elseif(mDataList.get(position).day ==21|| mDataList.get(position).day ==22) { tv_tip.setBackgroundColor(mRes .getColor(R.color.tip_leave)); }elseif(mDataList.get(position).day ==23|| mDataList.get(position).day ==24) { tv_tip.setBackgroundColor(mRes .getColor(R.color.tip_late)); }else{ tv_tip.setBackgroundColor(mRes .getColor(R.color.tip_normal));if(mDataList.get(position).month ==8&& mDataList.get(position).day >=25) { tv_tip.setBackgroundColor(mRes .getColor(R.color.white)); } } } }if(mDataList.get(position).isNowDate && mDataList.get(position).isSelfMonthDate) {// 如果为当前号数,则设置为白色背景并,字体为蓝色textView.setBackgroundColor(mRes.getColor(R.color.white)); textView.setTextColor(mRes.getColor(R.color.current_day_txt)); }if(!mDataList.get(position).isSelfMonthDate) {// 是否为本月的号数,不是本月号数显示白色,及不显示textView.setTextColor(mRes.getColor(R.color.white)); } layout.setTag(mDataList.get(position));// 把当前日历实体放入GridView 的Item中}returnlayout; } }

CalendarTool.java: 获取日历数据工具类,实现日历算法,包括闰年判断,配有详细注释可参考

packagecom.widget.mycalendar;importjava.text.SimpleDateFormat;importjava.util.ArrayList;importjava.util.Date;importjava.util.List;importandroid.content.Context;importandroid.graphics.Point;importandroid.util.Log;/** *@authordaij *@version1.0 获取日历数据工具类 */publicclassCalendarTool{publicstaticfinalString MONDAY ="周一";publicstaticfinalString TUESDAY ="周二";publicstaticfinalString WEDNESDAY ="周三";publicstaticfinalString THURSDAY ="周四";publicstaticfinalString FRIDAY ="周五";publicstaticfinalString SATURDAY ="周六";publicstaticfinalString SUNDAY ="周日";publicstaticfinalString[] weekDayRow = { SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY };privateList<DateEntity> mDataList =newArrayList<DateEntity>();privateDateEntity mDateEntity;privateintmYear;privateintmMonth;privateintmDay;privateintmDays;/** 系统当前年月日 */privateintmCurrenYear;privateintmCurrenMonth;privateintmCurrenDay;privateContext mContext;/** 用于算法的变量 *//** 已过去的年份总天数 */intmGoneYearDays =0;/** 本年包括今天的过去天数 */intthisYearDays =0;/** 是否为闰年 */booleanisLeapYear =false;/** 平年月天数数组 */intcommonYearMonthDay[] = {31,28,31,30,31,30,31,31,30,31,30,31};/** 闰年月天数数组 */intleapYearMonthDay[] = {31,29,31,30,31,30,31,31,30,31,30,31};publicCalendarTool(Context context) {this.mContext = context; initNowDate(); }/** 获取当前日历的年月 x为年,y为月 */publicPointgetNowCalendar() { Point p =newPoint(mYear, mMonth);returnp; }/** 通过年月获取日期实体集合 集合大小为7*6=42,星期为7,6行以显示足够数量日期 */publicList<DateEntity>getDateEntityList(intyear,intmonth) { mDataList.clear();intdayOfNowCalendar =42;// 当前日历板的日期总数 7*6个数intdayOfWeek =0;// 得到当前年月的每一天为星期几intselfDaysEndWeek =0;// 本月的最后一天是星期几intstartDate =0;// 当前月的上一个月在本日历的开始日期intdays =0;// 得到本月的总共天数/** 修改部分 */intendDate =0;// 得到上一个月的天数,作为上一个月在本日历的结束日期if((year -1) ==this.mYear || month ==1) {// 说明向前翻了一年,那么上个月的天数就应该是上一年的12月的天数,或者到翻到一月份的时候,那么上一个月的天数也是上一年的12月份的天数endDate =this.getDays(year -1,12); }else{// 得到上一个月的天数,作为上一个月在本日历的结束日期endDate =this.getDays(year, month -1); }/** 修改部分结束 */this.mYear = year;// 当前日历上显示的年this.mMonth = month;// 当前日历上显示的月intprevioursMonthDays =0;// 上一个月在本月显示的天数intnextMonthDays =0;// 下一个月在本月显示的天数days =this.getDays(year, month); dayOfWeek =this.getWeekDay(year, month);if(dayOfWeek ==0) { startDate = endDate -7+1; }else{ startDate = endDate - dayOfWeek +1; } previoursMonthDays = endDate - startDate +1; nextMonthDays = dayOfNowCalendar - days - previoursMonthDays;/** 先添加前面不属于本月的 */for(inti = startDate, j =0; i <= endDate; i++, j++) { mDateEntity =newDateEntity(); mDateEntity.day = i; mDateEntity.isSelfMonthDate =false; mDateEntity.year = year; mDateEntity.month = month -1; mDateEntity.weekDay = weekDayRow[j]; mDataList.add(mDateEntity); }/** 添加本月的 */for(inti =1, j = dayOfWeek; i <= days; i++, j++) { mDateEntity =newDateEntity(); mDateEntity.day = i; mDateEntity.isSelfMonthDate =true; mDateEntity.year = year; mDateEntity.month = month;if(j >=7) { j =0; } selfDaysEndWeek = j; mDateEntity.weekDay = weekDayRow[j];if((year == mCurrenYear) && (month == mCurrenMonth) && i == mCurrenDay) { mDateEntity.isNowDate =true; } mDataList.add(mDateEntity); }/*** 添加后面下一个月的 */for(inti =1, j = selfDaysEndWeek +1; i <= nextMonthDays; i++, j++) { mDateEntity =newDateEntity(); mDateEntity.day = i; mDateEntity.isSelfMonthDate =false; mDateEntity.year = year; mDateEntity.month = month +1;if(j >=7) { j =0; } mDateEntity.weekDay = weekDayRow[j]; mDataList.add(mDateEntity); }returnmDataList; }/** 通过年月,获取这个月一共有多少天 */publicintgetDays(intyear,intmonth) {intdays =0;if((year %4==0&& (year %100!=0)) || (year %400==0)) {if(month >0&& month <=12) { days = leapYearMonthDay[month -1]; } }else{if(month >0&& month <=12) { days = commonYearMonthDay[month -1]; } }returndays; }/** 获取星期的排列 */publicString[]getWeekDayRow() {returnweekDayRow; }/** 初始化当前系统的日期 */publicvoidinitNowDate() { Date date =newDate(); SimpleDateFormat simpleFormat =newSimpleDateFormat("yyyy-M-d"); String currentDate = simpleFormat.format(date);// 当期日期mCurrenYear = Integer.parseInt(currentDate.split("-")[0]); mCurrenMonth = Integer.parseInt(currentDate.split("-")[1]); mCurrenDay = Integer.parseInt(currentDate.split("-")[2]);this.mYear = mCurrenYear;this.mMonth = mCurrenMonth; }/** 通过年,月获取当前月的第一天1日为星期几 ,返回0是星期天,1是星期一,依次类推 */publicintgetWeekDay(intyear,intmonth) {intdayOfWeek;intgoneYearDays =0;intthisYearDays =0;booleanisLeapYear =false;intcommonYearMonthDay[] = {31,28,31,30,31,30,31,31,30,31,30,31};intleapYearMonthDay[] = {31,29,31,30,31,30,31,31,30,31,30,31};for(inti =1900; i < year; i++) {// 从1900年开始算起,1900年1月1日为星期一if((i %4==0&& (i %100!=0)) || (i %400==0)) { goneYearDays = goneYearDays +366; }else{ goneYearDays = goneYearDays +365; } }if((year %4==0&& (year %100!=0)) || (year %400==0)) { isLeapYear =true;for(inti =0; i < month -1; i++) { thisYearDays = thisYearDays + leapYearMonthDay[i]; } }else{ isLeapYear =false;for(inti =0; i < month -1; i++) { thisYearDays = thisYearDays + commonYearMonthDay[i]; } } dayOfWeek = (goneYearDays + thisYearDays +1) %7; Log.d(this.getClass().getName(),"从1990到现在有"+ (goneYearDays + thisYearDays +1) +"天"); Log.d(this.getClass().getName(), year +"年"+ month +"月"+1+"日是星期"+ dayOfWeek);returndayOfWeek; } }

DateEntity.java:实体类,比较简单

packagecom.widget.mycalendar;importjava.io.Serializable;/** *@authordaij *@version1.0 日历实体类,添加的参数在获取日历实体集合的时候设置 */publicclassDateEntityimplementsSerializable{privatestaticfinallongserialVersionUID = -6053739977785155088L;/** 年 */publicintyear;/** 月 */publicintmonth;/** 日 */publicintday;/** 星期 */publicString weekDay;/** 是否为当前日期 */publicbooleanisNowDate;/** 是否为本月日期 */publicbooleanisSelfMonthDate; }

MainActivity.java:主Activity,用于提供模拟数据和实现交互,代码如下:

packagecom.example.mycalendar;import java.util.List;import android.app.Activity;import android.graphics.Color;import android.graphics.Point;import android.graphics.drawable.ColorDrawable;import android.os.Bundle;import android.text.TextUtils;import android.view.View;import android.view.View.OnClickListener;import android.widget.AdapterView;import android.widget.AdapterView.OnItemClickListener;import android.widget.ImageView;import android.widget.TextView;importcom.widget.mycalendar.CalendarGridView;importcom.widget.mycalendar.CalendarGridViewAdapter;importcom.widget.mycalendar.CalendarTool;importcom.widget.mycalendar.DateEntity;/** * @author daij * @version 1.0 日历:考勤记录 */public class MainActivity extends Activity { private CalendarGridViewAdapter mAdapter;private CalendarTool mCalendarTool;private CalendarGridView mGridView;private List<DateEntity> mDateEntityList;private Point mNowCalendarPoint;private ImageView mPrevioursIv;private ImageView mNextIv;private ImageView ivBack;private TextView mCalendarTv;private TextView tvDetail;@Override protected void onCreate(Bundle savedInstanceState) { setContentView(R.layout.activity_main);super.onCreate(savedInstanceState);initView();// 初始化ViewsetListeners();// requestXmas();//请求数据}/** 初始化view */public void initView() { mPrevioursIv = (ImageView) findViewById(R.id.calendar_bar_iv_previours);mNextIv = (ImageView) findViewById(R.id.calendar_bar_iv_next);ivBack = (ImageView) findViewById(R.id.iv_back);mCalendarTv = (TextView) findViewById(R.id.calendar_bar_tv_date);tvDetail = (TextView) findViewById(R.id.tv_detail);mGridView = (CalendarGridView) findViewById(R.id.calendar_gridview);mGridView.setSelector(new ColorDrawable(Color.TRANSPARENT));mGridView.setOnItemClickListener(new CalendarItemClickListener());mPrevioursIv.setOnClickListener(new ImageViewClickListener());mNextIv.setOnClickListener(new ImageViewClickListener());mCalendarTool = new CalendarTool(this);mNowCalendarPoint = mCalendarTool.getNowCalendar();mCalendarTv.setText(mNowCalendarPoint.x+"年"+ mNowCalendarPoint.y+"月");mDateEntityList = mCalendarTool.getDateEntityList(mNowCalendarPoint.x, mNowCalendarPoint.y);mAdapter = new CalendarGridViewAdapter(this, getResources());mAdapter.setDateList(mDateEntityList);mGridView.setAdapter(mAdapter);}/** 日历监听类 */class CalendarItemClickListener implements OnItemClickListener { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { // TODO 模拟数据 DateEntity itemDate = (DateEntity) view.getTag();if (!((TextUtils.equals(CalendarTool.SATURDAY, itemDate.weekDay)) || TextUtils.equals(CalendarTool.SUNDAY, itemDate.weekDay))) {// 非周末 if (((itemDate.year==&& itemDate.month<=8) || (itemDate.year<&& itemDate.month<=12)) && itemDate.isSelfMonthDate) {//.8以前,且日期在当月 if (itemDate.day>0&& itemDate.day<=20|| itemDate.day==25) {// 正常出勤 tvDetail.setText("备注:"+"\n"+" "+ itemDate.year+"年"+ itemDate.month+"月"+ itemDate.day+"日"+"--"+ itemDate.weekDay+"\n"+"正常出勤。");} else if (itemDate.day==21|| itemDate.day==22) {// 请假 tvDetail.setText("备注:"+"\n"+" "+ itemDate.year+"年"+ itemDate.month+"月"+ itemDate.day+"日"+"--"+ itemDate.weekDay+"\n"+"请假两天,从"+ itemDate.month+"月21日到"+ itemDate.month+"月22日");} else if (itemDate.day==23|| itemDate.day==24) {// 迟到、早退 if (itemDate.day==23) { tvDetail.setText("备注:"+"\n"+" "+ itemDate.year+"年"+ itemDate.month+"月"+ itemDate.day+"日"+"--"+ itemDate.weekDay+"\n"+"上午8:36打卡,下午5:40打卡"+"\n"+"上午迟到6分钟");} else if (itemDate.day==24) { tvDetail.setText("备注:"+"\n"+" "+ itemDate.year+"年"+ itemDate.month+"月"+ itemDate.day+"日"+"--"+ itemDate.weekDay+"\n"+"上午8:25打卡,下午5:29打卡"+"\n"+"下午早退1分钟");} } else { if (itemDate.month==8&& itemDate.day>25) { return;} tvDetail.setText("备注:"+"\n"+" "+ itemDate.year+"年"+ itemDate.month+"月"+ itemDate.day+"日"+"--"+ itemDate.weekDay+"\n"+"正常出勤。");} } } // Toast.makeText( // RecordCalendar.this, //"选中的是"+ itemDate.year+"年"+ itemDate.month+"月"// + itemDate.day+"日"+"--"+ itemDate.weekDay, // Toast.LENGTH_SHORT).show();} }/** 按钮 */class ImageViewClickListener implements OnClickListener { @Override public void onClick(View v) { switch (v.getId()) { case R.id.calendar_bar_iv_previours:// 上月 mDateEntityList.clear();mNowCalendarPoint = mCalendarTool.getNowCalendar();if (mNowCalendarPoint.x>=1990&& mNowCalendarPoint.x<2038) { if (mNowCalendarPoint.y-1<=0) { mDateEntityList = mCalendarTool.getDateEntityList( mNowCalendarPoint.x-1,12);} else { mDateEntityList = mCalendarTool.getDateEntityList( mNowCalendarPoint.x, mNowCalendarPoint.y-1);} mAdapter.setDateList(mDateEntityList);mAdapter.notifyDataSetChanged();mNowCalendarPoint = mCalendarTool.getNowCalendar();mCalendarTv.setText(mNowCalendarPoint.x+"年"+ mNowCalendarPoint.y+"月");}break;case R.id.calendar_bar_iv_next:// 下月 mNowCalendarPoint = mCalendarTool.getNowCalendar();mDateEntityList.clear();if (mNowCalendarPoint.x>=1990&& mNowCalendarPoint.x<2038) { if (mNowCalendarPoint.y+1>12) { mDateEntityList = mCalendarTool.getDateEntityList( mNowCalendarPoint.x+1,1);} else { mDateEntityList = mCalendarTool.getDateEntityList( mNowCalendarPoint.x, mNowCalendarPoint.y+1);} mAdapter.setDateList(mDateEntityList);mAdapter.notifyDataSetChanged();mNowCalendarPoint = mCalendarTool.getNowCalendar();mCalendarTv.setText(mNowCalendarPoint.x+"年"+ mNowCalendarPoint.y+"月");}break;} } } private void setListeners() { ivBack.setOnClickListener(new OnClickListener() { @Override public void onClick(View arg0) { MainActivity.this.finish();} });} @Override public void onDestroy() { super.onDestroy();} }

模拟数据说明:在8月25日(包含这天)前均有打卡记录,模拟每月21、22号请假,23、24号迟到或早退,其他工作日正常出勤。模拟数据仅作展示效果之用。

代码是最好的语言,再加上详细的注释,相信有一定Java编程基础和Android应用开发经验的人都能看懂。因为需要自己按照项目去扩展,本文只提供解决思路,希望读者不要懒惰,只有读懂本文的代码逻辑才能扩展出你所需的代码。

其他布局和图片样式等资源文件均在项目内可获取,附上源码地址:/detail/daijin888888/9020535

下载需要资源分1分,整理不容易,还望理解。

请尊重原创,转载请附上原文地址,谢谢!

原文地址:/daijin888888/article/details/47752723

版权声明:本文为博主原创文章,未经博主允许。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。