1. 程式人生 > >自定義可以顯示考勤狀態的日曆控制元件

自定義可以顯示考勤狀態的日曆控制元件

最近專案中要做一個可顯示考勤狀態的日曆,於是就整理了一下寫了一個demo。先看一下專案中的效果圖
圖1:

圖2:

下面來看一下demo的效果圖
不會做動圖,只能發圖片了。。。

初始化會顯示今天所在月的考勤,並選中今天(圖3):

點選按鈕“上一個月”或者點選日曆上一月的單元格(上圖中的3月26日-31日),都會重新整理到上一個月(圖4):

點選按鈕“下一個月”或者點選日曆下一月的單元格,都會重新整理到下一個月(圖5):

點選單元格,顯示選中的日期(圖6):

下面來看下demo的程式碼:

主介面MainActivity:

/**
 * Created by ysc on 2017/4/1.
 */
public class MainActivity extends Activity implements MyCalendarView.OnItemClickListener { private TextView tv; private MyCalendarView mMyCalendarView;//自定義的日曆控制元件 //ClockStates:考勤狀態 private List<ClockStates> lists; @Override protected void onCreate(Bundle savedInstanceState) { super
.onCreate(savedInstanceState); setContentView(R.layout.activity_main); tv = ((TextView) findViewById(R.id.tv)); mMyCalendarView = ((MyCalendarView) findViewById(R.id.mcv)); //點選日曆單元格的回撥 mMyCalendarView.setOnItemClickListener(this); //使用造的資料看看效果(而我們在專案開發中是聯網獲取的當月考勤資料)
initData(); } //點選上一個月 public void last(View view) { mMyCalendarView.clickLeftMonth(); tv.setText(mMyCalendarView.getYearAndmonth()); //TODO 聯網載入上個月的考勤得到一個新lists設定給日曆 lists.clear();//這裡因為沒有聯網資料,那麼我們清空掉之前造的資料,看看有資料到沒資料切換的效果 mMyCalendarView.setClockStates(lists); } //點選下一個月 public void next(View view) { mMyCalendarView.clickRightMonth(); tv.setText(mMyCalendarView.getYearAndmonth()); //TODO 聯網載入上個月的考勤得到一個新lists設定給日曆 lists.clear();//這裡因為沒有聯網資料,那麼我們清空掉之前造的資料,看一下有資料到沒資料切換的效果 mMyCalendarView.setClockStates(lists); } @Override public void OnItemClick(Date date) { String selectDate = getStringByFormat(date, "yyyy-MM-dd");//2017-03-31 int selectYear = Integer.parseInt(selectDate.split("-")[0]); int selectMonth = Integer.parseInt(selectDate.split("-")[1]); Toast.makeText(MainActivity.this, selectDate, Toast.LENGTH_SHORT).show(); if (mMyCalendarView.isLastMonth(selectYear, selectMonth)) {//點選的那一天是當天顯示月的上一個月 mMyCalendarView.clickLeftMonth(); tv.setText(mMyCalendarView.getYearAndmonth()); //TODO 實際開發中我是聯網載入上個月的考勤,得到lists,再設定給日曆 lists.clear();//這裡因為沒有聯網資料,那麼我們清空掉之前造的資料,看看效果 mMyCalendarView.setClockStates(lists); } else if (mMyCalendarView.isNextMonth(selectYear, selectMonth)) {//是下一個月 mMyCalendarView.clickRightMonth(); tv.setText(mMyCalendarView.getYearAndmonth()); //TODO 實際開發中我是聯網載入下個月的考勤,得到lists,再設定給日曆 lists.clear();//這裡因為沒有聯網資料,那麼我們清空掉之前造的資料,看看效果 mMyCalendarView.setClockStates(lists); } } /** * 描述:Date型別轉化為String型別. * * @param date the date * @param format the format * @return String String型別日期時間 */ public String getStringByFormat(Date date, String format) { SimpleDateFormat mSimpleDateFormat = new SimpleDateFormat(format); String strDate = null; try { strDate = mSimpleDateFormat.format(date); } catch (Exception e) { e.printStackTrace(); } return strDate; } //造點考勤資料 private void initData(){ lists = new ArrayList<>(); lists.add(new ClockStates("2017-04-01","1")); lists.add(new ClockStates("2017-04-02","2")); lists.add(new ClockStates("2017-04-03","3")); lists.add(new ClockStates("2017-04-04","4")); lists.add(new ClockStates("2017-04-05","1")); lists.add(new ClockStates("2017-04-06","2")); lists.add(new ClockStates("2017-04-07","3")); lists.add(new ClockStates("2017-04-08","4")); lists.add(new ClockStates("2017-04-09","1")); lists.add(new ClockStates("2017-04-10","2")); lists.add(new ClockStates("2017-04-11","3")); lists.add(new ClockStates("2017-04-12","4")); lists.add(new ClockStates("2017-04-13","1")); lists.add(new ClockStates("2017-04-14","2")); lists.add(new ClockStates("2017-04-15","4")); lists.add(new ClockStates("2017-04-16","3")); lists.add(new ClockStates("2017-04-17","1")); lists.add(new ClockStates("2017-04-18","2")); lists.add(new ClockStates("2017-04-19","3")); lists.add(new ClockStates("2017-04-20","4")); lists.add(new ClockStates("2017-04-21","2")); lists.add(new ClockStates("2017-04-22","1")); lists.add(new ClockStates("2017-04-23","1")); lists.add(new ClockStates("2017-04-24","1")); lists.add(new ClockStates("2017-04-25","1")); lists.add(new ClockStates("2017-04-26","1")); lists.add(new ClockStates("2017-04-27","1")); lists.add(new ClockStates("2017-04-28","1")); lists.add(new ClockStates("2017-04-29","1")); lists.add(new ClockStates("2017-04-30","1")); mMyCalendarView.setClockStates(lists); } }

