Android自定義控件實(shí)現(xiàn)可多選課程日歷CalendarView
可多選課程日歷CalendarView的效果圖

開發(fā)環(huán)境
IDE版本:AndroidStudio2.0
物理機(jī)版本:Win7旗艦版(64位)
前言
最近的項(xiàng)目中用到了一個(gè)課程選擇的日歷View,于是在網(wǎng)上搜了搜自定義日歷View,發(fā)現(xiàn)基本上都是單選的,不能夠滿足項(xiàng)目中的需求。于是自己重新造了個(gè)輪子,寫了個(gè)可以被多選的自定義日歷View。最后面會(huì)給出GitHub地址。
代碼實(shí)現(xiàn)
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 {
// 列的數(shù)量
private static final int NUM_COLUMNS = 7;
// 行的數(shù)量
private static final int NUM_ROWS = 6;
/**
* 可選日期數(shù)據(jù)
*/
private List<String> mOptionalDates;
/**
* 以選日期數(shù)據(jù)
*/
private List<String> mSelectedDates = new ArrayList<>();
// 背景顏色
private int mBgColor = Color.parseColor("#F7F7F7");
// 天數(shù)默認(rèn)顏色
private int mDayNormalColor = Color.parseColor("#0070F8");
// 天數(shù)不可選顏色
private int mDayNotOptColor = Color.parseColor("#CBCBCB");
// 天數(shù)選擇后顏色
private int mDayPressedColor = Color.WHITE;
// 天數(shù)字體大小
private int mDayTextSize = 14;
// 是否可以被點(diǎn)擊狀態(tài)
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;
// 當(dāng)月一共有多少天
private int mMonthDays;
// 當(dāng)月第一天位于周幾
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() {
// 獲取手機(jī)屏幕參數(shù)
mMetrics = getResources().getDisplayMetrics();
// 創(chuàng)建畫筆
mPaint = new Paint();
// 獲取當(dāng)前日期
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];
// 設(shè)置繪制字體大小
mPaint.setTextSize(mDayTextSize * mMetrics.scaledDensity);
// 設(shè)置繪制字體顏色
String dayStr;
// 獲取當(dāng)月一共有多少天
mMonthDays = DateUtils.getMonthDays(mSelYear, mSelMonth);
// 獲取當(dāng)月第一天位于周幾
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);
// 判斷當(dāng)前天數(shù)是否可選
if(mOptionalDates.contains(getSelData(mSelYear, mSelMonth, mDays[row][column]))){
// 可選,繼續(xù)判斷是否是點(diǎn)擊過的
if(!mSelectedDates.contains(getSelData(mSelYear, mSelMonth, mDays[row][column]))){
// 沒有點(diǎn)擊過,繪制默認(rèn)背景
canvas.drawBitmap(mBgNotOptBitmap, startX - 22, startY - 55, mPaint);
mPaint.setColor(mDayNormalColor);
}else{
// 點(diǎn)擊過,繪制點(diǎn)擊過的背景
canvas.drawBitmap(mBgOptBitmap, startX - 22, startY - 55, mPaint);
mPaint.setColor(mDayPressedColor);
}
// 繪制天數(shù)
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;
}
/**
* 點(diǎn)擊事件
*/
private void onClick(int x, int y){
int row = y / mRowSize;
int column = x / mColumnSize;
setSelYTD(mSelYear, mSelMonth, mDays[row][column]);
// 判斷是否點(diǎn)擊過
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){
// 執(zhí)行回調(diào)
mListener.onClickDateListener(mSelYear, (mSelMonth + 1), mSelDate);
}
}
/**
* 初始化列寬和高
*/
private void initSize() {
// 初始化每列的大小
mColumnSize = getWidth() / NUM_COLUMNS;
// 初始化每行的大小
mRowSize = getHeight() / NUM_ROWS;
}
/**
* 設(shè)置可選擇日期
* @param dates 日期數(shù)據(jù)
*/
public void setOptionalDate(List<String> dates){
this.mOptionalDates = dates;
}
/**
* 設(shè)置年月日
* @param year 年
* @param month 月
* @param date 日
*/
public void setSelYTD(int year, int month, int date){
this.mSelYear = year;
this.mSelMonth = month;
this.mSelDate = date;
}
/**
* 設(shè)置上一個(gè)月日歷
*/
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){
// 如果當(dāng)前日期為該月最后一點(diǎn),當(dāng)向前推的時(shí)候,就需要改變選中的日期
month = month-1;
day = DateUtils.getMonthDays(year, month);
}else{
month = month-1;
}
setSelYTD(year,month,day);
invalidate();
}
/**
* 設(shè)置下一個(gè)日歷
*/
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){
// 如果當(dāng)前日期為該月最后一點(diǎn),當(dāng)向前推的時(shí)候,就需要改變選中的日期
month = month + 1;
day = DateUtils.getMonthDays(year, month);
}else{
month = month + 1;
}
setSelYTD(year,month,day);
invalidate();
}
/**
* 獲取當(dāng)前展示的年和月份
* @return 格式:2016-06
*/
public String getDate(){
String data;
if((mSelMonth + 1) < 10){
data = mSelYear + "-0" + (mSelMonth + 1);
}else{
data = mSelYear + "-" + (mSelMonth + 1);
}
return data;
}
/**
* 獲取當(dāng)前展示的日期
* @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);
}
// 判斷天數(shù)是否有非0情況
if((date) < 10){
day = "0" + (date);
}else{
day = String.valueOf(date);
}
return year + monty + day;
}
/**
* 獲取已選日期數(shù)據(jù)
*/
public List<String> getSelectedDates(){
return mSelectedDates;
}
/**
* 設(shè)置已選日期數(shù)據(jù)
*/
public void setSelectedDates(List<String> dates){
this.mSelectedDates = dates;
}
/**
* 設(shè)置日歷是否可以點(diǎn)擊
*/
@Override
public void setClickable(boolean clickable) {
this.mClickable = clickable;
}
private OnClickListener mListener;
public interface OnClickListener{
void onClickDateListener(int year, int month, int day);
}
/**
* 設(shè)置點(diǎn)擊回調(diào)
*/
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、初始化可以被選擇的天數(shù)數(shù)據(jù):
List<String> 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、設(shè)置給自定義日歷View:
// 設(shè)置可選日期 mCalendarView.setOptionalDate(mDatas);
設(shè)置點(diǎn)擊監(jiān)聽
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<String> dates = mCalendarView.getSelectedDates();
for (String date : dates) {
Log.e("test", "date: " + date);
}
}
});
如果只需要進(jìn)行數(shù)據(jù)展示,而不需要點(diǎn)擊,可以設(shè)置:
// 設(shè)置已選日期 mCalendarView.setSelectedDates(mDatas); // 設(shè)置不可以被點(diǎn)擊 mCalendarView.setClickable(false);
源碼下載:
GitHub地址:https://github.com/Airsaid/CalendarView 歡迎star~!
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Android 一個(gè)日歷控件的實(shí)現(xiàn)代碼
- Android開發(fā)之日歷CalendarView用法示例
- Android實(shí)現(xiàn)自定義日歷
- android 開發(fā)教程之日歷項(xiàng)目實(shí)踐(一)
- android 開發(fā)教程之日歷項(xiàng)目實(shí)踐(三)
- android 開發(fā)教程之日歷項(xiàng)目實(shí)踐(二)
- Android可簽到日歷控件的實(shí)現(xiàn)方法
- Android實(shí)現(xiàn)日歷控件示例代碼
- Android 仿日歷翻頁、仿htc時(shí)鐘翻頁、數(shù)字翻頁切換效果
- Android?Studio簡單實(shí)現(xiàn)自定義日歷
相關(guān)文章
android實(shí)現(xiàn)將位置信息寫入JPEG圖片文件
下面小編就為大家?guī)硪黄猘ndroid實(shí)現(xiàn)將位置信息寫入JPEG圖片文件。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-03-03
詳解AndroidStudio中代碼重構(gòu)菜單Refactor功能
這篇文章主要介紹了AndroidStudio中代碼重構(gòu)菜單Refactor功能詳解,本文通過代碼演示,功能截圖來詳細(xì)說明as為大名重構(gòu)提供的各項(xiàng)功能,需要的朋友可以參考下2019-11-11
Android開發(fā)之使用ViewPager實(shí)現(xiàn)圖片左右滑動(dòng)切換效果
這篇文章主要介紹了Android開發(fā)之使用ViewPager實(shí)現(xiàn)圖片左右滑動(dòng)切換效果的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-08-08
Android?imageVIew實(shí)現(xiàn)鏡像旋轉(zhuǎn)的方法
在Android應(yīng)用開發(fā)中,有時(shí)候我們需要對ImageView中的圖片進(jìn)行鏡像旋轉(zhuǎn),以展示不同的效果,本文將介紹如何使用代碼實(shí)現(xiàn)ImageView的鏡像旋轉(zhuǎn)效果,這篇文章主要介紹了Android?imageVIew如何做鏡像旋轉(zhuǎn),需要的朋友可以參考下2024-06-06
Android使用LinearLayout設(shè)置邊框
這篇文章主要介紹了Android如何使用LinearLayout設(shè)置邊框,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-09-09
Android 中讀取SD卡文件時(shí)拋出NullPointerException錯(cuò)誤解決辦法
這篇文章主要介紹了Android 中讀取SD卡文件時(shí)拋出NullPointerException錯(cuò)誤解決辦法的相關(guān)資料,需要的朋友可以參考下2017-05-05

