我们在开发中会遇到使用到日历控件,下面就介绍一个自定义日历控件。
思路:
1.自定义类CalendarView继承LinearLayout,使用布局文件,显示布局。
2.使用ViewFlipper,里面添加GridView,当月的日期。
3.使用手势GestureDetector,控制ViewFlipper的滑动。
calen_calendar.xml
<span style="font-family:SimSun;font-size:18px;"><?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<ImageView
android:id="@+id/prevMonth"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_weight="1"
android:src="@drawable/prev_month" />
<TextView
android:id="@+id/currentMonth"
android:layout_width="0dp"
android:layout_height="35dp"
android:layout_weight="3"
android:gravity="center"
android:text="2014年6月"
android:textColor="#177ed6"
android:textSize="18dp" />
<ImageView
android:id="@+id/nextMonth"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_weight="1"
android:src="@drawable/next_month" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="20dp"
android:background="#ffffffff" >
<TextView
style="@style/weekName"
android:text="日"
android:textColor="#177ed6" />
<TextView
style="@style/weekName"
android:text="一" />
<TextView
style="@style/weekName"
android:text="二" />
<TextView
style="@style/weekName"
android:text="三" />
<TextView
style="@style/weekName"
android:text="四" />
<TextView
style="@style/weekName"
android:text="五" />
<TextView
style="@style/weekName"
android:text="六"
android:textColor="#177ed6" />
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#ff8091a8" />
<ViewFlipper
android:id="@+id/flipper"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
</LinearLayout></span>
<span style="font-family:SimSun;font-size:18px;">public class CalendarView extends LinearLayout implements OnClickListener {
private final String TAG = CalendarView.class.getSimpleName();
private int year_c = 0;// 今天的年份
private int month_c = 0;// 今天的月份
private int day_c = 0;// 今天的日期
private String currentDate = "";
private Context mContext;
private TextView currentMonth;// 显示日期
private ImageView prevMonth;// 去上一个月
private ImageView nextMonth;// 去下一个月
private int gvFlag = 0;
private GestureDetector gestureDetector = null;
private CalendarAdapter calV = null;
private ViewFlipper flipper = null;
private GridView gridView = null;
private static int jumpMonth = 0; // 每次滑动,增加或减去一个月,默认为0(即显示当前月)
private static int jumpYear = 0; // 滑动跨越一年,则增加或者减去一年,默认为0(即当前年)
private ClickDataListener clickDataListener;
public CalendarView(Context context) {
this(context, null);
}
public CalendarView(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
initView();
}
private void initView() {
View view = View.inflate(mContext, R.layout.calen_calendar, this);
currentMonth = (TextView) view.findViewById(R.id.currentMonth);
prevMonth = (ImageView) view.findViewById(R.id.prevMonth);
nextMonth = (ImageView) view.findViewById(R.id.nextMonth);
setListener();
setCurrentDay();
gestureDetector = new GestureDetector(mContext, new MyGestureListener());
flipper = (ViewFlipper) findViewById(R.id.flipper);
flipper.removeAllViews();
calV = new CalendarAdapter(mContext, getResources(), jumpMonth,
jumpYear, year_c, month_c, day_c);
addGridView();
gridView.setAdapter(calV);
flipper.addView(gridView, 0);
addTextToTopTextView(currentMonth);
}
private void setListener() {
prevMonth.setOnClickListener(this);
nextMonth.setOnClickListener(this);
}
public void setClickDataListener(ClickDataListener clickDataListener) {
this.clickDataListener = clickDataListener;
}
private void setCurrentDay() {
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-M-dd");
currentDate = sdf.format(date); // 当期日期
year_c = Integer.parseInt(currentDate.split("-")[0]);
month_c = Integer.parseInt(currentDate.split("-")[1]);
day_c = Integer.parseInt(currentDate.split("-")[2]);
}
/**
* 移动到下一个月
*
* @param gvFlag
*/
private void enterNextMonth(int gvFlag) {
addGridView(); // 添加一个gridView
jumpMonth++; // 下一个月
calV = new CalendarAdapter(mContext, this.getResources(), jumpMonth,
jumpYear, year_c, month_c, day_c);
gridView.setAdapter(calV);
addTextToTopTextView(currentMonth); // 移动到下一月后,将当月显示在头标题中
gvFlag++;
flipper.addView(gridView, gvFlag);
flipper.setInAnimation(AnimationUtils.loadAnimation(mContext,
R.anim.push_left_in));
flipper.setOutAnimation(AnimationUtils.loadAnimation(mContext,
R.anim.push_left_out));
flipper.showNext();
flipper.removeViewAt(0);
}
/**
* 移动到上一个月
*
* @param gvFlag
*/
private void enterPrevMonth(int gvFlag) {
addGridView(); // 添加一个gridView
jumpMonth--; // 上一个月
calV = new CalendarAdapter(mContext, this.getResources(), jumpMonth,
jumpYear, year_c, month_c, day_c);
gridView.setAdapter(calV);
gvFlag++;
addTextToTopTextView(currentMonth); // 移动到上一月后,将当月显示在头标题中
flipper.addView(gridView, gvFlag);
flipper.setInAnimation(AnimationUtils.loadAnimation(mContext,
R.anim.push_right_in));
flipper.setOutAnimation(AnimationUtils.loadAnimation(mContext,
R.anim.push_right_out));
flipper.showPrevious();
flipper.removeViewAt(0);
}
/**
* 添加头部的年份 闰哪月等信息
*
* @param view
*/
private void addTextToTopTextView(TextView view) {
StringBuffer textDate = new StringBuffer();
// draw = getResources().getDrawable(R.drawable.top_day);
// view.setBackgroundDrawable(draw);
textDate.append(calV.getShowYear()).append("年")
.append(calV.getShowMonth()).append("月").append("\t");
view.setText(textDate);
}
private void addGridView() {
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
// 取得屏幕的宽度和高度
WindowManager windowManager = ((Activity) mContext).getWindowManager();
Display display = windowManager.getDefaultDisplay();
int Width = display.getWidth();
int Height = display.getHeight();
gridView = new GridView(mContext);
gridView.setNumColumns(7);
gridView.setColumnWidth(40);
// gridView.setStretchMode(GridView.STRETCH_COLUMN_WIDTH);
if (Width == 720 && Height == 1280) {
gridView.setColumnWidth(40);
}
gridView.setGravity(Gravity.CENTER_VERTICAL);
gridView.setSelector(new ColorDrawable(Color.TRANSPARENT));
// 去除gridView边框
gridView.setVerticalSpacing(0);
gridView.setHorizontalSpacing(0);
gridView.setOnTouchListener(new OnTouchListener() {
// 将gridview中的触摸事件回传给gestureDetector
public boolean onTouch(View v, MotionEvent event) {
// TODO Auto-generated method stub
return gestureDetector.onTouchEvent(event);
}
});
gridView.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> arg0, View arg1,
int position, long arg3) {
// TODO Auto-generated method stub
// 点击任何一个item,得到这个item的日期(排除点击的是周日到周六(点击不响应))
int startPosition = calV.getStartPositon();
int endPosition = calV.getEndPosition();
if (startPosition <= position + 7
&& position <= endPosition - 7) {
String scheduleDay = calV.getDateByClickItem(position)
.split("\\.")[0]; // 这一天的阳历
String scheduleYear = calV.getShowYear();
String scheduleMonth = calV.getShowMonth();
((CalendarAdapter) arg0.getAdapter())
.setColorDataPosition(position);
if (clickDataListener != null) {
clickDataListener.clickData(scheduleYear,
scheduleMonth, scheduleDay);
}
}
}
});
gridView.setLayoutParams(params);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.nextMonth: // 下一个月
enterNextMonth(gvFlag);
Log.d(TAG, "gvFlag=" + gvFlag);
break;
case R.id.prevMonth: // 上一个月
enterPrevMonth(gvFlag);
break;
}
}
class MyGestureListener extends SimpleOnGestureListener {
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
int gvFlag = 0; // 每次添加gridview到viewflipper中时给的标记
if (e1.getX() - e2.getX() > 120) {
// 像左滑动
enterNextMonth(gvFlag);
return true;
} else if (e1.getX() - e2.getX() < -120) {
// 向右滑动
enterPrevMonth(gvFlag);
return true;
}
return false;
}
}
}</span>
在initView中调用addGridView()。初始化显示日期的View,然后设置其适配器,然后添加到flipper.addView(gridView, 0);中
addTextToTopTextView()更改标题显示的年和月
enterNextMonth()显示下一个月日历,首先是将下个月的GridView添加到ViewFlipper中,显示下个月的信息,并且移除上个月的GridView。
enterPrevMonth():显示上一个月的日历,首先将上个月的GridView添加到ViewFlipper中,显示下个月的信息,并且移除当前月的GridView。
在MyGestureListener类中,判断是否显示上个月或者下一月信息的情况,向左滑动,显示上一个月信息,向右滑动,显示上月日历。
在gridView的setOnItemClickListener,如果点击日期,差通过接口传递出去,并且通知适配器,当前日期的颜色背景需要改变(当月的日期)。
适配器
<span style="font-family:SimSun;font-size:18px;">public CalendarAdapter(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));
}
</span>
在这里做将显示日历信息。根绝传入的stepMonth,表示点击间距,和系统的日期比较,系统时间的月表示0,如果显示下一个月信息,stepMonth++;如果显示上一个信息,stepMonth--;
通过stepMonth+系统显示的月,就可以知道要使显示yyyy年mm月了。
<span style="font-family:SimSun;font-size:18px;">private void getweek(int year, int month) {
int j = 1;
String lunarDay = "";
// 得到当前月的所有日程日期(这些日期需要标记)
for (int i = 0; i < dayNumber.length; i++) {
if (i < dayOfWeek) { // 前一个月
int temp = lastDaysOfMonth - dayOfWeek + 1;
lunarDay = lc.getLunarDate(year, month - 1, temp + i, false);
dayNumber[i] = (temp + i) + "." + lunarDay;
} else if (i < daysOfMonth + dayOfWeek) { // 本月
String day = String.valueOf(i - dayOfWeek + 1); // 得到的日期
lunarDay = lc.getLunarDate(year, month, i - dayOfWeek + 1,
false);
dayNumber[i] = i - dayOfWeek + 1 + "." + lunarDay;
// 对于当前月才去标记当前日期
if (sys_year.equals(String.valueOf(year))
&& sys_month.equals(String.valueOf(month))
&& sys_day.equals(day)) {
// 标记当前日期
colorDataPosition = i;
}
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);
}</span>
在此方法中,是要获取要展示的42天的信息,通过LunarCalendar中的getLunarDate()获取阴历的日期。
通过SpecialCalendar中的getWeekdayOfMonth获取当月第一天为星期几(dayOfWeek),
显示位置:position<dayOfWeek表示前一个月的日期
dayOfWeek<=position<daysOfMonth + dayOfWeek 表示当前要显示月日期
position>=daysOfMonth + dayOfWeek 表示显示下一个月日期
<span style="font-family:SimSun;font-size:18px;">public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = LayoutInflater.from(context).inflate(
R.layout.calen_calendar_item, null);
}
TextView textView = (TextView) convertView.findViewById(R.id.tvtext);
String d = dayNumber[position].split("\\.")[0];
String dv = dayNumber[position].split("\\.")[1];
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);
}
textView.setText(sp);
textView.setTextColor(Color.GRAY);
if (position < daysOfMonth + dayOfWeek && position >= dayOfWeek) {
// 当前月信息显示
textView.setTextColor(Color.BLACK);// 当月字体设黑
// drawable = new ColorDrawable(Color.rgb(23, 126, 214));
if (position % 7 == 0 || position % 7 == 6) {
// 当前月信息显示
textView.setTextColor(Color.rgb(23, 126, 214));// 当月字体设黑
}
}
if (colorDataPosition == position) {
// 设置当天的背景
textView.setTextColor(Color.WHITE);
textView.setBackgroundResource(R.drawable.bg_circle);
} else {
textView.setBackgroundColor(res
.getColor(android.R.color.transparent));
}
return convertView;
}</span>
在getView中,设置要显示的日期的信息,通过颜色区分出当月和上个月、下月。
有一个点击日期的标记,改变点击日期的背景颜色,但是有一个条件,只可以点击当月的日期,默认是系统的日期。
<span style="font-family:SimSun;font-size:18px;">public void setColorDataPosition(int position) {
if (position >= dayOfWeek && position < daysOfMonth + dayOfWeek) {
colorDataPosition = position;
notifyDataSetChanged();
}
}</span>
使用步骤:
activity_main.xml
<span style="font-family:SimSun;font-size:18px;"><?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<com.example.clander.view.CalendarView
android:id="@+id/calendarview"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
</com.example.clander.view.CalendarView>
</LinearLayout></span>
CalendarActivity.java
<span style="font-family:SimSun;font-size:18px;">public class CalendarActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
CalendarView calendarview = (CalendarView) findViewById(R.id.calendarview);
//设置点击日期的事件监听
calendarview.setClickDataListener(new ClickDataListener() {
@Override
public void clickData(String year, String month, String day) {
Toast.makeText(getApplicationContext(),
year + "-" + month + "-" + day, 0).show();
}
});
}
}</span>
效果图: