横向滑动的日历控件的实现

横向滑动的日历控件实现
实现横向滑动的日历控件可以作为签到的日历控件

看见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;
}

}

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);
}

}

接下来实现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;
}

// @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);
    }
}

}

最后是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);

}

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);
}

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);
    }



}

}

源码下载地址:

有什么不明白的欢迎交流!!!!

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值