转载请注明转自,谢谢:http://blog.csdn.net/wmlove_hqy/article/details/46716951
首先分析一下功能。
1.能够左右滑动,这里选择通过ViewPager实现。
2.能够点击、选中日期,说明每一天是一个单独的View,选中的状态的变化改变,这里用StateDrawbleList实现。
3.通过Calendar提供的方法实现数据填充。
我们先来看看效果。
好,让我们开始吧!
我首先在这里定义一个DayView.
public class DayView extends TextView{
private int backgroupColor = Color.GREEN;
private Date date;
private int day;
private int month;
public DayView(Context context) {
super(context);
setTextAlignment(TEXT_ALIGNMENT_CENTER);
setBackgroupColor();
setTextSize(20);
setHeight(150);
setWidth(150);
setTextColor(Color.BLACK);
setGravity(Gravity.CENTER);
}
//提供更改选中之后背景颜色
public void setSelectColor(int color){
this.backgroupColor = color;
setBackgroupColor();
postInvalidate();
}
private void setBackgroupColor(){
//设置点击时改变背景色
StateListDrawable drawable = new StateListDrawable();
ShapeDrawable shape = new ShapeDrawable(new OvalShape());
shape.setShaderFactory(new ShapeDrawable.ShaderFactory() {
@Override
public Shader resize(int i, int i1) {
return new LinearGradient(0,0,0,0,backgroupColor,backgroupColor, Shader.TileMode.REPEAT);
}
});
drawable.addState(new int[]{android.R.attr.state_selected}, shape);
setBackground(drawable);
}
//设置具体的日期
public void setDay(boolean showotherdays,boolean inrange){
//判断是否显示除了这个月份之外的日期。
//默认不显示
//当视图为周视图时,默认为真
if(!showotherdays){
String text = inrange ? day+"" : "";
boolean enable = inrange ? true : false;
setEnabled(enable);
setText(text);
return;
}
setText(String.valueOf(day));
}
//供外部调用,接受具体日期
public void setTimeRange(Calendar calendar) {
this.day = calendar.get(Calendar.DAY_OF_MONTH);
this.month = calendar.get(Calendar.MONTH);
this.date = calendar.getTime();
}
public String getTime(){
String time;
SimpleDateFormat format = new SimpleDateFormat("dd MMMM", Locale.ENGLISH);
time = format.format(date);
return time;
}
public Date getDate(){
return this.date;
}
}
这就表示每一天的日期,只需要实例化出来并且设置他的日期。
每一天有了,现在需要一个容器把他放进去,可以是一个月,或者是一周。
所以我定义了一个TimeView来放这些Day.
public class TimeView extends RelativeLayout implements View.OnClickListener{
//每个星期有的天数
private static final int DEFAULT_DAY_IN_WEEK = 7;
//一个月有几个星期,为月视图的时候默认是 6个星期,周视图为 1个星期。
private int WEEK_IN_MONTH;
//用来表示周视图或者月视图的flag
private int timeflag;
private boolean showOtherDay;
private List<DayView> dayViewList;
private Calendar mCalendarCurrent = null;
//当Day被选中时候的回调
private OnDaySelectListener mDaySelectCallBack;
//当Day选中状态切换的时候的回调
private OnDaySelectChangeListener mOnDaySelectChangeListener;
public void setOnDaySelectListener(OnDaySelectListener mDaySelectCallBack) {
this.mDaySelectCallBack = mDaySelectCallBack;
}
public void setOnDaySelectChangeListener(OnDaySelectChangeListener onDaySelectChangeListener){
this.mOnDaySelectChangeListener = onDaySelectChangeListener;
}
public TimeView(Context context,Calendar calendarCurrent,int timeflag) {
super(context);
mCalendarCurrent = CalendarUtils.getCalenar();
CalendarUtils.copyTo(calendarCurrent,mCalendarCurrent);
showOtherDay = false;
timeflag = Calendar.MONTH;
dayViewList = new ArrayList<>()
WEEK_IN_MONTH = (timeflag==Calendar.MONTH) ? 6 : 1;
showOtherDay = (timeflag==Calendar.WEEK_OF_YEAR) ? true : false;
setUpView();
setDayView(timeflag);
}
//画出每一行,填充后用来显示一周
private LinearLayout makeRow(){
LinearLayout row = new LinearLayout(getContext());
row.setOrientation(LinearLayout.HORIZONTAL);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,0,1f);
row.setLayoutParams(params);
return row;
}
//把周视图或者月视图画出来
private void setUpView() {
LinearLayout root = new LinearLayout(getContext());
root.setOrientation(LinearLayout.VERTICAL);
LinearLayout row;
for(int i=0;i<WEEK_IN_MONTH;i++){
row = makeRow();
for(int x=0;x<DEFAULT_DAY_IN_WEEK;x++){
DayView day = new DayView(getContext());
day.setOnClickListener(this);
row.addView(day, new LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.MATCH_PARENT, 1f));
dayViewList.add(day);
}
root.addView(row);
}
addView(root, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, 1f));
}
//把每一个DayView设置日期
private void setDayView(int timeflag){
Calendar calendar = getWorkingCalendar();
for(DayView dayView : dayViewList){
dayView.setTimeRange(calendar);
dayView.setDay(showOtherDay, calendar.get(timeflag) == mCalendarCurrent.get(timeflag));
calendar.add(Calendar.DATE,1);
}
}
//把Calendar设置为每个月或者每个星期的第一天
private Calendar getWorkingCalendar(){
Calendar calendar = CalendarUtils.getCalenar();
CalendarUtils.copyTo(mCalendarCurrent, calendar);
if(timeflag==Calendar.MONTH){CalendarUtils.setToFirstDayInMonth(calendar);}
CalendarUtils.setToFirstDayInWeek(calendar);
return calendar;
}
public Calendar getTimeCalendar(){
Log.i("TAG",mCalendarCurrent.getTime()+"Timecurrent");
return this.mCalendarCurrent;
}
public Date getDate(){
return this.mCalendarCurrent.getTime();
}
public void setSelectColor(int color){
for(DayView dayView : dayViewList){
dayView.setSelectColor(color);
}
}
public void setTimeFlag(int timeflag){
this.timeflag = timeflag;
}
//处理点击事件
@Override
public void onClick(View view) {
String time = null;
Date date = null;
if(view instanceof DayView){
clearSelected();
DayView dayView = (DayView) view;
dayView.setSelected(true);
Toast.makeText(getContext(),dayView.getText(),Toast.LENGTH_SHORT).show();
time = dayView.getTime();
date = dayView.getDate();
postInvalidate();
}
mDaySelectCallBack.OnDaySelect(time,date);
mOnDaySelectChangeListener.SelectChangeCallBack();
}
//用于清除其他TimeView的选中状态
public void clearSelected() {
for(DayView other : dayViewList){
other.setSelected(false);
}
}
}
这就表示一个月或者一个星期的View,其实也就是ViewPager中的一个Item。
OnDaySelectListener 和OnDaySelectChangeListener 是定义用来回调的接口。
CalendarUtils是一个工具类。
那么有了Item,接下来就是添加到ViewPager中了。
而ViewPager也是放在一个容器之中,构成了这整个CalendarView。
public class CalendarView extends RelativeLayout {
private ViewPager mViewPager;
private CalendarViewAdapter mCalendarViewAdapter;
private DaySelectChanegListener mDaySelectChanegListener = new DaySelectChanegListener();
//默认是显示月视图的
private boolean inweek = false;
private int TimeFlag ;
private List<TimeView> timeViewList = new ArrayList<>();
private List<DaySelectChanegListener> OnTimeChangeListenerList = new ArrayList<>();
private Calendar mCalendarMax = null;
private Calendar mCalendarMin = null;
private Calendar mCalendarCurrent = null;
private OnTimeChangeListener mOnTimeChangeListener = null;
public int getTimeFlag(boolean inweek) {
return inweek ? Calendar.WEEK_OF_YEAR : Calendar.MONTH;
}
//把除了选中的Day所在的TimeView之外的day全部设置为非选中状态
private class DaySelectChanegListener implements OnDaySelectChangeListener {
@Override
public void SelectChangeCallBack() {
TimeView current = timeViewList.get(mViewPager.getCurrentItem());
for (TimeView other : timeViewList) {
if (other!=current){other.clearSelected();}
}
}
}
public CalendarView(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray t = context.obtainStyledAttributes(attrs,R.styleable.CalendarView);
inweek = t.getBoolean(R.styleable.CalendarView_viewinview,false);
t.recycle();
TimeFlag = getTimeFlag(inweek);
LinearLayout root = new LinearLayout(context);
root.setOrientation(LinearLayout.VERTICAL);
addTimeView();
LinearLayout title = new LinearLayout(context);
title.setOrientation(LinearLayout.HORIZONTAL);
Calendar calendar = CalendarUtils.getCalenar();
CalendarUtils.setToFirstDayInWeek(calendar);
SimpleDateFormat format = new SimpleDateFormat("EEE", Locale.ENGLISH);
for (int i = 0; i < 7; i++) {
TextView textView = new TextView(context);
textView.setWidth(150);
textView.setHeight(150);
textView.setGravity(Gravity.CENTER);
textView.setTextAlignment(TEXT_ALIGNMENT_CENTER);
textView.setTextColor(Color.BLACK);
textView.setText(format.format(calendar.getTime()));
Log.i("TAG", format.format(calendar.getTime()));
calendar.add(Calendar.DATE, 1);
title.addView(textView);
}
Log.i("TAG", title.getChildCount() + "childcount");
root.addView(title);
mViewPager = new ViewPager(context);
mCalendarViewAdapter = new CalendarViewAdapter();
mViewPager.setAdapter(mCalendarViewAdapter);
mViewPager.setCurrentItem(getIndex());
mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {}
@Override
public void onPageSelected(int position) {
TimeView timeView = timeViewList.get(position);
Date date = timeView.getDate();
mOnTimeChangeListener.OnTimeChange(date);
}
@Override
public void onPageScrollStateChanged(int state) {}
});
root.addView(mViewPager, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
addView(root);
}
//当前的ViewPager中的TimeView因为滑动而改变时的回调。
public void setOnTimeChangeListener(OnTimeChangeListener listener){
if(listener!=null){
this.mOnTimeChangeListener = listener;
}
else throw new NullPointerException("OnTimeChangeListener can not be null!");
}
public void setOnDaySelectListener(OnDaySelectListener listener){
if(listener!=null){
for(TimeView timeView : timeViewList){
timeView.setOnDaySelectListener(listener);
}
}
else throw new NullPointerException("OnDaySelectListener can not be null!");
}
//设置可以显示的最大日期
public void setMaxDate(Calendar calendarMax){
this.mCalendarMax = calendarMax;
postInvalidate();
}
//设置可以显示的最小日期
public void setMinDate(Calendar calendarMin){
this.mCalendarMin = calendarMin;
postInvalidate();
}
public void setSelectColor(int color){
for(TimeView timeView : timeViewList){
timeView.setSelectColor(color);
}
}
//设置当前的日期
public void setCurrentCalendar(Calendar currentCalendar){
this.mCalendarCurrent = currentCalendar;
postInvalidate();
}
//得到可以显示的最小Calendar
private Calendar getMinCalendarDay(){
if(mCalendarMin==null){
int mindate = -2;
Calendar min = CalendarUtils.getCalenar();
min.add(Calendar.YEAR, mindate);
return min;
}
return mCalendarMin;
}
//得到可以显示的最大Calendar
private Calendar getMaxCalendarDay(){
if(mCalendarMax==null){
int maxdate = 2;
Calendar max = CalendarUtils.getCalenar();
max.add(Calendar.YEAR,maxdate);
return max;
}
return mCalendarMax;
}
//把符合条件的TimeView添加到集合当中
private void addTimeView(){
Calendar minCalendar = getMinCalendarDay();
Calendar maxCalendar = getMaxCalendarDay();
if(mCalendarCurrent==null){
mCalendarCurrent = CalendarUtils.getCalenar();
}
CalendarUtils.copyTo(minCalendar,mCalendarCurrent);
Log.i("TAG",minCalendar.getTime()+"minCalendar"+maxCalendar.getTime()+"maxCalendar"+mCalendarCurrent.getTime()+"mCalendarCurrent");
while (CalendarUtils.isBefore(mCalendarCurrent, maxCalendar)){
TimeView timeView = new TimeView(getContext(),mCalendarCurrent,TimeFlag);
timeView.setOnDaySelectChangeListener(mDaySelectChanegListener);
timeViewList.add(timeView);
mCalendarCurrent.add(TimeFlag, 1);
}
}
//得到当前日期的Index
private int getIndex(){
Calendar c = CalendarUtils.getCalenar();
for(int i=0;i<timeViewList.size();i++){
TimeView timeView = timeViewList.get(i);
Calendar other = timeView.getTimeCalendar();
if(c.get(Calendar.YEAR)==other.get(Calendar.YEAR)
&&c.get(Calendar.MONTH)==other.get(Calendar.MONTH)){
if(TimeFlag==Calendar.WEEK_OF_YEAR){
if(c.get(Calendar.WEEK_OF_YEAR)==other.get(Calendar.WEEK_OF_YEAR)){return i;}
}
return i;
}
}
return 0;
}
//ViewPager的适配器
private class CalendarViewAdapter extends PagerAdapter{
@Override
public int getCount() {
return timeViewList.size();
}
@Override
public boolean isViewFromObject(View view, Object o) {
return view == o;
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
TimeView timeView = timeViewList.get(position);
Log.i("TAG",timeViewList.size()+"size");
container.addView(timeView);
return timeViewList.get(position);
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView(timeViewList.get(position));
}
}
}
到此就完成了这一个自定义的CalendarView,很简单吧。
总结一下。
主要是ViewPager中Item数据的填充,通过Calendar提供的一些方法就可以轻松的把你需要的日期筛选出来。然后就是把这些日期通过简单的逻辑判断填充到每一个所需要的View中,再就当做Item显示出来。还有一些基本的功能,如选中某一天能够回调这一天的日期,当前显示的月或者周是第几周或者哪一个月,再就是能够设置显示的最大日期和最小日期,选中某一天时的背景。最后是我主要想实现的,能够通过在布局文件中设置CalendarView是显示月视图还是周视图。
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:wmlove="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent" tools:context=".MainActivity">
<wmlove.library.CalendarView
wmlove:viewinview="false"
android:id="@+id/calendarview"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</wmlove.library.CalendarView>
</RelativeLayout>
如果有考虑不周到的地方还提出,一同探讨,谢谢。
也可以去我的GitHubh上给我Pull Requests,或者提issues
https://github.com/t154191277/CalendarView-Android