佈局activity_main :

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.yuan.calendardemo.MainActivity">

    <com.yuan.calendardemo.Widget.MyCalendarView
        android:id="@+id/mcv"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>

    <LinearLayout
        android:layout_marginTop="5dp"
        android:layout_width="match_parent"
        android:orientation="horizontal"
        android:layout_height="wrap_content">

        <Button
            android:onClick="last"
            android:text="上一個月"
            android:layout_width="0dp"
            android:layout_weight="1"
            android:layout_height="40dp" />

        <TextView
            android:id="@+id/tv"
            android:layout_width="0dp"
            android:text="2017年4月"
            android:gravity="center"
            android:layout_weight="1"
            android:layout_height="40dp" />

        <Button
            android:onClick="next"
            android:text="下一個月"
            android:layout_width="0dp"
            android:layout_weight="1"
            android:layout_height="40dp" />

    </LinearLayout>

</LinearLayout>

考勤狀態的bean類:

/**
 * Created by ysc on 2017/4/1.
 */
public class ClockStates {
    private String date;// 日期 年-月-日
    private String dateType;// 1表示正常,2.異常,3.休息,4.曠工

    public ClockStates(String date, String dateType) {
        this.date = date;
        this.dateType = dateType;
    }

    public ClockStates() {
    }

    public String getDate() {
        return date;
    }

    public void setDate(String date) {
        this.date = date;
    }

    public String getDateType() {
        return dateType;
    }

    public void setDateType(String dateType) {
        this.dateType = dateType;
    }
}

然後重點就是自定義的日曆控制元件了MyCalendarView:

/**
 * Created by ysc on 2017/4/1.
 */
public class MyCalendarView extends View implements View.OnTouchListener {

    private Date selectedStartDate; //選中開始的月
    private Date selectedEndDate; //選中結束的月
    private Date curDate; // 當前日曆顯示的月
    private Date today; // 今天的日期文字顯示紅色

    private Date downDate; // 手指按下狀態時臨時日期
    private Date showFirstDate, showLastDate; // 日曆顯示的第一個日期和最後一個日期
    private int downIndex = -1; // 按下的格子索引
    private Calendar calendar;
    private Surface surface;

    private int[] date = new int[42]; // 日曆顯示數字
    private int curStartIndex, curEndIndex; // 當前顯示月的日曆起始的索引
    private List<ClockStates> mLists = null;// 考勤狀態集合 (年-月-日 1/2/3/4)

