首页 > 代码库 > 自定义日历控件-CalendarView
自定义日历控件-CalendarView
转载请注明出处: http://blog.csdn.net/forwardyzk/article/details/43056675
我们在开发中会遇到使用到日历控件,下面就介绍一个自定义日历控件。
思路:
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=http://www.mamicode.com/"@drawable/prev_month" />><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>源码下载: http://download.csdn.net/detail/forwardyzk/8390217
效果图:
自定义日历控件-CalendarView