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~!
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(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 仿日歷翻頁(yè)、仿htc時(shí)鐘翻頁(yè)、數(shù)字翻頁(yè)切換效果
- Android?Studio簡(jiǎn)單實(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-11Android開發(fā)之使用ViewPager實(shí)現(xiàn)圖片左右滑動(dòng)切換效果
這篇文章主要介紹了Android開發(fā)之使用ViewPager實(shí)現(xiàn)圖片左右滑動(dòng)切換效果的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-08-08Android?imageVIew實(shí)現(xiàn)鏡像旋轉(zhuǎn)的方法
在Android應(yīng)用開發(fā)中,有時(shí)候我們需要對(duì)ImageView中的圖片進(jìn)行鏡像旋轉(zhuǎn),以展示不同的效果,本文將介紹如何使用代碼實(shí)現(xiàn)ImageView的鏡像旋轉(zhuǎn)效果,這篇文章主要介紹了Android?imageVIew如何做鏡像旋轉(zhuǎn),需要的朋友可以參考下2024-06-06Android使用LinearLayout設(shè)置邊框
這篇文章主要介紹了Android如何使用LinearLayout設(shè)置邊框,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-09-09Android 中讀取SD卡文件時(shí)拋出NullPointerException錯(cuò)誤解決辦法
這篇文章主要介紹了Android 中讀取SD卡文件時(shí)拋出NullPointerException錯(cuò)誤解決辦法的相關(guān)資料,需要的朋友可以參考下2017-05-05