Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android 自定義控件之可多選課程日歷CalendarView

Android 自定義控件之可多選課程日歷CalendarView

編輯:關於Android編程

效果圖

這裡寫圖片描述

開發環境

IDE版本:AndroidStudio2.0
物理機版本:Win7旗艦版(64位)

前言

最近的項目中用到了一個課程選擇的日歷View,於是在網上搜了搜自定義日歷View,發現基本上都是單選的,不能夠滿足項目中的需求。於是自己重新造了個輪子,寫了個可以被多選的自定義日歷View。最後面會給出GitHub地址。

代碼實現

package widget;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.MotionEvent;
import android.view.View;

import com.arisaid.calendarview.R;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;

/**
 * Created by zhouyou on 2016/7/25.
 * Class desc:
 *
 * 自定義日歷View,可多選
 */
public class CalendarView extends View {

    // 列的數量
    private static final int NUM_COLUMNS    =   7;
    // 行的數量
    private static final int NUM_ROWS       =   6;

    /**
     * 可選日期數據
     */
    private List mOptionalDates;

    /**
     * 以選日期數據
     */
    private List mSelectedDates = new ArrayList<>();

    // 背景顏色
    private int mBgColor = Color.parseColor("#F7F7F7");
    // 天數默認顏色
    private int mDayNormalColor = Color.parseColor("#0070F8");
    // 天數不可選顏色
    private int mDayNotOptColor = Color.parseColor("#CBCBCB");
    // 天數選擇後顏色
    private int mDayPressedColor = Color.WHITE;
    // 天數字體大小
    private int mDayTextSize = 14;
    // 是否可以被點擊狀態
    private boolean mClickable = true;

    private DisplayMetrics mMetrics;
    private Paint mPaint;
    private int mCurYear;
    private int mCurMonth;
    private int mCurDate;

    private int mSelYear;
    private int mSelMonth;
    private int mSelDate;
    private int mColumnSize;
    private int mRowSize;
    private int[][] mDays;

    // 當月一共有多少天
    private int mMonthDays;
    // 當月第一天位於周幾
    private int mWeekNumber;
    // 已選中背景Bitmap
    private Bitmap mBgOptBitmap;
    // 未選中背景Bitmap
    private Bitmap mBgNotOptBitmap;

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

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

    public CalendarView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        // 獲取手機屏幕參數
        mMetrics = getResources().getDisplayMetrics();
        // 創建畫筆
        mPaint = new Paint();
        // 獲取當前日期
        Calendar calendar = Calendar.getInstance();
        mCurYear    =   calendar.get(Calendar.YEAR);
        mCurMonth   =   calendar.get(Calendar.MONTH);
        mCurDate    =   calendar.get(Calendar.DATE);
        setSelYTD(mCurYear, mCurMonth, mCurDate);

