这里主要记录一下在编写日历apk过程中一些主要的点:先看下效果图
一、主要功能
二、基本结构
我们要实现的日历控件采用GestureDetector构造器,使用OnGestureListener监听滑动。目前我们设定日历左右滑动为月份切换的操作,每一个月份显示通过GridView实现,里面的数据是通过构造CalendarView,将其绑定到GridView。每次左右滑动,都会刷新CalendarView。布局上,会在GridView上显示当前日期农历时间。
三、主要代码及解析
CalendarActivity.java
/**
* 日历显示activity
*
* @author 孔乙己大叔
*/
public class CalendarActivity extends Activity implements OnGestureListener {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
gestureDetector = new GestureDetector(this, this);
flipper = (ViewFlipper) findViewById(R.id.flipper);
flipper.removeAllViews();
calV = new CalendarView(this, getResources(), jumpMonth, jumpYear, year_c, month_c, day_c);
addGridView();
gridView.setAdapter(calV);
flipper.addView(gridView, 0);
topText = (BorderText) findViewById(R.id.toptext);
addTextToTopTextView(topText);
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
int gvFlag = 0; //每次添加gridview到viewflipper中时给的标记
if (e1.getX() - e2.getX() > 120) {
//像左滑动
addGridView(); //添加一个gridView
jumpMonth++; //下一个月
calV = new CalendarView(this, getResources(), jumpMonth, jumpYear, year_c, month_c, day_c);
gridView.setAdapter(calV);
addTextToTopTextView(topText);
gvFlag++;
flipper.addView(gridView, gvFlag);
this.flipper.setInAnimation(AnimationUtils.loadAnimation(this, R.anim.push_left_in));
this.flipper.setOutAnimation(AnimationUtils.loadAnimation(this, R.anim.push_left_out));
this.flipper.showNext();
flipper.removeViewAt(0);
return true;
} else if (e1.getX() - e2.getX() < -120) {
//向右滑动
addGridView(); //添加一个gridView
jumpMonth--; //上一个月
calV = new CalendarView(this, getResources(), jumpMonth, jumpYear, year_c, month_c, day_c);
gridView.setAdapter(calV);
gvFlag++;
addTextToTopTextView(topText);
//flipper.addView(gridView);
flipper.addView(gridView, gvFlag);
this.flipper.setInAnimation(AnimationUtils.loadAnimation(this, R.anim.push_right_in));
this.flipper.setOutAnimation(AnimationUtils.loadAnimation(this, R.anim.push_right_out));
this.flipper.showPrevious();
flipper.removeViewAt(0);
return true;
}
return false;
}
/**
* 创建菜单
*/
@Override
public boolean onCreateOptionsMenu(Menu menu) {
menu.add(0, menu.FIRST, menu.FIRST, "今天");
menu.add(0, menu.FIRST + 1, menu.FIRST + 1, "跳转");
menu.add(0, menu.FIRST + 2, menu.FIRST + 2, "日程");
menu.add(0, menu.FIRST + 3, menu.FIRST + 3, "日期转换");
return super.onCreateOptionsMenu(menu);
}
/**
* 选择菜单
*/
@Override
public boolean onMenuItemSelected(int featureId, MenuItem item) {
switch (item.getItemId()) {
case Menu.FIRST:
//跳转到今天
int xMonth = jumpMonth;
int xYear = jumpYear;
int gvFlag = 0;
jumpMonth = 0;
jumpYear = 0;
addGridView(); //添加一个gridView
year_c = Integer.parseInt(currentDate.split("-")[0]);
month_c = Integer.parseInt(currentDate.split("-")[1]);
day_c = Integer.parseInt(currentDate.split("-")[2]);
calV = new CalendarView(this, getResources(), jumpMonth, jumpYear, year_c, month_c, day_c);
gridView.setAdapter(calV);
addTextToTopTextView(topText);
gvFlag++;
flipper.addView(gridView, gvFlag);
if (xMonth == 0 && xYear == 0) {
//nothing to do
} else if ((xYear == 0 && xMonth > 0) || xYear > 0) {
this.flipper.setInAnimation(AnimationUtils.loadAnimation(this, R.anim.push_left_in));
this.flipper.setOutAnimation(AnimationUtils.loadAnimation(this, R.anim.push_left_out));
this.flipper.showNext();
} else {
this.flipper.setInAnimation(AnimationUtils.loadAnimation(this, R.anim.push_right_in));
this.flipper.setOutAnimation(AnimationUtils.loadAnimation(this, R.anim.push_right_out));
this.flipper.showPrevious();
}
flipper.removeViewAt(0);
break;
case Menu.FIRST + 1:
new DatePickerDialog(this, new OnDateSetListener() {
@Override
public void onDateSet(DatePicker view, int year, int monthOfYear,
int dayOfMonth) {
//1901-1-1 ----> 2049-12-31
if (year < 1901 || year > 2049) {
//不在查询范围内
new AlertDialog.Builder(CalendarActivity.this).setTitle("错误日期").setMessage("跳转日期范围(1901/1/1-2049/12/31)").setPositiveButton("确认", null).show();
} else {
int gvFlag = 0;
addGridView(); //添加一个gridView
calV = new CalendarView(CalendarActivity.this, CalendarActivity.this.getResources(), year, monthOfYear + 1, dayOfMonth);
gridView.setAdapter(calV);
addTextToTopTextView(topText);
gvFlag++;
flipper.addView(gridView, gvFlag);
if (year == year_c && monthOfYear + 1 == month_c) {
//nothing to do
}
if ((year == year_c && monthOfYear + 1 > month_c) || year > year_c) {
CalendarActivity.this.flipper.setInAnimation(AnimationUtils.loadAnimation(CalendarActivity.this, R.anim.push_left_in));
CalendarActivity.this.flipper.setOutAnimation(AnimationUtils.loadAnimation(CalendarActivity.this, R.anim.push_left_out));
CalendarActivity.this.flipper.showNext();
} else {
CalendarActivity.this.flipper.setInAnimation(AnimationUtils.loadAnimation(CalendarActivity.this, R.anim.push_right_in));
CalendarActivity.this.flipper.setOutAnimation(AnimationUtils.loadAnimation(CalendarActivity.this, R.anim.push_right_out));
CalendarActivity.this.flipper.showPrevious();
}
flipper.removeViewAt(0);
//跳转之后将跳转之后的日期设置为当期日期
year_c = year;
month_c = monthOfYear + 1;
day_c = dayOfMonth;
jumpMonth = 0;
jumpYear = 0;
}
}
}, year_c, month_c - 1, day_c).show();
break;
case Menu.FIRST + 2:
Intent intent = new Intent();
intent.setClass(CalendarActivity.this, ScheduleAll.class);
startActivity(intent);
break;
case Menu.FIRST + 3:
Intent intent1 = new Intent();
intent1.setClass(CalendarActivity.this, CalendarConvert.class);
intent1.putExtra("date", new int[]{year_c, month_c, day_c});
startActivity(intent1);
break;
}
return super.onMenuItemSelected(featureId, item);
}
//添加头部的年份 闰哪月等信息
public void addTextToTopTextView(TextView view) {
StringBuffer textDate = new StringBuffer();
view.setBackgroundResource(R.drawable.top_day);
view.setBackground(getResources().getDrawable(R.drawable.top_day));
textDate.append(calV.getShowYear()).append("年").append(calV.getShowMonth()).append("月").append("\t");
if (!calV.getLeapMonth().equals("") && calV.getLeapMonth() != null) {
textDate.append("闰").append(calV.getLeapMonth()).append("月").append("\t");
}
textDate.append(calV.getAnimalsYear()).append("年").append("(").append(calV.getCyclical()).append("年)");
view.setText(textDate);
view.setTextColor(Color.BLACK);
view.setTypeface(Typeface.DEFAULT_BOLD);
}
//添加gridview
private void addGridView() {
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
DisplayMetrics displayMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
int Width = displayMetrics.widthPixels;
int Height = displayMetrics.heightPixels;
gridView = new GridView(this);
gridView.setNumColumns(7);
gridView.setColumnWidth(46);
if (Width == 480 && Height == 800) {
gridView.setColumnWidth(69);
}
gridView.setGravity(Gravity.CENTER_VERTICAL);
gridView.setSelector(new ColorDrawable(Color.TRANSPARENT)); // 去除gridView边框
gridView.setVerticalSpacing(1);
gridView.setHorizontalSpacing(1);
gridView.setBackgroundResource(R.drawable.gridview_bk);
gridView.setOnTouchListener(new OnTouchListener() {
//将gridview中的触摸事件回传给gestureDetector
@Override
public boolean onTouch(View v, MotionEvent event) {
// TODO Auto-generated method stub
return CalendarActivity.this.gestureDetector
.onTouchEvent(event);
}
});
gridView.setOnItemClickListener(new OnItemClickListener() {
//gridView中的每一个item的点击事件
@Override
public void onItemClick(AdapterView> arg0, View arg1, int position,
long arg3) {
//点击任何一个item,得到这个item的日期(排除点击的是周日到周六(点击不响应))
int startPosition = calV.getStartPositon();
int endPosition = calV.getEndPosition();
if (startPosition <= position && position <= endPosition) {
String scheduleDay = calV.getDateByClickItem(position).split("\\.")[0]; //这一天的阳历
String scheduleYear = calV.getShowYear();
String scheduleMonth = calV.getShowMonth();
String week = "";
//通过日期查询这一天是否被标记,如果标记了日程就查询出这天的所有日程信息
String[] scheduleIDs = dao.getScheduleByTagDate(Integer.parseInt(scheduleYear), Integer.parseInt(scheduleMonth), Integer.parseInt(scheduleDay));
if (scheduleIDs != null && scheduleIDs.length > 0) {
//跳转到显示这一天的所有日程信息界面
Intent intent = new Intent();
intent.setClass(CalendarActivity.this, ScheduleInfoView.class);
intent.putExtra("scheduleID", scheduleIDs);
startActivity(intent);
} else {
//直接跳转到需要添加日程的界面
//得到这一天是星期几
switch (position % 7) {
case 0:
week = "星期日";
break;
case 1:
week = "星期一";
break;
case 2:
week = "星期二";
break;
case 3:
week = "星期三";
break;
case 4:
week = "星期四";
break;
case 5:
week = "星期五";
break;
case 6:
week = "星期六";
break;
}
ArrayList scheduleDate = new ArrayList();
scheduleDate.add(scheduleYear);
scheduleDate.add(scheduleMonth);
scheduleDate.add(scheduleDay);
scheduleDate.add(week);
//scheduleDate.add(scheduleLunarDay);
Intent intent = new Intent();
intent.putStringArrayListExtra("scheduleDate", scheduleDate);
intent.setClass(CalendarActivity.this, ScheduleView.class);
startActivity(intent);
}
}
}
});
gridView.setLayoutParams(params);
}
}
自定义view实现:
/**
* 日历gridview中的每一个item显示的textview
*
* @author 孔乙己大叔
*/
public class CalendarView extends BaseAdapter {
public CalendarView(Context context, Resources rs, int jumpMonth, int jumpYear, int year_c, int month_c, int day_c) {
this();
this.context = context;
sc = new SpecialCalendar();
lc = new LunarCalendar();
this.res = rs;
int stepYear = year_c + jumpYear;
int stepMonth = month_c + jumpMonth;
if (stepMonth > 0) {
//往下一个月滑动
if (stepMonth % 12 == 0) {
stepYear = year_c + stepMonth / 12 - 1;
stepMonth = 12;
} else {
stepYear = year_c + stepMonth / 12;
stepMonth = stepMonth % 12;
}
} else {
//往上一个月滑动
stepYear = year_c - 1 + stepMonth / 12;
stepMonth = stepMonth % 12 + 12;
if (stepMonth % 12 == 0) {
}
}
currentYear = String.valueOf(stepYear);; //得到当前的年份
currentMonth = String.valueOf(stepMonth); //得到本月 (jumpMonth为滑动的次数,每滑动一次就增加一月或减一月)
currentDay = String.valueOf(day_c); //得到当前日期是哪天
getCalendar(Integer.parseInt(currentYear), Integer.parseInt(currentMonth));
}
public CalendarView(Context context, Resources rs, int year, int month, int day) {
this();
this.context = context;
sc = new SpecialCalendar();
lc = new LunarCalendar();
this.res = rs;
currentYear = String.valueOf(year);
; //得到跳转到的年份
currentMonth = String.valueOf(month); //得到跳转到的月份
currentDay = String.valueOf(day); //得到跳转到的天
getCalendar(Integer.parseInt(currentYear), Integer.parseInt(currentMonth));
}
@Override
public int getCount() {
// TODO Auto-generated method stub
return dayNumber.length;
}
@Override
public Object getItem(int position) {
// TODO Auto-generated method stub
return position;
}
@Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = LayoutInflater.from(context).inflate(R.layout.calendar, null);
}
TextView textView = (TextView) convertView.findViewById(R.id.tvtext);
String d = dayNumber[position].split("\\.")[0];
String dv = dayNumber[position].split("\\.")[1];
//Typeface typeface = Typeface.createFromAsset(context.getAssets(), "fonts/Helvetica.ttf");
//textView.setTypeface(typeface);
SpannableString sp = new SpannableString(d + "\n" + dv);
sp.setSpan(new StyleSpan(android.graphics.Typeface.BOLD), 0, d.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
sp.setSpan(new RelativeSizeSpan(1.2f), 0, d.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
if (dv != null || dv != "") {
sp.setSpan(new RelativeSizeSpan(0.75f), d.length() + 1, dayNumber[position].length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
//sp.setSpan(new ForegroundColorSpan(Color.MAGENTA), 14, 16, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
textView.setText(sp);
textView.setTextColor(Color.GRAY);
if (position < 7) {
//设置周
textView.setTextColor(Color.BLACK);
textView.setBackgroundResource(R.drawable.week_top);
}
if (position < daysOfMonth + dayOfWeek + 7 && position >= dayOfWeek + 7) {
// 当前月信息显示
textView.setTextColor(Color.BLACK);// 当月字体设黑
drawable = res.getDrawable(R.drawable.item);
//textView.setBackgroundDrawable(drawable);
//textView.setBackgroundColor(Color.WHITE);
}
if (schDateTagFlag != null && schDateTagFlag.length > 0) {
for (int i = 0; i < schDateTagFlag.length; i++) {
if (schDateTagFlag[i] == position) {
//设置日程标记背景
textView.setBackgroundResource(R.drawable.mark);
}
}
}
if (currentFlag == position) {
//设置当天的背景
textView.setBackgroundResource(R.drawable.current_day_bgc);
textView.setTextColor(Color.WHITE);
}
return convertView;
}
//得到某年的某月的天数且这月的第一天是星期几
public void getCalendar(int year, int month) {
isLeapyear = sc.isLeapYear(year); //是否为闰年
daysOfMonth = sc.getDaysOfMonth(isLeapyear, month); //某月的总天数
dayOfWeek = sc.getWeekdayOfMonth(year, month); //某月第一天为星期几
lastDaysOfMonth = sc.getDaysOfMonth(isLeapyear, month - 1); //上一个月的总天数
Log.d("DAY", isLeapyear + " ====== " + daysOfMonth + " ============ " + dayOfWeek + " ========= " + lastDaysOfMonth);
getweek(year, month);
}
//将一个月中的每一天的值添加入数组dayNuber中
private void getweek(int year, int month) {
int j = 1;
int flag = 0;
String lunarDay = "";
//得到当前月的所有日程日期(这些日期需要标记)
dao = new ScheduleDAO(context);
ArrayList dateTagList = dao.getTagDate(year, month);
if (dateTagList != null && dateTagList.size() > 0) {
schDateTagFlag = new int[dateTagList.size()];
}
for (int i = 0; i < dayNumber.length; i++) {
// 周一
if (i < 7) {
dayNumber[i] = week[i] + "." + " ";
} else if (i < dayOfWeek + 7) { //前一个月
int temp = lastDaysOfMonth - dayOfWeek + 1 - 7;
lunarDay = lc.getLunarDate(year, month - 1, temp + i, false);
dayNumber[i] = (temp + i) + "." + lunarDay;
} else if (i < daysOfMonth + dayOfWeek + 7) { //本月
String day = String.valueOf(i - dayOfWeek + 1 - 7); //得到的日期
lunarDay = lc.getLunarDate(year, month, i - dayOfWeek + 1 - 7, false);
dayNumber[i] = i - dayOfWeek + 1 - 7 + "." + lunarDay;
//对于当前月才去标记当前日期
if (sys_year.equals(String.valueOf(year)) && sys_month.equals(String.valueOf(month)) && sys_day.equals(day)) {
//笔记当前日期
currentFlag = i;
}
//标记日程日期
if (dateTagList != null && dateTagList.size() > 0) {
for (int m = 0; m < dateTagList.size(); m++) {
ScheduleDateTag dateTag = dateTagList.get(m);
int matchYear = dateTag.getYear();
int matchMonth = dateTag.getMonth();
int matchDay = dateTag.getDay();
if (matchYear == year && matchMonth == month && matchDay == Integer.parseInt(day)) {
schDateTagFlag[flag] = i;
flag++;
}
}
}
setShowYear(String.valueOf(year));
setShowMonth(String.valueOf(month));
setAnimalsYear(lc.animalsYear(year));
setLeapMonth(lc.leapMonth == 0 ? "" : String.valueOf(lc.leapMonth));
setCyclical(lc.cyclical(year));
} else { //下一个月
lunarDay = lc.getLunarDate(year, month + 1, j, false);
dayNumber[i] = j + "." + lunarDay;
j++;
}
}
String abc = "";
for (int i = 0; i < dayNumber.length; i++) {
abc = abc + dayNumber[i] + ":";
}
Log.d("DAYNUMBER", abc);
}
}
判断是否为闰年,当月多少天数,当天是星期几代码实现:
// 判断是否为闰年
public boolean isLeapYear(int year) {
if (year % 100 == 0 && year % 400 == 0) {
return true;
} else if (year % 100 != 0 && year % 4 == 0) {
return true;
}
return false;
}
//得到某月有多少天数
public int getDaysOfMonth(boolean isLeapyear, int month) {
switch (month) {
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12:
daysOfMonth = 31;
break;
case 4:
case 6:
case 9:
case 11:
daysOfMonth = 30;
break;
case 2:
if (isLeapyear) {
daysOfMonth = 29;
} else {
daysOfMonth = 28;
}
}
return daysOfMonth;
}
//指定某年中的某月的第一天是星期几
public int getWeekdayOfMonth(int year, int month) {
Calendar cal = Calendar.getInstance();
cal.set(year, month - 1, 1);
dayOfWeek = cal.get(Calendar.DAY_OF_WEEK) - 1;
return dayOfWeek;
}
四、小结
这里我们只介绍了日历的基本实现原理,和一些关键的点,其实这种实现方式相对还是比较简单的,容易理解,当然难免有不足的地方,后边根据需要再逐步完善和扩展吧。数据库操作暂时没进行解析,后面会单独用demo说明。
源码下载地址:https://download.csdn.net/download/Ctrl_qun/12436629