首页 > 代码库 > 自定义日历控件-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