    //由於點選單元格我們要做相應的處理,所以這裡做一個介面回撥:
    private OnItemClickListener onItemClickListener;//日曆格子的點選監聽器

    // 給控制元件設定監聽事件
    public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
        this.onItemClickListener = onItemClickListener;
    }

    public MyCalendarView(Context context) {
        super(context);
        init();
    }

    public MyCalendarView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    //初始化資料
    private void init() {
        curDate = selectedStartDate = selectedEndDate = today = new Date();//初始化
        calendar = Calendar.getInstance();//獲取日曆
        calendar.setTime(curDate);//日曆設定當前月
        surface = new Surface();
        surface.density = getResources().getDisplayMetrics().density;//取螢幕的密度
        setBackgroundColor(surface.bgColor);
        setOnTouchListener(this);
    }

    //測量控制元件
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        surface.width = getResources().getDisplayMetrics().widthPixels;//整個控制元件的寬度
        surface.height = (int) (getResources().getDisplayMetrics().heightPixels * 1 / 2);//控制元件的高度取螢幕高度的一半
        widthMeasureSpec = MeasureSpec.makeMeasureSpec(surface.width, MeasureSpec.EXACTLY);
        heightMeasureSpec = MeasureSpec.makeMeasureSpec(surface.height, MeasureSpec.EXACTLY);
        setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        if (changed) {
            surface.init();
        }
        super.onLayout(changed, left, top, right, bottom);
    }

    //畫圖
    @Override
    protected void onDraw(Canvas canvas) {
        // 畫 星期(日 一 二……)
        float weekTextY = surface.monthHeight + surface.weekHeight * 3 / 5f;
        for (int i = 0; i < surface.weekText.length; i++) {
            float weekTextX = i * surface.cellWidth
                    + (surface.cellWidth - surface.weekPaint.measureText(surface.weekText[i])) / 2f;
            canvas.drawText(surface.weekText[i], weekTextX, weekTextY, surface.weekPaint);
        }
        // 計算日期
        calculateDate();

        // 按下狀態,選擇狀態背景色
        drawDownOrSelectedBg(canvas);

        //單元格內的文字繪製
        int todayIndex = -1;
        calendar.setTime(curDate);
        String curYearAndMonth = calendar.get(Calendar.YEAR) + "" + calendar.get(Calendar.MONTH);//當前年月
        calendar.setTime(today);
        String todayYearAndMonth = calendar.get(Calendar.YEAR) + "" + calendar.get(Calendar.MONTH);//今天所在的年月
        if (curYearAndMonth.equals(todayYearAndMonth)) {
            int todayNumber = calendar.get(Calendar.DAY_OF_MONTH);//獲取 今天的日期(日)
            todayIndex = curStartIndex + todayNumber - 1;//今天日期的索引
        }

        int stateIndex = 0;//記錄當月 有狀態的索引
        String signText = "";//考勤的標記
        int signColor = 0;//考勤標記的顏色
        for (int i = 0; i < 42; i++) {
            int tColor = Color.parseColor("#000000");// 文字顏色
            if (todayIndex != -1 && i == todayIndex) {// 設定選中當天的背景
                if (todayIndex == downIndex || downIndex == -1) {
                    tColor = Color.parseColor("#ffffff");//今天的日期 剛好是你按下時的日期
                }
            }

            //把日曆當前月之外的日期設定為灰暗色
            if (isLastMonth(i)) {// 上一個月的日期顏色
                tColor = surface.borderColor;
            } else if (isNextMonth(i)) {// 下一個月的日期顏色
                tColor = surface.borderColor;
            }

            //索引不是上一個月的,從本月第一天開始
            if (!isLastMonth(i) && !isNextMonth(i) && mLists != null && stateIndex < mLists.size()) {
                switch (Integer.parseInt(mLists.get(stateIndex).getDateType())) {
                    case 1://正常
                        signText = surface.signText[1];
                        signColor = surface.zcColor;
                        break;
                    case 2://異常
                        signText = surface.signText[2];
                        signColor = surface.ycColor;
                        break;
                    case 3://休息
                        signText = surface.signText[3];
                        signColor = surface.xxColor;
                        break;
                    case 4://曠工
                        signText = surface.signText[4];
                        signColor = surface.kgColor;
                        break;
                }
                //畫 考勤狀態標記
                drawSignText(canvas, i, signText, signColor);
                stateIndex++;
            }
            //畫日期
            drawCellText(canvas, i, date[i] + "", tColor);
        }
        // 畫邊框
        canvas.drawPath(surface.boxPath, surface.borderPaint);
        super.onDraw(canvas);
    }

    //計算日期
    private void calculateDate() {
        calendar.setTime(curDate);
        calendar.set(Calendar.DAY_OF_MONTH, 1);
        int dayInWeek = calendar.get(Calendar.DAY_OF_WEEK);
        int monthStart = dayInWeek;
        if (monthStart == 1) {
            monthStart = 8;
        }
        monthStart -= 1; // 以日為開頭-1,以星期一為開頭-2
        curStartIndex = monthStart;
        date[monthStart] = 1;
        // last month
        if (monthStart > 0) {
            calendar.set(Calendar.DAY_OF_MONTH, 0);
            int dayInmonth = calendar.get(Calendar.DAY_OF_MONTH);
            for (int i = monthStart - 1; i >= 0; i--) {
                date[i] = dayInmonth;
                dayInmonth--;
            }
            calendar.set(Calendar.DAY_OF_MONTH, date[0]);
        }
        showFirstDate = calendar.getTime();
        // this month
        calendar.setTime(curDate);
        calendar.add(Calendar.MONTH, 1);
        calendar.set(Calendar.DAY_OF_MONTH, 0);
        // Log.d(TAG, "m:" + calendar.get(Calendar.MONTH) + " d:" +
        // calendar.get(Calendar.DAY_OF_MONTH));
        int monthDay = calendar.get(Calendar.DAY_OF_MONTH);
        for (int i = 1; i < monthDay; i++) {
            date[monthStart + i] = i + 1;
        }
        curEndIndex = monthStart + monthDay;
        // next month
        for (int i = monthStart + monthDay; i < 42; i++) {
            date[i] = i - (monthStart + monthDay) + 1;
        }
        if (curEndIndex < 42) {
            // 顯示了下一月的
            calendar.add(Calendar.DAY_OF_MONTH, 1);
        }
        calendar.set(Calendar.DAY_OF_MONTH, date[41]);
        showLastDate = calendar.getTime();
    }

    /**
     * 畫 單元格內的文字
     *
     * @param canvas
     * @param index
     * @param text
     */
    private void drawCellText(Canvas canvas, int index, String text, int color) {
        int x = getXByIndex(index);//列數
        int y = getYByIndex(index);//行數

        surface.datePaint.setColor(color);
        //月高+星期高+(行數-1)*單元格高+單元格高*0.75f
        float cellY = surface.monthHeight + surface.weekHeight + (y - 1) * surface.cellHeight
                + surface.cellHeight * 1 / 2f;
        //單元格寬 * (列數-1)+(單元格寬—文字寬)/2f
        float cellX = (surface.cellWidth * (x - 1)) + (surface.cellWidth - surface.datePaint.measureText(text)) / 2f;
        canvas.drawText(text, cellX, cellY, surface.datePaint);


    }

    /**
     * 畫 單元格內的標記(正常  異常  休息……)
     *
     * @param canvas
     * @param index
     * @param text
     * @param color
     */
    private void drawSignText(Canvas canvas, int index, String text, int color) {
        int x = getXByIndex(index);//列數
        int y = getYByIndex(index);//行數
        surface.signPaint.setColor(color);
        //月高+星期高+(行數-1)*單元格高+單元格高*0.75f
        float cellY = surface.monthHeight + surface.weekHeight + (y - 1) * surface.cellHeight
                + surface.cellHeight * 1 / 2f + surface.cellHeight * 1 / 3;
        //單元格寬 * (列數-1)+(單元格寬—文字寬)/2f
        float cellX = (surface.cellWidth * (x - 1)) + (surface.cellWidth - surface.signPaint.measureText(text)) / 2f;
        canvas.drawText(text, cellX, cellY, surface.signPaint);
    }

    /**
     * 畫格子的背景
     *
     * @param canvas
     * @param index
     * @param color
     */
    private void drawCellBg(Canvas canvas, int index, int color) {
        int x = getXByIndex(index);
        int y = getYByIndex(index);
        surface.cellBgPaint.setColor(color);
        float left = surface.cellWidth * (x - 1) + surface.borderWidth - 1;
        float top = surface.monthHeight + surface.weekHeight + (y - 1) * surface.cellHeight + surface.borderWidth - 1;
        canvas.drawRect(left, top, left + surface.cellWidth - surface.borderWidth + 1,
                top + surface.cellHeight - surface.borderWidth + 1, surface.cellBgPaint);
    }

    /**
     * 把 一個月的考勤狀態設定給日曆
     *
     * @param lists
     */
    public void setClockStates(List<ClockStates> lists) {
        this.mLists = lists;
        invalidate();
    }

    //畫 按下或者選擇日期時的背景
    private void drawDownOrSelectedBg(Canvas canvas) {
        // 按下未擡起的背景
        if (downDate != null) {
            drawCellBg(canvas, downIndex, surface.cellDownColor);
        }
        // 選擇的背景
        if (!selectedEndDate.before(showFirstDate) && !selectedStartDate.after(showLastDate)) {
            int[] section = new int[]{-1, -1};
            calendar.setTime(curDate);
            calendar.add(Calendar.MONTH, -1);
            findSelectedIndex(0, curStartIndex, calendar, section);
            if (section[1] == -1) {
                calendar.setTime(curDate);
                findSelectedIndex(curStartIndex, curEndIndex, calendar, section);
            }
            if (section[1] == -1) {
                calendar.setTime(curDate);
                calendar.add(Calendar.MONTH, 1);
                findSelectedIndex(curEndIndex, 42, calendar, section);
            }
            if (section[0] == -1) {
                section[0] = 0;
            }
            if (section[1] == -1) {
                section[1] = 41;
            }
            for (int i = section[0]; i <= section[1]; i++) {
                drawCellBg(canvas, i, surface.cellSelectedColor);
            }

        }
    }

    //查到 選中單元格的索引
    private void findSelectedIndex(int startIndex, int endIndex, Calendar calendar, int[] section) {
        for (int i = startIndex; i < endIndex; i++) {
            calendar.set(Calendar.DAY_OF_MONTH, date[i]);
            Date temp = calendar.getTime();
            if (temp.compareTo(selectedStartDate) == 0) {
                section[0] = i;
            }
            if (temp.compareTo(selectedEndDate) == 0) {
                section[1] = i;
                return;
            }
        }
    }

    //獲取 選中開始的月
    public Date getSelectedStartDate() {
        return selectedStartDate;
    }

    //獲取 選中結束的月
    public Date getSelectedEndDate() {
        return selectedEndDate;
    }

    //當前點選的索引是不是上一個月的
    private boolean isLastMonth(int i) {
        if (i < curStartIndex) {
            return true;
        }
        return false;
    }

    //當前點選的索引是不是下一個月的
    private boolean isNextMonth(int i) {
        if (i >= curEndIndex) {
            return true;
        }
        return false;
    }

    //判斷當前點選的格子(所在的年月)是不是上個月的
    public boolean isLastMonth(int year, int month) {
        calendar.setTime(curDate);
        int showYear = calendar.get(Calendar.YEAR);
        int showMonth = calendar.get(Calendar.MONTH) + 1;
        if (year == showYear && month == showMonth - 1) {
            return true;
        }
        if (year == showYear - 1 && year == 12) {
            return true;
        }

        return false;
    }

    //判斷當前點選的格子(所在的年月)是不是下個月的
    public boolean isNextMonth(int year, int month) {
        calendar.setTime(curDate);
        int showYear = calendar.get(Calendar.YEAR);
        int showMonth = calendar.get(Calendar.MONTH) + 1;
        if (year == showYear && month == showMonth + 1) {
            return true;
        }
        if (year == showYear + 1 && year == 1) {
            return true;
        }

        return false;
    }

    //根據單元格的索引獲取 所在的列數
    private int getXByIndex(int i) {
        return i % 7 + 1; // 1 2 3 4 5 6 7
    }

    //根據單元格所在的索引獲取 所在的行數
    private int getYByIndex(int i) {
        return i / 7 + 1; // 1 2 3 4 5 6
    }

    /**
     * 獲得當前應該顯示的年月
     *
     * @return 格式:xxxx年x月
     */
    public String getYearAndmonth() {
        calendar.setTime(curDate);
        int year = calendar.get(Calendar.YEAR);
        int month = calendar.get(Calendar.MONTH) + 1;
        return year + "年" + month + "月";
    }

    /**
     * 獲得當前應該顯示的年月
     *
     * @return 格式:xxxx,x
     */
    public String getYearAndmonths() {
        calendar.setTime(curDate);
        int year = calendar.get(Calendar.YEAR);
        int month = calendar.get(Calendar.MONTH) + 1;
        StringBuffer str = new StringBuffer();
        str.append(year);
        str.append(",");
        str.append(month);
        return str.toString();
    }

    // 點選上一月
    public String clickLeftMonth() {
        calendar.setTime(curDate);
        calendar.add(Calendar.MONTH, -1);
        curDate = calendar.getTime();
        invalidate();
        return getYearAndmonth();
    }

    // 點選下一月
    public String clickRightMonth() {
        calendar.setTime(curDate);
        calendar.add(Calendar.MONTH, 1);
        curDate = calendar.getTime();
        invalidate();
        return getYearAndmonth();
    }

    /**
     * 跳轉到指定的年月
     *
     * @param year  指定的年(格式:2017)
     * @param month 指定的月(格式:1,2……11,……)
     * @return 當前的月份  格式:2017年3月
     */
    public String clickSpecifiedMonth(int year, int month) {
        calendar.setTime(curDate);
        calendar.get(Calendar.YEAR);

        int addMonth = 0;//要跳轉多少個月
        if (year == calendar.get(Calendar.YEAR)) {
            addMonth = month - calendar.get(Calendar.MONTH) - 1;
            calendar.add(Calendar.MONTH, addMonth);
        } else if (year < calendar.get(Calendar.YEAR)) {
            addMonth = (calendar.get(Calendar.YEAR) - year) * 12 + calendar.get(Calendar.MONTH) + 1 - month;
            calendar.add(Calendar.MONTH, -addMonth);
        } else {
            addMonth = (year - calendar.get(Calendar.YEAR)) * 12 + month - calendar.get(Calendar.MONTH) - 1;
            calendar.add(Calendar.MONTH, addMonth);
        }

        curDate = calendar.getTime();
        invalidate();
        return getYearAndmonth();
    }

    //設定選中的單元格
    private void setSelectedDateByCoor(float x, float y) {
        //按下日期單元格(不包括星期)
        if (y > surface.monthHeight + surface.weekHeight) {
            int m = (int) (Math.floor(x / surface.cellWidth) + 1);//單元格的列數
            //單元格的行數(不包括星期)
            int n = (int) (Math
                    .floor((y - (surface.monthHeight + surface.weekHeight)) / Float.valueOf(surface.cellHeight)) + 1);
            downIndex = (n - 1) * 7 + m - 1;//計算索引 從0開始記起
            calendar.setTime(curDate);
            if (isLastMonth(downIndex)) {
                calendar.add(Calendar.MONTH, -1);
            } else if (isNextMonth(downIndex)) {
                calendar.add(Calendar.MONTH, 1);
            }
            calendar.set(Calendar.DAY_OF_MONTH, date[downIndex]);
            downDate = calendar.getTime();
        }
        invalidate();
    }

    //觸控單元格的事件處理
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                setSelectedDateByCoor(event.getX(), event.getY());
                break;
            case MotionEvent.ACTION_UP:
                if (downDate != null) {
                    selectedStartDate = selectedEndDate = downDate;//選中開始的月=選中結束的月 = 按下時的月
                    // 響應監聽事件
                    onItemClickListener.OnItemClick(selectedStartDate);
                    downDate = null;
                    invalidate();
                }
                break;
        }
        return true;
    }


    // 監聽介面
    public interface OnItemClickListener {
        void OnItemClick(Date date);
    }

    /**
     * 外觀:
     * 1. 佈局尺寸 2. 文字顏色,大小 3. 當前日期的顏色,選擇的日期顏色
     */
    private class Surface {

        public float density; //密度(設定邊框寬度使用)
        public int width; // 整個控制元件的寬度
        public int height; // 整個控制元件的高度
        public float monthHeight; // 顯示月的高度?

        public float weekHeight; // 顯示星期的高度
        public float cellWidth; // 日期方框寬度
        public float cellHeight; // 日期單元格高度
        public float borderWidth;//邊框的寬度


        public int bgColor = Color.parseColor("#FFFFFF");// 背景顏色
        // 原來的 日期 天數 的文字字型顏色 private int textColor = Color.BLACK;
        private int textColor = Color.parseColor("#999999");// Color.rgb(90, 90, 90);文字顏色
        private int btnColor = Color.parseColor("#666666");//

        private int borderColor = Color.parseColor("#CCCCCC");// 背景邊框線

        public int cellDownColor = Color.parseColor("#FF9900");//格子按下時的顏色
        public int cellSelectedColor = Color.parseColor("#FF7700");// 格子 選中背景

        //畫筆
        public Paint borderPaint;//邊框畫筆
        public Paint monthPaint;//畫月  的畫筆
        public Paint weekPaint;//畫 星期的畫筆
        public Paint datePaint;//畫日期(日)的畫筆
        public Paint monthChangeBtnPaint;//月份改變的畫筆
        public Paint cellBgPaint;//格子背景的畫筆
        public Path boxPath; // 邊框路徑
        public Paint signPaint;//標記畫筆

        //資料
        public String[] weekText = {"日", "一", "二", "三", "四", "五", "六"};
        public String[] monthText = {"一", "二", "三", "四", "五", "六", "七", "八", "九", "十", "十一", "十二"};
        public String[] signText = {"", "√", "異常", "休", "曠"};

        /**
         * 獲取不同狀態時的顏色值(用在日期下面標記的顏色)
         * 1.正常,2.異常,3.休息,4.曠工
         */
        public int zcColor = getResources().getColor(R.color.zc_clock);// 正常
        public int ycColor = getResources().getColor(R.color.yc_clock);// 異常
        public int xxColor = getResources().getColor(R.color.xx_clock);// 休息
        public int kgColor = getResources().getColor(R.color.kg_clock);// 曠

        //外觀初始化
        public void init() {
            float temp = height / 7f;

            monthHeight = 0;
            weekHeight = (float) ((temp + temp * 0.3f) * 0.7);//星期的高度
            cellHeight = (height - monthHeight - weekHeight) / 6f;//日期(日)單元格的高度= 總-月-星期
            cellWidth = width / 7f;//單元格的寬度

            //畫邊框
            borderPaint = new Paint();
            borderPaint.setColor(borderColor);//設定邊框的顏色
            borderPaint.setStyle(Paint.Style.STROKE);//設定畫筆的樣式:輪廓線
            borderWidth = (float) (0.5 * density);//邊框的寬度
            borderWidth = borderWidth < 1 ? 1 : borderWidth;//邊框的寬度跟螢幕適配
            borderPaint.setStrokeWidth(borderWidth);//設定邊框的寬度

            //畫月份
            monthPaint = new Paint();
            monthPaint.setColor(textColor);//月份文字的顏色
            monthPaint.setAntiAlias(true);//抗鋸齒
            float textSize = cellHeight * 0.4f;//月 文字的大小(為單元格的0.4)
            monthPaint.setTextSize(textSize);//設定月 文字的大小

            //畫 星期
            weekPaint = new Paint();
            weekPaint.setColor(textColor);//星期 的文字顏色
            weekPaint.setAntiAlias(true);
            float weekTextSize = weekHeight * 0.4f;
            weekPaint.setTextSize(weekTextSize);//設定星期 文字的大小?

            //畫 日期(日)
            datePaint = new Paint();
            datePaint.setColor(textColor);//畫筆顏色
            datePaint.setAntiAlias(true);//抗鋸齒
            float cellTextSize = cellHeight * 0.4f;//單元格內文字的大小
            datePaint.setTextSize(cellTextSize);//設定文字大小

            //畫 標記(“