編輯:關於Android編程
效果圖:
依賴導入<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwcmUgY2xhc3M9"brush:java;">
compile 'cn.aigestudio.datepicker:DatePicker:2.2.0'
DatePicker直接在xml布局使用,代碼進行相關的初始化配置監聽設置等。(setData方法必須調用進行初始化)
......
DatePicker datePicker = (DatePicker) findViewById(R.id.datePicker);
datePicker.setDate(mYear, mMonth);
datePicker.setOnDateSelectedListener(new DatePicker.OnDateSelectedListener() {
@Override
public void onDateSelected(List date) {
}
});
......
DatePicker支持多種模式:單選、多選、正常模式,具體定義如下
public enum DPMode {
SINGLE, MULTIPLE, NONE
}
DatePicker不同的模式通過不同的監聽回調事件,獲取選中的時間值
setOnDateSelectedListener // 多選監聽
setOnDatePickedListener // 單選監聽
選擇後的日期將會以列表(多選模式下)或字符串(單選模式下)的形式返回,日期字符串的格式為:
格式:yyyy-M-d
示例:2015-3-28
DatePicker有一套自己的默認UI,默認的Color配置,如果你使用Module導入工程,可以直接修改默認配置,關聯類如下
DatePicker默認天朝的節日背景色
當然你也可以自己修改定制,同時還可以為特殊節日添加浮標,首先添加指定需要修改號數集,添加到緩存到對應map集合
List tmpTR = new ArrayList<>();
tmpTR.add("2015-10-10");
tmpTR.add("2015-10-11");
tmpTR.add("2015-10-12");
tmpTR.add("2015-10-13");
tmpTR.add("2015-10-14");
tmpTR.add("2015-10-15");
tmpTR.add("2015-10-16");
DPCManager.getInstance().setDecorTR(tmpTR);
private static final HashMap> DECOR_CACHE_BG = new HashMap<>();
private static final HashMap> DECOR_CACHE_TL = new HashMap<>();
private static final HashMap> DECOR_CACHE_T = new HashMap<>();
private static final HashMap> DECOR_CACHE_TR = new HashMap<>();
private static final HashMap> DECOR_CACHE_L = new HashMap<>();
private static final HashMap> DECOR_CACHE_R = new HashMap<>();
完成上述步驟後,再通過setDPDecor方法繪制對應號數的浮標
picker.setDPDecor(new DPDecor() {
@Override
public void drawDecorTL(Canvas canvas, Rect rect, Paint paint, String data) {
super.drawDecorTL(canvas, rect, paint, data);
switch (data) {
case "2015-10-5":
case "2015-10-7":
case "2015-10-9":
case "2015-10-11":
paint.setColor(Color.GREEN);
canvas.drawRect(rect, paint);
break;
default:
paint.setColor(Color.RED);
canvas.drawCircle(rect.centerX(), rect.centerY(), rect.width() / 2, paint);
break;
}
}
@Override
public void drawDecorTR(Canvas canvas, Rect rect, Paint paint, String data) {
super.drawDecorTR(canvas, rect, paint, data);
switch (data) {
case "2015-10-10":
case "2015-10-11":
case "2015-10-12":
paint.setColor(Color.BLUE);
canvas.drawCircle(rect.centerX(), rect.centerY(), rect.width() / 2, paint);
break;
default:
paint.setColor(Color.YELLOW);
canvas.drawRect(rect, paint);
break;
}
}
});
繪制的浮標可以是不同形狀不同顏色,具體怎麼關聯到月視圖稍後再看,繪制的浮標可以是不同方向,具體使用參考DPDecor類的定義。(具體使用參考上面代碼塊實例)
繪制浮標效果如下圖:
更為詳盡的使用請參考Aige官方示例:https://github.com/AigeStudio/DatePicker
calendars包內部提供方法都有中文注釋,相信都能看懂,關於一些節日號數相關基礎數據的獲取判斷,內部的一些算法,表示無能為力(我算法白癡),DPDecor定義方法上面有提到,具體使用不詳解。
抽象類DPLManager是一個語言管理方面的,該庫支持中英文的日歷,根據CH標示采取不同的實現
theme模塊關於UI配置方面的,上面有提到,具體代碼設置color,請參考源碼,頭有中文注釋一目了然。
日歷數據實體,封裝日歷繪制時需要的數據(封裝按照二維數組,每個月可能存在的行列進行封裝)
public class DPInfo {
public String strG, strF;
public boolean isHoliday;
public boolean isToday, isWeekend;
public boolean isSolarTerms, isFestival, isDeferred;
public boolean isDecorBG;
public boolean isDecorTL, isDecorT, isDecorTR, isDecorL, isDecorR;
}
utils包裡面就一個dp px的轉換和一維數組轉二維數組
整個庫的UI核心是下面兩個類
DatePicker
MonthView
DatePicker自定義的Layout容器,構造函數先添加標題欄顯示控件和星期-到星期日的顯示視圖,最後添加月視圖
public DatePicker(Context context, AttributeSet attrs) {
super(context, attrs);
//..............略過些許方法................
mTManager = DPTManager.getInstance();
mLManager = DPLManager.getInstance();
// 設置排列方向為豎向
setOrientation(VERTICAL);
// 標題欄根布局
RelativeLayout rlTitle = new RelativeLayout(context);
// 周視圖根布局
LinearLayout llWeek = new LinearLayout(context);
// 標題欄子元素布局參數
RelativeLayout.LayoutParams lpYear =
new RelativeLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT);
// --------------------------------------------------------------------------------標題欄
// 年份顯示
tvYear = new TextView(context);
// 月份顯示
tvMonth = new TextView(context);
tvMonth.setText("六月");
// 確定顯示
tvEnsure = new TextView(context);
tvEnsure.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (null != onDateSelectedListener) {
onDateSelectedListener.onDateSelected(monthView.getDateSelected());
}
}
});
rlTitle.addView(tvYear, lpYear);
rlTitle.addView(tvMonth, lpMonth);
rlTitle.addView(tvEnsure, lpEnsure);
addView(rlTitle, llParams);
// --------------------------------------------------------------------------------周視圖
for (int i = 0; i < mLManager.titleWeek().length; i++) {
TextView tvWeek = new TextView(context);
tvWeek.setText(mLManager.titleWeek()[i]);
tvWeek.setGravity(Gravity.CENTER);
tvWeek.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14);
tvWeek.setTextColor(mTManager.colorTitle());
llWeek.addView(tvWeek, lpWeek);
}
addView(llWeek, llParams);
// ------------------------------------------------------------------------------------月視圖
monthView = new MonthView(context);
monthView.setOnDateChangeListener(new MonthView.OnDateChangeListener() {
@Override
public void onMonthChange(int month) {
tvMonth.setText(mLManager.titleMonth()[month - 1]);
}
@Override
public void onYearChange(int year) {
String tmp = String.valueOf(year);
if (tmp.startsWith("-")) {
tmp = tmp.replace("-", mLManager.titleBC());
}
tvYear.setText(tmp);
}
});
addView(monthView, llParams);
}
設置日歷選擇模式如果支持多選則顯示確定按鈕
/**
* 設置日期選擇模式
*
* @param mode ...
*/
public void setMode(DPMode mode) {
if (mode != DPMode.MULTIPLE) {
tvEnsure.setVisibility(GONE);
}
monthView.setDPMode(mode);
}
提供多個set方法,本質是調用了月視圖View進行set,特別是對於DatePicker的監聽綁定做了判斷。Aige的使用文檔是這樣說的
MonthView默認多選模式,在沒有設置模式的情況下直接設置單選監聽會拋出異常,反之同理
/**
* 設置單選監聽器
*
* @param onDatePickedListener ...
*/
public void setOnDatePickedListener(OnDatePickedListener onDatePickedListener) {
if (monthView.getDPMode() != DPMode.SINGLE) {
throw new RuntimeException(
"Current DPMode does not SINGLE! Please call setMode set DPMode to SINGLE!");
}
monthView.setOnDatePickedListener(onDatePickedListener);
}
/**
* 設置多選監聽器
*
* @param onDateSelectedListener ...
*/
public void setOnDateSelectedListener(OnDateSelectedListener onDateSelectedListener) {
if (monthView.getDPMode() != DPMode.MULTIPLE) {
throw new RuntimeException(
"Current DPMode does not MULTIPLE! Please call setMode set DPMode to MULTIPLE!");
}
this.onDateSelectedListener = onDateSelectedListener;
}
MonthView月視圖自定義控件,內部構造函數初始化一個Scroller實例用於輔助滑動,一個縮放動畫監聽(單選或者多選模式下,選中某一項會有個縮放動畫的圓圈背景)
public MonthView(Context context) {
super(context);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
scaleAnimationListener = new ScaleAnimationListener();
}
mScroller = new Scroller(context);
mPaint.setTextAlign(Paint.Align.CENTER);
}
computeScroll:主要功能是計算拖動的位移量更新UI,重寫computeScroll()的原因,調用startScroll()是不會有滾動效果的,只有在computeScroll()獲取滾動情況,參考Scroller計算,做出滾動的響應,computeScroll在父控件執行drawChild時,會調用這個方法.要知道計算有沒有終止,需要通過mScroller.computeScrollOffset()
@Override
public void computeScroll() {
if (mScroller.computeScrollOffset()) {
scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
invalidate();
} else {
requestLayout()
}
}
onTouch方法內判斷滑動事件,如果還在滑動,down時結束滑動,並記錄位置,MOVE時判斷滑動距離,設置滑動方向mSlideMode (內部定義枚舉類型),根據不同的Mode和計算出的距離,調用Scroller.startScroll方法配合computescroll實現滑動視圖。而Touch的up事件如果造成月視圖切換,可能月份年份的變化,這時候會調用到computeDate方法,該方法判斷後根據條件執行回調,而回調函數在DatePicker內部實現了局部更新UI
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mScroller.forceFinished(true);
mSlideMode = null;
isNewEvent = true;
lastPointX = (int) event.getX();
lastPointY = (int) event.getY();
break;
case MotionEvent.ACTION_MOVE:
if (isNewEvent) {
if (Math.abs(lastPointX - event.getX()) > 100) {
mSlideMode = SlideMode.HOR;
isNewEvent = false;
} else if (Math.abs(lastPointY - event.getY()) > 50) {
mSlideMode = SlideMode.VER;
isNewEvent = false;
}
}
if (mSlideMode == SlideMode.HOR) {
int totalMoveX = (int) (lastPointX - event.getX()) + lastMoveX;
smoothScrollTo(totalMoveX, indexYear * height);
} else if (mSlideMode == SlideMode.VER) {
int totalMoveY = (int) (lastPointY - event.getY()) + lastMoveY;
smoothScrollTo(width * indexMonth, totalMoveY);
}
break;
case MotionEvent.ACTION_UP:
if (mSlideMode == SlideMode.VER) {
if (Math.abs(lastPointY - event.getY()) > 25) {
if (lastPointY < event.getY()) {
if (Math.abs(lastPointY - event.getY()) >= criticalHeight) {
indexYear--;
centerYear = centerYear - 1;
}
} else if (lastPointY > event.getY()) {
if (Math.abs(lastPointY - event.getY()) >= criticalHeight) {
indexYear++;
centerYear = centerYear + 1;
}
}
buildRegion();
computeDate();
smoothScrollTo(width * indexMonth, height * indexYear);
lastMoveY = height * indexYear;
} else {
defineRegion((int) event.getX(), (int) event.getY());
}
} else if (mSlideMode == SlideMode.HOR) {
if (Math.abs(lastPointX - event.getX()) > 25) {
if (lastPointX > event.getX() &&
Math.abs(lastPointX - event.getX()) >= criticalWidth) {
indexMonth++;
centerMonth = (centerMonth + 1) % 13;
if (centerMonth == 0) {
centerMonth = 1;
centerYear++;
}
} else if (lastPointX < event.getX() &&
Math.abs(lastPointX - event.getX()) >= criticalWidth) {
indexMonth--;
centerMonth = (centerMonth - 1) % 12;
if (centerMonth == 0) {
centerMonth = 12;
centerYear--;
}
}
buildRegion();
computeDate();
smoothScrollTo(width * indexMonth, indexYear * height);
lastMoveX = width * indexMonth;
} else {
defineRegion((int) event.getX(), (int) event.getY());
}
} else {
defineRegion((int) event.getX(), (int) event.getY());
}
break;
}
return true;
}
至於上面的defineRegion方法我也只了解一點(計算真心看不懂),基於v11版本區分單選模式和多選模式,執行縮放動畫,加減速差值器效果(圓圈背景縮放),動畫結束執行對應的回調函數,至於多選的值緩存方式使用的是List,個人認為還有更好的方案,具體參考AbsListView(看了別打我~~(>_<)~~)
private List dateSelected = new ArrayList<>();
至於圓形背景怎麼來,以前博客提過很多次,不再逼逼叨叨了
Aige就是Aige,看完這個庫收獲良多!今後要加強算法方面的學習了,閏年判斷都差不多忘了,時光飛逝,遙想當年學java,一個閏年判斷的java的demo分分鐘的事,現在還得百度一下!!!
Android基礎入門教程——8.3.14 Paint幾個枚舉/常量值以及ShadowLayer陰影效果標簽(空格分隔): Android基礎入門
魅藍Max和小米Max哪個好?在小米Max發布了四個月了,魅族旗下的子系列魅藍Max也跟隨腳步發布了,那麼對於兩款都是“MAX”的手
一、為什麼要進行屏幕適配某廠商統計如下數據2012年,支持Android的設備共有3997種 2013年,支持Android的設備共有11868種 2014年,支持And
篇外話:先來說下今天的日期,今天是2015年02月18日也就是大年三十,大家都在歡歡喜喜的准備過大年,活動也各式各樣,搓麻將、打撲克、放煙花、准備看春晚,而我卻還在敲代碼