        // 獲取背景Bitmap
        mBgOptBitmap    = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_bg_course_optional);
        mBgNotOptBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_bg_course_not_optional);
    }

    @Override
    public void invalidate() {
        // 避免程序過度繪制
        if(hasWindowFocus()) super.invalidate();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        initSize();

        // 繪制背景
        mPaint.setColor(mBgColor);
        canvas.drawRect(0, 0, canvas.getWidth(), canvas.getHeight(), mPaint);

        mDays = new int[6][7];
        // 設置繪制字體大小
        mPaint.setTextSize(mDayTextSize * mMetrics.scaledDensity);
        // 設置繪制字體顏色

        String dayStr;
        // 獲取當月一共有多少天
        mMonthDays = DateUtils.getMonthDays(mSelYear, mSelMonth);
        // 獲取當月第一天位於周幾
        mWeekNumber = DateUtils.getFirstDayWeek(mSelYear, mSelMonth);

        for(int day = 0; day < mMonthDays; day++){
            dayStr = String.valueOf(day + 1);
            int column  =  (day + mWeekNumber - 1) % 7;
            int row     =  (day + mWeekNumber - 1) / 7;
            mDays[row][column] = day + 1;
            int startX = (int) (mColumnSize * column + (mColumnSize - mPaint.measureText(dayStr)) / 2);
            int startY = (int) (mRowSize * row + mRowSize / 2 - (mPaint.ascent() + mPaint.descent()) / 2);

            // 判斷當前天數是否可選
            if(mOptionalDates.contains(getSelData(mSelYear, mSelMonth, mDays[row][column]))){
                // 可選,繼續判斷是否是點擊過的
                if(!mSelectedDates.contains(getSelData(mSelYear, mSelMonth, mDays[row][column]))){
                    // 沒有點擊過,繪制默認背景
                    canvas.drawBitmap(mBgNotOptBitmap, startX - 22, startY - 55, mPaint);
                    mPaint.setColor(mDayNormalColor);
                }else{
                    // 點擊過,繪制點擊過的背景
                    canvas.drawBitmap(mBgOptBitmap, startX - 22, startY - 55, mPaint);
                    mPaint.setColor(mDayPressedColor);
                }
                // 繪制天數
                canvas.drawText(dayStr, startX, startY - 10, mPaint);
            }else{
                mPaint.setColor(mDayNotOptColor);
                canvas.drawText(dayStr, startX, startY, mPaint);
            }
        }
    }

    private int downX = 0,downY = 0;

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int eventCode = event.getAction();
        switch(eventCode){
            case MotionEvent.ACTION_DOWN:
                downX = (int) event.getX();
                downY = (int) event.getY();
                break;
            case MotionEvent.ACTION_MOVE:
                break;
            case MotionEvent.ACTION_UP:
                if(!mClickable) return true;

                int upX = (int) event.getX();
                int upY = (int) event.getY();
                if(Math.abs(upX - downX) < 10 && Math.abs(upY - downY) < 10){
                    performClick();
                    onClick((upX + downX) / 2, (upY + downY) / 2);
                }
                break;
        }
        return true;
    }

    /**
     * 點擊事件
     */
    private void onClick(int x, int y){
        int row = y / mRowSize;
        int column = x / mColumnSize;
        setSelYTD(mSelYear, mSelMonth, mDays[row][column]);

        // 判斷是否點擊過
        boolean isSelected = mSelectedDates.contains(getSelData(mSelYear, mSelMonth, mSelDate));
        if(isSelected){
            mSelectedDates.remove(getSelData(mSelYear, mSelMonth, mSelDate));
        }else{
            mSelectedDates.add(getSelData(mSelYear, mSelMonth, mSelDate));
        }

        invalidate();
        if(mListener != null){
            // 執行回調
            mListener.onClickDateListener(mSelYear, (mSelMonth + 1), mSelDate);
        }
    }

    /**
     * 初始化列寬和高
     */
    private void initSize() {
        // 初始化每列的大小
        mColumnSize = getWidth() / NUM_COLUMNS;
        // 初始化每行的大小
        mRowSize = getHeight() / NUM_ROWS;
    }

    /**
     * 設置可選擇日期
     * @param dates 日期數據
     */
    public void setOptionalDate(List dates){
        this.mOptionalDates = dates;
    }

    /**
     * 設置年月日
     * @param year  年
     * @param month 月
     * @param date  日
     */
    public void setSelYTD(int year, int month, int date){
        this.mSelYear   =   year;
        this.mSelMonth  =   month;
        this.mSelDate   =   date;
    }

    /**
     * 設置上一個月日歷
     */
    public void setLastMonth(){
        int year    =   mSelYear;
        int month   =   mSelMonth;
        int day     =   mSelDate;
        // 如果是1月份,則變成12月份
        if(month == 0){
            year = mSelYear-1;
            month = 11;
        }else if(DateUtils.getMonthDays(year, month) == day){
            // 如果當前日期為該月最後一點,當向前推的時候,就需要改變選中的日期
            month = month-1;
            day = DateUtils.getMonthDays(year, month);
        }else{
            month = month-1;
        }
        setSelYTD(year,month,day);
        invalidate();
    }

    /**
     * 設置下一個日歷
     */
    public void setNextMonth(){
        int year    =   mSelYear;
        int month   =   mSelMonth;
        int day     =   mSelDate;
        // 如果是12月份,則變成1月份
        if(month == 11){
            year = mSelYear+1;
            month = 0;
        }else if(DateUtils.getMonthDays(year, month) == day){
            // 如果當前日期為該月最後一點,當向前推的時候,就需要改變選中的日期
            month = month + 1;
            day = DateUtils.getMonthDays(year, month);
        }else{
            month = month + 1;
        }
        setSelYTD(year,month,day);
        invalidate();
    }

    /**
     * 獲取當前展示的年和月份
     * @return 格式:2016-06
     */
    public String getDate(){
        String data;
        if((mSelMonth + 1) < 10){
            data = mSelYear + "-0" + (mSelMonth + 1);
        }else{
            data = mSelYear + "-" + (mSelMonth + 1);
        }
        return data;
    }

    /**
     * 獲取當前展示的日期
     * @return 格式:20160606
     */
    private String getSelData(int year, int month, int date){
        String monty, day;
        month = (month + 1);

        // 判斷月份是否有非0情況
        if((month) < 10) {
            monty = "0" + month;
        }else{
            monty = String.valueOf(month);
        }

        // 判斷天數是否有非0情況
        if((date) < 10){
            day = "0" + (date);
        }else{
            day = String.valueOf(date);
        }
        return year + monty + day;
    }

    /**
     * 獲取已選日期數據
     */
    public List getSelectedDates(){
        return mSelectedDates;
    }

    /**
     * 設置已選日期數據
     */
    public void setSelectedDates(List dates){
        this.mSelectedDates = dates;
    }

    /**
     * 設置日歷是否可以點擊
     */
    @Override
    public void setClickable(boolean clickable) {
        this.mClickable = clickable;
    }

    private OnClickListener mListener;

    public interface OnClickListener{
        void onClickDateListener(int year, int month, int day);
    }

    /**
     * 設置點擊回調
     */
    public void setOnClickDate(OnClickListener listener){
        this.mListener = listener;
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        recyclerBitmap(mBgOptBitmap);
        recyclerBitmap(mBgNotOptBitmap);
    }

    /**
     * 釋放Bitmap資源
     */
    private void recyclerBitmap(Bitmap bitmap) {
        if(bitmap != null && !bitmap.isRecycled()){
            bitmap.recycle();
        }
    }
}

