1. 程式人生 > >橫向滑動的日曆控制元件的實現

橫向滑動的日曆控制元件的實現

橫向滑動的日曆控制元件實現
實現橫向滑動的日曆控制元件可以作為簽到的日曆控制元件

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



}

}

原始碼下載地址:

有什麼不明白的歡迎交流!!!!