橫向滑動的日曆控制元件的實現
實現橫向滑動的日曆控制元件可以作為簽到的日曆控制元件
看見MaterialCalendarView 仿照它大致的思路自己實現一個CanlendarView,
給一個ClaendarPagerView extends viewgroup新增四十二個TextView,用來顯示一個月的天數,
然後在ViewPagerAdapter 中填充CalendarPagerView,給viewPager填充adpater,就可以實現橫向滑動的日曆控制元件了。其中比較麻煩的就是日期的處理,還有一些小的細節.
首先實現單獨天數的控制元件繼承CheckedTextView
public class DayView extends CheckedTextView {
//這一天的日期類
private CalendarDay day;
//建構函式給個Visible引數 決定日期是否可見 public DayView(Context context,Calendar day,int visible) { super(context); setGravity(Gravity.CENTER); if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1){ setTextAlignment(TEXT_ALIGNMENT_CENTER); } this.day = CalendarDay.from(day); setVisibility(visible); setText(CalendarUtils.getDay(day)+""); } //返回這個日期是幾號 public String getLabel(){ return CalendarUtils.getDay(day.getCalendar())+""; } //返回真實日期 Calendar類的月份是從0開始的 public CalendarDay getDate(){ CalendarDay tempDay = CalendarDay.from(day.getYear(),day.getMonth()+1,day.getDay()); return tempDay; } //返回CalendarDay物件 CalendarDay類是對Calendar的封裝類 public CalendarDay getDay(){ CalendarDay tempDay = CalendarDay.from(day.getYear(),day.getMonth(),day.getDay()); return tempDay; } //設定選擇背景 private Drawable customBackground; //設定背景圖片 public void setCustomBackground(Drawable drawable){ if(drawable == null){ this.customBackground = null; }else{ this.customBackground = drawable.getConstantState().newDrawable(getResources()); } invalidate(); } //自定義了一個TextSpan 給需要的日期集合設定這個樣式 擴充套件日期控制元件的內容 // int mode mode 的不同設定不同的樣式 //visible 決定日期顯示不顯示 ,如果是當前頁面的日期就為true 不是當前頁面的月份就不顯示 public void applyTextSpan(int mode,boolean visible){ setVisibility(visible ? View.VISIBLE : View.INVISIBLE); if(mode == 0 || mode == 1){ String label = getLabel(); SpannableString formattedLabel = new SpannableString(getLabel()); formattedLabel.setSpan(new TextSpan(mode), 0, label.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); setText(formattedLabel); }else{ setText(getLabel()); } // invalidate(); } //清空DayView的背景, public void setUnselected(){ customBackground = null; invalidate(); } //設定DayVIew 點選日期後的背景 public void setSelected(){ customBackground = generateCircleDrawable(Color.GRAY); invalidate(); } private final Rect tempRect = new Rect(); @Override protected void onDraw(Canvas canvas) { if(customBackground != null){ canvas.getClipBounds(tempRect); customBackground.setBounds(tempRect); customBackground.setState(getDrawableState()); customBackground.draw(canvas); } super.onDraw(canvas); } //生成圓的的drawable private static Drawable generateCircleDrawable(final int color){ ShapeDrawable drawable = new ShapeDrawable(new OvalShape()); drawable.setShaderFactory(new ShapeDrawable.ShaderFactory() { @Override public Shader resize(int width, int height) { return new LinearGradient(0, 0, 0, 0, color, color, Shader.TileMode.REPEAT); } }); return drawable; }
}
CalendarPagerView 中填充DayView控制元件
public class CalendarPagerView extends ViewGroup implements View.OnClickListener{
//當前月的頁面中的 當前月
private int currentMonth;
//儲存四十二天的日期
private List dayViews = new ArrayList<>();
//用來傳遞日期點選事件 private CalendarViewGAC calendarView; public CalendarPagerView(Context context,CalendarDay firstDayCurrentMonth,CalendarViewGAC calendarView) { super(context); currentMonth = firstDayCurrentMonth.getMonth()+1; this.calendarView = calendarView; buildWeekViews(); buildayViews(firstDayCurrentMonth); } public int getCurrentMonth(){ return currentMonth; } public void clearSelction(){ for(int i = 0; i < dayViews.size();i++){ dayViews.get(i).setUnselected(); } } //建立週日到週一的標題欄 private void buildWeekViews(){ TextView tv1 = new TextView(getContext()); tv1.setText("星期日"); TextView tv2 = new TextView(getContext()); tv2.setText("星期一"); TextView tv3 = new TextView(getContext()); tv3.setText("星期二"); TextView tv4 = new TextView(getContext()); tv4.setText("星期三"); TextView tv5 = new TextView(getContext()); tv5.setText("星期四"); TextView tv6 = new TextView(getContext()); tv6.setText("星期五"); TextView tv7 = new TextView(getContext()); tv7.setText("星期六"); addView(tv1); addView(tv2); addView(tv3); addView(tv4); addView(tv5); addView(tv6); addView(tv7); } //根據當前月份生成是四十二天的日期 private void buildayViews(CalendarDay firstDayCurrentMonth){ dayViews.clear(); Log.e("gac","firstDayCurrentMonth:"+firstDayCurrentMonth.toString()); Calendar calendar = firstDayCurrentMonth.getCalendar(); Log.e("gac","month:"+calendar.get(Calendar.MONTH)); calendar.setFirstDayOfWeek(Calendar.SUNDAY); int delta = CalendarUtils.getDayOfWeek(calendar); Log.e("gac","delta:"+delta); if(delta > 0){ delta = Calendar.SUNDAY - delta; calendar.add(Calendar.DATE,delta); }else{ } for(int i = 0; i < 42;i++){ DayView day = null; if(currentMonth != (calendar.get(Calendar.MONTH)+1)){ day = new DayView(getContext(),calendar,View.INVISIBLE); }else{ day = new DayView(getContext(),calendar,View.VISIBLE); } if(currentMonth != day.getDate().getMonth()){ day.setVisibility(INVISIBLE); } calendar.add(Calendar.DATE, 1); day.setOnClickListener(this); dayViews.add(day); addView(day); } } //給CalendarPagerView 應用設定好的TextSpan //TextDecorator 得到傳遞的日期集合 並且判斷該日期需要設定的控制元件內容型別 public void applayDecorator(TextDecorator decorator){ for(int i = 0; i < dayViews.size();i++){ DayView dayView = dayViews.get(i); int mode = decorator.shouldDecorateGAC(dayView.getDay()); Log.e("gac","date:"+dayView.getDate()+" mode:"+mode); dayView.applyTextSpan(mode,currentMonth==dayView.getDate().getMonth()); } } //給四十九個控制元件 進行排列 @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { int count = getChildCount(); final int parentLeft = 0; int childTop = 0; int childLeft = parentLeft; for (int i = 0; i < count; i++) { final View child = getChildAt(i); final int width = child.getMeasuredWidth(); final int height = child.getMeasuredHeight(); child.layout(childLeft, childTop, childLeft + width, childTop + height); childLeft += width; //We should warp every so many children if (i % 7 == 6) { childLeft = parentLeft; childTop += height; } } } //測量佈局檔案 對佈局進行測量 @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { final int specWidthSize = MeasureSpec.getSize(widthMeasureSpec); final int specWidthMode = MeasureSpec.getMode(widthMeasureSpec); final int specHeightSize = MeasureSpec.getSize(heightMeasureSpec); final int specHeightMode = MeasureSpec.getMode(heightMeasureSpec); if (specHeightMode == MeasureSpec.UNSPECIFIED || specWidthMode == MeasureSpec.UNSPECIFIED) { throw new IllegalStateException("CalendarPagerView should never be left to decide it's size"); } //The spec width should be a correct multiple final int measureTileSize = specWidthSize / 7; //Just use the spec sizes setMeasuredDimension(specWidthSize, specHeightSize); int count = getChildCount(); for (int i = 0; i < count; i++) { final View child = getChildAt(i); int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec( measureTileSize, MeasureSpec.EXACTLY ); int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec( measureTileSize, MeasureSpec.EXACTLY ); child.measure(childWidthMeasureSpec, childHeightMeasureSpec); } } //將日期的點選事件傳給CalendarViewGAC @Override public void onClick(View v) { DayView d = (DayView)v; calendarView.onDateClicked(d); }
}
接下來實現CalendarPagerAdapter 主要為了填充CalendarPagerView 最後將adpater設定進ViewPager中去實現日曆的滑動效果:
public class CalendarPagerAdapter extends PagerAdapter{
private CalendarViewGAC view;
private int currentPosition = -1;
private Context context;
CalendarPagerView pager;//當前這個月的日曆頁面
private CalendarViewGAC calendarViewGAC;//將CalendarViewGAC傳遞給CalendarPagerView 為了傳遞日期點選事件
private List<CalendarPagerView> pagers = new ArrayList<>();//儲存快取的日曆頁面的集合
private TextDecorator decorator;//設定DayView控制元件的擴充套件介面的日期集合
public CalendarPagerAdapter(Context c,CalendarViewGAC calendarViewGAC){
context = c;
this.calendarViewGAC = calendarViewGAC;
}
//通過此方法得到一個CalendarVIewPager頁面 根據position位置去獲得當前日期
@Override
public Object instantiateItem(ViewGroup container, int position) {
//Log.e("gac","instaniateItem........");
pager = createViewByPosition(position);//new CalendarPagerView(view.getContext());
pagers.add(pager);
container.addView(pager);
//invalidateDecorators();
invalidateDecorators();
return pager;
}
public void setDecorator(TextDecorator decorator){
this.decorator = decorator;
invalidateDecorators();
}
private void invalidateDecorators(){
Log.e("gac","size:"+pagers.size());
for(int i = 0;i < pagers.size(); i++){
Log.e("gac","pager current month:"+pagers.get(i).getCurrentMonth());
pagers.get(i).applayDecorator(decorator);
}
}
private CalendarPagerView createViewByPosition(int position){
if(currentPosition == -1 || position == 0){
currentPosition = DateAndPostion.getPosition(CalendarDay.today());
}
// Log.e("gac","create ************************View By position!!!!!!!");
// Log.e("gac","position:"+position);
CalendarDay day = DateAndPostion.getCalendarDay(position);
// Log.e("gac","day:"+day.toString());
return new CalendarPagerView(context,day,calendarViewGAC);
}
@Override
public int getCount() {
return 50000;
}
// @Override
// public int getItemPosition(Object object) {
// Log.e(“gac”,”getItemPosition”);
// int index = 0;
// if(object instanceof CalendarPagerView){
// index = currentPosition;
// }
// Log.e(“gac”,”index;”+index);
// return index;
// }
//清除所有選中的日期背景 然後重新設定點選背景
public void clearSelections(){
for(int i = 0; i < pagers.size();i++){
pagers.get(i).clearSelction();
}
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
((ViewPager) container).removeView((CalendarPagerView) object);
pagers.remove((CalendarPagerView)object);
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
//這個類用於日期和ViewPager當前位置的轉換類 可以將當前位置轉換為日期,也可以將日期轉換為當前位置
public static class DateAndPostion{
public static int getPosition(CalendarDay c){
int year = c.getYear();
int month = c.getMonth();
return (year-1)*12+month;
}
public static CalendarDay getCalendarDay(int position){
int year = position/12;
int month = position-(year*12);
return CalendarDay.from(year+1, month, 1);
}
}
}
最後是CalendarViewGAC類,這個類繼承LinearLayout集合,設定了一個標題顯示 年月份的TextView,標題下面就是一個ViewPager類,這個類填充CalendarViewPagerAdapter類,最後就可以實現滑動的日曆控制元件了.
package com.gac.calendarviewgac;
import android.content.Context;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.LinearLayout;
import android.widget.TextView;
import java.text.SimpleDateFormat;
/**
* Created by Administrator on 2016/3/22.
*/
public class CalendarViewGAC extends LinearLayout {
private CalendarPager pager;//繼承ViewPager 為了裝載月份頁面的檢視
private CalendarPagerAdapter adapter;
private OnDateSelectedListener listener;//單獨日期點選事件的監聽器
private TextView title;//顯示年月的標題
public interface OnDateSelectedListener{
void onDateSelected(DayView view);
}
public CalendarViewGAC(Context context) {
this(context, null);
}
public CalendarViewGAC(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CalendarViewGAC(Context context,AttributeSet attrs,int def){
super(context, attrs, def);
setOrientation(VERTICAL);
initTopBar(context);
init(context);
//addView(new CalendarPagerView(context));
}
public void setOnDateSelectedLintener(OnDateSelectedListener lintener){
this.listener = lintener;
}
//初始化標題欄
private void initTopBar(Context context){
title = new TextView(context);
title.setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
title.setText(“title”);
title.setTextSize(20);
title.setGravity(Gravity.CENTER);
addView(title,new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
}
//初始化CalendarPager 並且填充Adapter
private void init(Context context){
pager = new CalendarPager(context);
adapter = new CalendarPagerAdapter(context,this);
pager.setAdapter(adapter);
SimpleDateFormat format = new SimpleDateFormat(“MM 月 yyyy 年”);
String date = format.format((CalendarDay.today().getDate()));
title.setText(date);
//設定當前日期為顯示時間
pager.setCurrentItem(CalendarPagerAdapter.DateAndPostion.getPosition(CalendarDay.today()));
//給pager新增滑動監聽器
pager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
CalendarDay day = CalendarPagerAdapter.DateAndPostion.getCalendarDay(position);
// date = DateUtils.getDateStr(day.getDate());
SimpleDateFormat format = new SimpleDateFormat("MM 月 yyyy 年");
String date = format.format(day.getDate());
title.setText(date);
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
addView(pager,new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
}
@Override
public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
super.onInitializeAccessibilityEvent(event);
event.setClassName(CalendarViewGAC.class.getName());
}
@Override
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(info);
info.setClassName(CalendarViewGAC.class.getName());
}
@Override
public boolean onTouchEvent(MotionEvent event) {
return pager.dispatchTouchEvent(event);
}
@Override
protected void onLayout(boolean changed, int left, int t, int right, int b) {
final int count = getChildCount();
Log.e("gac","count:count:"+count);
final int parentLeft = getPaddingLeft();
final int parentWidth = right - left - parentLeft - getPaddingRight();
int childTop = getPaddingTop();
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
final int width = child.getMeasuredWidth();
final int height = child.getMeasuredHeight();
int delta = (parentWidth - width) / 2;
int childLeft = parentLeft + delta;
child.layout(childLeft, childTop, childLeft + width, childTop + height);
childTop += height;
}
}
private static int clampSize(int size, int spec) {
int specMode = MeasureSpec.getMode(spec);
int specSize = MeasureSpec.getSize(spec);
switch (specMode) {
case MeasureSpec.EXACTLY: {
return specSize;
}
case MeasureSpec.AT_MOST: {
return Math.min(size, specSize);
}
case MeasureSpec.UNSPECIFIED:
default: {
return size;
}
}
}
protected void onDateClicked(DayView dayView){
adapter.clearSelections();
listener.onDateSelected(dayView);
}
public void setDecorator(TextDecorator decorator){
adapter.setDecorator(decorator);
}
class CalendarPager extends ViewPager {
private boolean pagingEnabled = true;
public CalendarPager(Context context) {
super(context);
}
CalendarPager(Context context,AttributeSet attrs){
super(context,attrs);
}
public void setChildrenDrawingOrderEnabledCompat(boolean enable) {
setChildrenDrawingOrderEnabled(enable);
}
/**
* enable disable viewpager scroll
*
* @param pagingEnabled false to disable paging, true for paging (default)
*/
public void setPagingEnabled(boolean pagingEnabled) {
this.pagingEnabled = pagingEnabled;
}
/**
* @return is this viewpager allowed to page
*/
public boolean isPagingEnabled() {
return pagingEnabled;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return pagingEnabled && super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
return pagingEnabled && super.onTouchEvent(ev);
}
@Override
public boolean canScrollVertically(int direction) {
/**
* disables scrolling vertically when paging disabled, fixes scrolling
* for nested {@link android.support.v4.view.ViewPager}
*/
return pagingEnabled && super.canScrollVertically(direction);
}
@Override
public boolean canScrollHorizontally(int direction) {
/**
* disables scrolling horizontally when paging disabled, fixes scrolling
* for nested {@link android.support.v4.view.ViewPager}
*/
return pagingEnabled && super.canScrollHorizontally(direction);
}
}
}
原始碼下載地址:
有什麼不明白的歡迎交流!!!!