使用步驟

1,初始化自定義日歷View:

CalendarView mCalendarView = (CalendarView) findViewById(R.id.calendarView);

2,初始化可以被選擇的天數數據:

List mDatas = new ArrayList<>();
mDatas.add("20160801");
mDatas.add("20160802");
mDatas.add("20160803");
mDatas.add("20160816");
mDatas.add("20160817");
mDatas.add("20160826");
mDatas.add("20160910");
mDatas.add("20160911");
mDatas.add("20160912");

3,設置給自定義日歷View:

// 設置可選日期
mCalendarView.setOptionalDate(mDatas);

設置點擊監聽

mCalendarView.setOnClickDate(new CalendarView.OnClickListener() {
    @Override
    public void onClickDateListener(int year, int month, int day) {
        Toast.makeText(getApplication(), year + "年" + month + "月" + day + "天", Toast.LENGTH_SHORT).show();

        // 獲取已選擇日期
        List dates = mCalendarView.getSelectedDates();
        for (String date : dates) {
            Log.e("test", "date: " + date);
        }

    }
});

如果只需要進行數據展示,而不需要點擊,可以設置:

// 設置已選日期
mCalendarView.setSelectedDates(mDatas);
// 設置不可以被點擊
mCalendarView.setClickable(false);

源碼下載

GitHub地址:https://github.com/Airsaid/CalendarView 歡迎star~!
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved