日历控件--小程序
实现横向滑动的日历控件可以作为签到的日历控件
看见MaterialCalendarView 仿照它大致的思路自己实现一个CanlendarView,
给一个ClaendarPagerView extends viewgroup添加四十二个TextView,用来显示一个月的天数,
然后在ViewPagerAdapter 中填充CalendarPagerView,给viewPager填充adpater,就可以实现横向滑动的日历控件了。其中比较麻烦的就是日期的处理,还有一些小的细节.
首先实现单独天数的控件继承CheckedTextView
public class DayView extends CheckedTextView {
//这一天的日期类
private CalendarDay day;
//构造函数给个Visible参数 决定日期是否可见
public DayView(Context context,Calendar day,int visible) {
super(context);
setGravity(Gravity.CENTER);
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1){
setTextAlignment(TEXT_ALIGNMENT_CENTER);
}
this.day = CalendarDay.from(day);
setVisibility(visible);
setText(CalendarUtils.getDay(day)+"");
}
//返回这个日期是几号
public String getLabel(){
return CalendarUtils.getDay(day.getCalendar())+"";
}
//返回真实日期 Calendar类的月份是从0开始的
public CalendarDay getDate(){
CalendarDay tempDay = CalendarDay.from(day.getYear(),day.getMonth()+1,day.getDay());
return tempDay;
}
//返回CalendarDay对象 CalendarDay类是对Calendar的封装类
public CalendarDay getDay(){
CalendarDay tempDay = CalendarDay.from(day.getYear(),day.getMonth(),day.getDay());
return tempDay;
}
//设置选择背景
private Drawable customBackground;
//设置背景图片
public void setCustomBackground(Drawable drawable){
if(drawable == null){
this.customBackground = null;
}else{
this.customBackground = drawable.getConstantState().newDrawable(getResources());
}
invalidate();
}
//自定义了一个TextSpan 给需要的日期集合设置这个样式 扩展日期控件的内容
// int mode mode 的不同设置不同的样式
//visible 决定日期显示不显示 ,如果是当前页面的日期就为true 不是当前页面的月份就不显示
public void applyTextSpan(int mode,boolean visible){
setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
if(mode == 0 || mode == 1){
String label = getLabel();
SpannableString formattedLabel = new SpannableString(getLabel());
formattedLabel.setSpan(new TextSpan(mode), 0, label.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
setText(formattedLabel);
}else{
setText(getLabel());
}
// invalidate();
}
//清空DayView的背景,
public void setUnselected(){
customBackground = null;
invalidate();
}
//设置DayVIew 点击日期后的背景
public void setSelected(){
customBackground = generateCircleDrawable(Color.GRAY);
invalidate();
}
private final Rect tempRect = new Rect();
@Override
protected void onDraw(Canvas canvas) {
if(customBackground != null){
canvas.getClipBounds(tempRect);
customBackground.setBounds(tempRect);
customBackground.setState(getDrawableState());
customBackground.draw(canvas);
}
super.onDraw(canvas);
}
//生成圆的的drawable
private static Drawable generateCircleDrawable(final int color){
ShapeDrawable drawable = new ShapeDrawable(new OvalShape());
drawable.setShaderFactory(new ShapeDrawable.ShaderFactory() {
@Override
public Shader resize(int width, int height) {
return new LinearGradient(0, 0, 0, 0, color, color, Shader.TileMode.REPEAT);
}
});
return drawable;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
}
CalendarPagerView 中填充DayView控件
public class CalendarPagerView extends ViewGroup implements View.OnClickListener{
//当前月的页面中的 当前月
private int currentMonth;
//存储四十二天的日期
private List dayViews = new ArrayList<>();
//用来传递日期点击事件
private CalendarViewGAC calendarView;
public CalendarPagerView(Context context,CalendarDay firstDayCurrentMonth,CalendarViewGAC calendarView) {
super(context);
currentMonth = firstDayCurrentMonth.getMonth()+1;
this.calendarView = calendarView;
buildWeekViews();
buildayViews(firstDayCurrentMonth);
}
public int getCurrentMonth(){
return currentMonth;
}
public void clearSelction(){
for(int i = 0; i < dayViews.size();i++){
dayViews.get(i).setUnselected();
}
}
//建立周日到周一的标题栏
private void buildWeekViews(){
TextView tv1 = new TextView(getContext());
tv1.setText("星期日");
TextView tv2 = new TextView(getContext());
tv2.setText("星期一");
TextView tv3 = new TextView(getContext());
tv3.setText("星期二");
TextView tv4 = new TextView(getContext());
tv4.setText("星期三");
TextView tv5 = new TextView(getContext());
tv5.setText("星期四");
TextView tv6 = new TextView(getContext());
tv6.setText("星期五");
TextView tv7 = new TextView(getContext());
tv7.setText("星期六");
addView(tv1);
addView(tv2);
addView(tv3);
addView(tv4);
addView(tv5);
addView(tv6);
addView(tv7);
}
//根据当前月份生成是四十二天的日期
private void buildayViews(CalendarDay firstDayCurrentMonth){
dayViews.clear();
Log.e("gac","firstDayCurrentMonth:"+firstDayCurrentMonth.toString());
Calendar calendar = firstDayCurrentMonth.getCalendar();
Log.e("gac","month:"+calendar.get(Calendar.MONTH));
calendar.setFirstDayOfWeek(Calendar.SUNDAY);
int delta = CalendarUtils.getDayOfWeek(calendar);
Log.e("gac","delta:"+delta);
if(delta > 0){
delta = Calendar.SUNDAY - delta;
calendar.add(Calendar.DATE,delta);
}else{
}
for(int i = 0; i < 42;i++){
DayView day = null;
if(currentMonth != (calendar.get(Calendar.MONTH)+1)){
day = new DayView(getContext(),calendar,View.INVISIBLE);
}else{
day = new DayView(getContext(),calendar,View.VISIBLE);
}
if(currentMonth != day.getDate().getMonth()){
day.setVisibility(INVISIBLE);
}
calendar.add(Calendar.DATE, 1);
day.setOnClickListener(this);
dayViews.add(day);
addView(day);
}
}
//给CalendarPagerView 应用设置好的TextSpan
//TextDecorator 得到传递的日期集合 并且判断该日期需要设置的控件内容类型
public void applayDecorator(TextDecorator decorator){
for(int i = 0; i < dayViews.size();i++){
DayView dayView = dayViews.get(i);
int mode = decorator.shouldDecorateGAC(dayView.getDay());
Log.e("gac","date:"+dayView.getDate()+" mode:"+mode);
dayView.applyTextSpan(mode,currentMonth==dayView.getDate().getMonth());
}
}
//给四十九个控件 进行排列
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int count = getChildCount();
final int parentLeft = 0;
int childTop = 0;
int childLeft = parentLeft;
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
final int width = child.getMeasuredWidth();
final int height = child.getMeasuredHeight();
child.layout(childLeft, childTop, childLeft + width, childTop + height);
childLeft += width;
//We should warp every so many children
if (i % 7 == 6) {
childLeft = parentLeft;
childTop += height;
}
}
}
//测量布局文件 对布局进行测量
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int specWidthSize = MeasureSpec.getSize(widthMeasureSpec);
final int specWidthMode = MeasureSpec.getMode(widthMeasureSpec);
final int specHeightSize = MeasureSpec.getSize(heightMeasureSpec);
final int specHeightMode = MeasureSpec.getMode(heightMeasureSpec);
if (specHeightMode == MeasureSpec.UNSPECIFIED || specWidthMode == MeasureSpec.UNSPECIFIED) {
throw new IllegalStateException("CalendarPagerView should never be left to decide it's size");
}
//The spec width should be a correct multiple
final int measureTileSize = specWidthSize / 7;
//Just use the spec sizes
setMeasuredDimension(specWidthSize, specHeightSize);
int count = getChildCount();
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
measureTileSize,
MeasureSpec.EXACTLY
);
int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
measureTileSize,
MeasureSpec.EXACTLY
);
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
}
//将日期的点击事件传给CalendarViewGAC
@Override
public void onClick(View v) {
DayView d = (DayView)v;
calendarView.onDateClicked(d);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
}
接下来实现CalendarPagerAdapter 主要为了填充CalendarPagerView 最后将adpater设置进ViewPager中去实现日历的滑动效果:
public class CalendarPagerAdapter extends PagerAdapter{
private CalendarViewGAC view;
private int currentPosition = -1;
private Context context;
CalendarPagerView pager;//当前这个月的日历页面
private CalendarViewGAC calendarViewGAC;//将CalendarViewGAC传递给CalendarPagerView 为了传递日期点击事件
private List<CalendarPagerView> pagers = new ArrayList<>();//存储缓存的日历页面的集合
private TextDecorator decorator;//设置DayView控件的扩展界面的日期集合
public CalendarPagerAdapter(Context c,CalendarViewGAC calendarViewGAC){
context = c;
this.calendarViewGAC = calendarViewGAC;
}
//通过此方法得到一个CalendarVIewPager页面 根据position位置去获得当前日期
@Override
public Object instantiateItem(ViewGroup container, int position) {
//Log.e("gac","instaniateItem........");
pager = createViewByPosition(position);//new CalendarPagerView(view.getContext());
pagers.add(pager);
container.addView(pager);
//invalidateDecorators();
invalidateDecorators();
return pager;
}
public void setDecorator(TextDecorator decorator){
this.decorator = decorator;
invalidateDecorators();
}
private void invalidateDecorators(){
Log.e("gac","size:"+pagers.size());
for(int i = 0;i < pagers.size(); i++){
Log.e("gac","pager current month:"+pagers.get(i).getCurrentMonth());
pagers.get(i).applayDecorator(decorator);
}
}
private CalendarPagerView createViewByPosition(int position){
if(currentPosition == -1 || position == 0){
currentPosition = DateAndPostion.getPosition(CalendarDay.today());
}
// Log.e("gac","create ************************View By position!!!!!!!");
// Log.e("gac","position:"+position);
CalendarDay day = DateAndPostion.getCalendarDay(position);
// Log.e("gac","day:"+day.toString());
return new CalendarPagerView(context,day,calendarViewGAC);
}
@Override
public int getCount() {
return 50000;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
// @Override
// public int getItemPosition(Object object) {
// Log.e(“gac”,”getItemPosition”);
// int index = 0;
// if(object instanceof CalendarPagerView){
// index = currentPosition;
// }
// Log.e(“gac”,”index;”+index);
// return index;
// }
//清除所有选中的日期背景 然后重新设置点击背景
public void clearSelections(){
for(int i = 0; i < pagers.size();i++){
pagers.get(i).clearSelction();
}
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
((ViewPager) container).removeView((CalendarPagerView) object);
pagers.remove((CalendarPagerView)object);
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
//这个类用于日期和ViewPager当前位置的转换类 可以将当前位置转换为日期,也可以将日期转换为当前位置
public static class DateAndPostion{
public static int getPosition(CalendarDay c){
int year = c.getYear();
int month = c.getMonth();
return (year-1)*12+month;
}
public static CalendarDay getCalendarDay(int position){
int year = position/12;
int month = position-(year*12);
return CalendarDay.from(year+1, month, 1);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
}
最后是CalendarViewGAC类,这个类继承LinearLayout集合,设置了一个标题显示 年月份的TextView,标题下面就是一个ViewPager类,这个类填充CalendarViewPagerAdapter类,最后就可以实现滑动的日历控件了.
package com.gac.calendarviewgac;
import android.content.Context;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.LinearLayout;
import android.widget.TextView;
import java.text.SimpleDateFormat;
/**
* Created by Administrator on 2016/3/22.
*/
public class CalendarViewGAC extends LinearLayout {
private CalendarPager pager;//继承ViewPager 为了装载月份页面的视图
private CalendarPagerAdapter adapter;
private OnDateSelectedListener listener;//单独日期点击事件的监听器
private TextView title;//显示年月的标题
public interface OnDateSelectedListener{
void onDateSelected(DayView view);
}
public CalendarViewGAC(Context context) {
this(context, null);
}
public CalendarViewGAC(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
- 1
- 2
- 3
- 4
- 5
public CalendarViewGAC(Context context,AttributeSet attrs,int def){
super(context, attrs, def);
setOrientation(VERTICAL);
initTopBar(context);
init(context);
//addView(new CalendarPagerView(context));
}
public void setOnDateSelectedLintener(OnDateSelectedListener lintener){
this.listener = lintener;
}
//初始化标题栏
private void initTopBar(Context context){
title = new TextView(context);
title.setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
title.setText(“title”);
title.setTextSize(20);
title.setGravity(Gravity.CENTER);
addView(title,new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
}
//初始化CalendarPager 并且填充Adapter
private void init(Context context){
pager = new CalendarPager(context);
adapter = new CalendarPagerAdapter(context,this);
pager.setAdapter(adapter);
SimpleDateFormat format = new SimpleDateFormat(“MM 月 yyyy 年”);
String date = format.format((CalendarDay.today().getDate()));
title.setText(date);
//设置当前日期为显示时间
pager.setCurrentItem(CalendarPagerAdapter.DateAndPostion.getPosition(CalendarDay.today()));
//给pager添加滑动监听器
pager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
CalendarDay day = CalendarPagerAdapter.DateAndPostion.getCalendarDay(position);
// date = DateUtils.getDateStr(day.getDate());
SimpleDateFormat format = new SimpleDateFormat("MM 月 yyyy 年");
String date = format.format(day.getDate());
title.setText(date);
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
addView(pager,new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
}
@Override
public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
super.onInitializeAccessibilityEvent(event);
event.setClassName(CalendarViewGAC.class.getName());
}
@Override
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(info);
info.setClassName(CalendarViewGAC.class.getName());
}
@Override
public boolean onTouchEvent(MotionEvent event) {
return pager.dispatchTouchEvent(event);
}
@Override
protected void onLayout(boolean changed, int left, int t, int right, int b) {
final int count = getChildCount();
Log.e("gac","count:count:"+count);
final int parentLeft = getPaddingLeft();
final int parentWidth = right - left - parentLeft - getPaddingRight();
int childTop = getPaddingTop();
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
final int width = child.getMeasuredWidth();
final int height = child.getMeasuredHeight();
int delta = (parentWidth - width) / 2;
int childLeft = parentLeft + delta;
child.layout(childLeft, childTop, childLeft + width, childTop + height);
childTop += height;
}
}
private static int clampSize(int size, int spec) {
int specMode = MeasureSpec.getMode(spec);
int specSize = MeasureSpec.getSize(spec);
switch (specMode) {
case MeasureSpec.EXACTLY: {
return specSize;
}
case MeasureSpec.AT_MOST: {
return Math.min(size, specSize);
}
case MeasureSpec.UNSPECIFIED:
default: {
return size;
}
}
}
protected void onDateClicked(DayView dayView){
adapter.clearSelections();
listener.onDateSelected(dayView);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
public void setDecorator(TextDecorator decorator){
adapter.setDecorator(decorator);
}
class CalendarPager extends ViewPager {
private boolean pagingEnabled = true;
public CalendarPager(Context context) {
super(context);
}
CalendarPager(Context context,AttributeSet attrs){
super(context,attrs);
}
public void setChildrenDrawingOrderEnabledCompat(boolean enable) {
setChildrenDrawingOrderEnabled(enable);
}
/**
* enable disable viewpager scroll
*
* @param pagingEnabled false to disable paging, true for paging (default)
*/
public void setPagingEnabled(boolean pagingEnabled) {
this.pagingEnabled = pagingEnabled;
}
/**
* @return is this viewpager allowed to page
*/
public boolean isPagingEnabled() {
return pagingEnabled;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return pagingEnabled && super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
return pagingEnabled && super.onTouchEvent(ev);
}
@Override
public boolean canScrollVertically(int direction) {
/**
* disables scrolling vertically when paging disabled, fixes scrolling
* for nested {@link android.support.v4.view.ViewPager}
*/
return pagingEnabled && super.canScrollVertically(direction);
}
@Override
public boolean canScrollHorizontally(int direction) {
/**
* disables scrolling horizontally when paging disabled, fixes scrolling
* for nested {@link android.support.v4.view.ViewPager}
*/
return pagingEnabled && super.canScrollHorizontally(direction);
}
}