本文出自:http://blog.csdn.net/dt235201314/article/details/78678419
Android自定义时间控件选择开始时间到结束时间
Android自定义时间控件不可选择未来时间
一丶效果图
二丶概述
大概是去年的这个时候说要做一个关于时间控件的总结,到这会才开始。在整理demo时,发现直接上代码也无法由浅入深讲解,眨眼就是11月30号了,踩着11月份的尾巴,先来一篇,后面再补充。
三丶看代码
(1).需求分析
1.选择开始时间到结束时间
2.结束时间不能超过今天
3.开始时间不能超过结束时间
4.最早选择2017年1月1日
5.仿iOS(表示很无奈)
(2)看代码
首先是一个Dialog
DoubleTimeSelectDialog.java
public class DoubleTimeSelectDialog extends Dialog implements View.OnClickListener{ private Context mContext; /** * 事件选取完毕监听 */ private OnDateSelectFinished onDateSelectFinished; /** * 开始、结束年份 */ private static int START_YEAR = 1990, END_YEAR = 2100; /** * 最大年龄 */ private final int LARGEST_AGE = 119; /** * 年 */ private WheelView mYearView; /** * 月 */ private WheelView mMonthView; /** * 日 */ private WheelView mDayView; /** * 时 */ private WheelView mHourView; /** * 分 */ private WheelView mMinuteView; /** * list列表(大月份) */ private ListmListBig; /** * list列表(小月份) */ private List mListLittle; /* 是否只选择本年 */ private boolean isOnlyThisYear = false; private boolean isShowMinute = false; /* 是否只选择本月 */ private boolean isOnlyThisMonth = false; public static final String YEAR = "year"; public static final String MONTH = "month"; public static final String DAY = "day"; private int year; private int month; private int day; private int hour; private int minute; private int curYear; private int curMonth; private int curDay; /**时间容器*/ private LinearLayout mTimeContainerLl; /**开始时间*/ private TextView mBeginTimeTv; /**结束时间*/ private TextView mEndTimeTv; /**选择的开始时间*/ private String mSelectStartTime; /**选择的结束时间*/ private String mSelectEndTime; /**当前选择时间模式*/ private TIME_TYPE mTimeType = TIME_TYPE.TYPE_START; /**最小时间*/ private String allowedSmallestTime; /**最大时间*/ private String allowedBiggestTime; private enum TIME_TYPE { TYPE_START, TYPE_END } public DoubleTimeSelectDialog(Context context) { super(context, R.style.PopBottomDialogStyle); } public DoubleTimeSelectDialog(Context context, String curTime, String startTime) { super(context, R.style.PopBottomDialogStyle); this.mContext = context; this.allowedSmallestTime = startTime; setContentView(R.layout.popwindow_bottom_layout); setCanceledOnTouchOutside(true); Window mDialogWindow = getWindow(); mDialogWindow.setGravity(Gravity.BOTTOM); WindowManager.LayoutParams lp = mDialogWindow.getAttributes(); lp.y = 0;//设置Dialog距离底部的距离 lp.width = ViewGroup.LayoutParams.MATCH_PARENT; mDialogWindow.setAttributes(lp); initDialogView(); init(curTime, false); String monthS = String.format("%02d", curMonth); String dayS = String.format("%02d", curDay); String yearS = String.format("%02d", curYear); if(!TextUtils.isEmpty(curTime)){ mSelectStartTime = curTime; mBeginTimeTv.setText(makeFormatContent(mContext.getString(R.string.begin_at), curTime.replaceAll("-","."))); mSelectEndTime = curTime; mEndTimeTv.setText(makeFormatContent(mContext.getString(R.string.end_at), curTime.replaceAll("-","."))); }else{ mSelectStartTime = yearS + "-" + monthS + "-" + dayS; mBeginTimeTv.setText(makeFormatContent(mContext.getString(R.string.begin_at), yearS + "." + monthS + "." + dayS)); mSelectEndTime = yearS + "-" + monthS + "-" + dayS; mEndTimeTv.setText(makeFormatContent(mContext.getString(R.string.end_at), yearS + "." + monthS + "." + dayS)); } } public DoubleTimeSelectDialog(Context context, String earliestTime, String startTime, String endTime) { super(context, R.style.PopBottomDialogStyle); this.mContext = context; this.allowedSmallestTime = earliestTime; this.allowedBiggestTime = endTime; setContentView(R.layout.popwindow_bottom_layout); setCanceledOnTouchOutside(true); Window mDialogWindow = getWindow(); mDialogWindow.setGravity(Gravity.BOTTOM); WindowManager.LayoutParams lp = mDialogWindow.getAttributes(); lp.y = 0;//设置Dialog距离底部的距离 lp.width = ViewGroup.LayoutParams.MATCH_PARENT; mDialogWindow.setAttributes(lp); initDialogView(); init(startTime, false); String monthS = String.format("%02d", curMonth); String dayS = String.format("%02d", curDay); String yearS = String.format("%02d", curYear); if(!TextUtils.isEmpty(startTime)){ mSelectStartTime = startTime; mBeginTimeTv.setText(makeFormatContent(mContext.getString(R.string.begin_at), startTime.replaceAll("-","."))); mSelectEndTime = endTime; mEndTimeTv.setText(makeFormatContent(mContext.getString(R.string.end_at), endTime.replaceAll("-","."))); }else{ mSelectStartTime = yearS + "-" + monthS + "-" + dayS; mBeginTimeTv.setText(makeFormatContent(mContext.getString(R.string.begin_at), yearS + "." + monthS + "." + dayS)); mSelectEndTime = yearS + "-" + monthS + "-" + dayS; mEndTimeTv.setText(makeFormatContent(mContext.getString(R.string.end_at), yearS + "." + monthS + "." + dayS)); } } private void initDialogView () { mTimeContainerLl = (LinearLayout) findViewById(R.id.ll_tclTimeToTime); mBeginTimeTv = (TextView) findViewById(R.id.tv_tclBeginTime); mEndTimeTv = (TextView) findViewById(R.id.tv_tclEndTime); findViewById(R.id.tv_tclCancel).setOnClickListener(this); findViewById(R.id.tv_tclOk).setOnClickListener(this); mBeginTimeTv.setOnClickListener(this); mEndTimeTv.setOnClickListener(this); } /*恢复起始时间按钮的点击状态*/ public void recoverButtonState(){ mTimeType = TIME_TYPE.TYPE_START; init(mSelectStartTime, false); mTimeContainerLl.setBackgroundResource(R.mipmap.begin_time_bg); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.tv_tclBeginTime: mTimeType = TIME_TYPE.TYPE_START; init(mSelectStartTime, false); mTimeContainerLl.setBackgroundResource(R.mipmap.begin_time_bg); break; case R.id.tv_tclEndTime: mTimeType = TIME_TYPE.TYPE_END; init(mSelectEndTime, false); mTimeContainerLl.setBackgroundResource(R.mipmap.end_time_bg); break; case R.id.tv_tclCancel: this.dismiss(); break; case R.id.tv_tclOk: SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd", Locale.CHINA); try { if (sdf.parse(mSelectStartTime).getTime() > sdf.parse(mSelectEndTime).getTime()) { ToastUtils.showText(mContext, R.string.time_start_larger_end_not_allowed, ToastUtils.ONE_SECOND); } else { if (onDateSelectFinished != null) { onDateSelectFinished.onSelectFinished(mSelectStartTime, mSelectEndTime); } this.dismiss(); } } catch (ParseException e) { e.printStackTrace(); } } } public void init(String date, boolean isShowHour) { Calendar calendar = Calendar.getInstance(); curYear = calendar.get(Calendar.YEAR); curMonth = calendar.get(Calendar.MONTH) + 1; curDay = calendar.get(Calendar.DATE); hour = calendar.get(Calendar.HOUR_OF_DAY); minute = calendar.get(Calendar.MINUTE); calendar.clear(); if (date != null) { String[] ymd = date.split("-"); if (ymd.length > 2) { curYear = Integer.parseInt(ymd[0]); curMonth = Integer.parseInt(ymd[1]) - 1; String[] dhm = ymd[2].split(" "); curDay = Integer.parseInt(dhm[0]); if (dhm.length > 1) { String[] hm = dhm[1].split(":"); if (hm.length > 1) { hour = Integer.parseInt(hm[0]); minute = Integer.parseInt(hm[1]); } } } } mYearView = (WheelView) findViewById(R.id.year); mMonthView = (WheelView) findViewById(R.id.month); mDayView = (WheelView) findViewById(R.id.day); mHourView = (WheelView) findViewById(R.id.hour); mMinuteView = (WheelView) findViewById(R.id.minute); if (!isShowHour) { mHourView.setVisibility(View.GONE); mMinuteView.setVisibility(View.GONE); } if (!isShowMinute) { mMinuteView.setVisibility(View.GONE); } /* 年,月,日,时,分 等单位,和值同在 */ findViewById(R.id.tv_yearUnit).setVisibility(mYearView.getVisibility()); findViewById(R.id.tv_monthUnit).setVisibility(mMonthView.getVisibility()); findViewById(R.id.tv_dayUnit).setVisibility(mDayView.getVisibility()); findViewById(R.id.v_dayAndMinute).setVisibility(isShowHour ? View.VISIBLE : View.GONE); findViewById(R.id.tv_hourUnit).setVisibility(mHourView.getVisibility()); findViewById(R.id.tv_minuteUnit).setVisibility(mMinuteView.getVisibility()); initDatePicker(); mYearView.removeChangingListener(yearWheelListener); mMonthView.removeChangingListener(monthWheelListener); mDayView.removeChangingListener(dayWheelListener); mYearView.addChangingListener(yearWheelListener); mMonthView.addChangingListener(monthWheelListener); mDayView.addChangingListener(dayWheelListener); } /** * 弹出日期时间选择器 */ private void initDatePicker() { Calendar calendar = Calendar.getInstance(); year = calendar.get(Calendar.YEAR); month = calendar.get(Calendar.MONTH); day = calendar.get(Calendar.DATE); // END_YEAR = year; String[] ymd = allowedSmallestTime.split("-"); if(TextUtils.isEmpty(allowedBiggestTime)) allowedBiggestTime = TimeUtil.getCurData(); String[] ymdEnd= allowedBiggestTime.split("-"); if (ymd.length > 2) { START_YEAR = Integer.parseInt(ymd[0]); } if(ymdEnd.length>2){ END_YEAR = Integer.parseInt(ymdEnd[0]); month = Integer.parseInt(ymdEnd[1])-1; day= Integer.parseInt(ymdEnd[2]); } // 添加大小月月份并将其转换为list,方便之后的判断 String[] monthsBig = {"1", "3", "5", "7", "8", "10", "12"}; String[] monthsLittle = {"4", "6", "9", "11"}; mListBig = Arrays.asList(monthsBig); mListLittle = Arrays.asList(monthsLittle); // 年 mYearView.setAdapter(new NumericWheelAdapter(START_YEAR, END_YEAR));// 设置"年"的显示数据 mYearView.setLabel("");// 添加文字 int yearPos = isOnlyThisYear ? END_YEAR - START_YEAR : curYear != 0 ? curYear - START_YEAR : END_YEAR - START_YEAR; mYearView.setCurrentItem(yearPos);// 初始化时显示的数据 START_YEAR - END_YEAR mYearView.setCyclic(false);// 循环滚动 // 月 int startMonth = 1; if (isOnlyThisMonth) { startMonth = curMonth + 1; } //初始年份最大值应该是当年最大月 mMonthView.setAdapter(new NumericWheelAdapter(startMonth, year == curYear ? month + 1 : 12)); mMonthView.setLabel(""); mMonthView.setCurrentItem(isOnlyThisMonth ? 0 : curMonth != 0 ? curMonth : month); mMonthView.setCyclic(false); // 日 //判断是否属于当前月份,如果不是,需要判断大小月,进行初始化 if (curMonth < month){ // 判断大小月及是否闰年,用来确定"日"的数据 if (mListBig.contains(String.valueOf(curMonth))) { mDayView.setAdapter(new NumericWheelAdapter(1, 31)); } else if (mListLittle.contains(String.valueOf(curMonth))) { mDayView.setAdapter(new NumericWheelAdapter(1, 30)); } else { if (((mYearView.getCurrentItem() + START_YEAR) % 4 == 0 && (mYearView.getCurrentItem() + START_YEAR) % 100 != 0) || (mYearView.getCurrentItem() + START_YEAR) % 400 == 0) mDayView.setAdapter(new NumericWheelAdapter(1, 29)); else mDayView.setAdapter(new NumericWheelAdapter(1, 28)); } }else { if(mTimeType == TIME_TYPE.TYPE_END){ mDayView.setAdapter(new NumericWheelAdapter(1, Integer.parseInt(ymdEnd[2]))); }else if(mTimeType == TIME_TYPE.TYPE_START){ mDayView.setAdapter(new NumericWheelAdapter(Integer.parseInt(ymd[2]), day)); }else{ mDayView.setAdapter(new NumericWheelAdapter(1, day)); } } mDayView.setLabel(""); mDayView.setCurrentItem(curDay == 0 ? day - 1 : curDay - 1); mDayView.setCyclic(true); // 时 mHourView.setAdapter(new NumericWheelAdapter(0, 23)); mHourView.setLabel(""); mHourView.setCurrentItem(hour); mHourView.setCyclic(true); // 分 mMinuteView.setAdapter(new NumericWheelAdapter(0, 59)); mMinuteView.setLabel(""); mMinuteView.setCurrentItem(minute); mMinuteView.setCyclic(true); // 选择器字体的大小 int textSize = mContext.getResources().getDimensionPixelSize(R.dimen.ymd_text_size); mDayView.TEXT_SIZE = textSize; mMonthView.TEXT_SIZE = textSize; mYearView.TEXT_SIZE = textSize; mHourView.TEXT_SIZE = textSize; mMinuteView.TEXT_SIZE = textSize; } /** * 添加对"年"监听 */ private OnWheelChangedListener yearWheelListener = new OnWheelChangedListener() { public void onChanged(WheelView wheel, int oldValue, int newValue) { int year_num = newValue + START_YEAR; if (year_num < year) { mMonthView.setAdapter(new NumericWheelAdapter(1, 12)); } else if (year_num >= year) { mMonthView.setAdapter(new NumericWheelAdapter(1, month + 1)); } mMonthView.setCurrentItem(0); // 判断大小月及是否闰年,用来确定"日"的数据 if (mListBig.contains(String.valueOf(mMonthView.getCurrentItem() + 1))) { mDayView.setAdapter(new NumericWheelAdapter(1, 31)); } else if (mListLittle.contains(String.valueOf(mMonthView.getCurrentItem() + 1))) { mDayView.setAdapter(new NumericWheelAdapter(1, 30)); } else { if ((year_num % 4 == 0 && year_num % 100 != 0) || year_num % 400 == 0) mDayView.setAdapter(new NumericWheelAdapter(1, 29)); else mDayView.setAdapter(new NumericWheelAdapter(1, 28)); } onScroll(); mMonthView.setCurrentItem(mMonthView.getCurrentItem()); mDayView.setCurrentItem(mDayView.getCurrentItem()); } }; /** * 添加对"月"监听 */ private OnWheelChangedListener monthWheelListener = new OnWheelChangedListener() { public void onChanged(WheelView wheel, int oldValue, int newValue) { int month_num = newValue + 1; if (month_num == (month + 1)) { mDayView.setAdapter(new NumericWheelAdapter(1, day)); } else { // 判断大小月及是否闰年,用来确定"日"的数据 if (mListBig.contains(String.valueOf(month_num))) { mDayView.setAdapter(new NumericWheelAdapter(1, 31)); } else if (mListLittle.contains(String.valueOf(month_num))) { mDayView.setAdapter(new NumericWheelAdapter(1, 30)); } else { if (((mYearView.getCurrentItem() + START_YEAR) % 4 == 0 && (mYearView.getCurrentItem() + START_YEAR) % 100 != 0) || (mYearView.getCurrentItem() + START_YEAR) % 400 == 0) mDayView.setAdapter(new NumericWheelAdapter(1, 29)); else mDayView.setAdapter(new NumericWheelAdapter(1, 28)); } } onScroll(); mDayView.setCurrentItem(mDayView.getCurrentItem()); } }; /** * 添加对 日滚动控件 的添加 */ private OnWheelChangedListener dayWheelListener = new OnWheelChangedListener() { public void onChanged(WheelView wheel, int oldValue, int newValue) { mDayView.setCurrentItem(newValue); onScroll(); } }; private void onScroll() { int year = isOnlyThisYear ? Integer.parseInt(mYearView.getAdapter().getItem(0)) : mYearView.getCurrentItem() + START_YEAR; int month = isOnlyThisMonth ? Integer.parseInt(mMonthView.getAdapter().getItem(0)) : mMonthView.getCurrentItem() + 1; int day = mDayView.getCurrentItem() + 1; String monthS = String.format("%02d", month); String dayS = String.format("%02d", day); String yearS = String.format("%02d", year); if (mTimeType == TIME_TYPE.TYPE_START) { mSelectStartTime = yearS + "-" + monthS + "-" + dayS; mBeginTimeTv.setText(makeFormatContent(mContext.getString(R.string.begin_at), yearS + "." + monthS + "." + dayS)); } else { mSelectEndTime = yearS + "-" + monthS + "-" + dayS; mEndTimeTv.setText(makeFormatContent(mContext.getString(R.string.end_at), yearS + "." + monthS + "." + dayS)); } } /** * 格式化显示的数据,必须返回SpannableString对象 * * @param priFix 前缀 * @param content 内容 * @return 返回格式化的数据 */ private SpannableString makeFormatContent(String priFix, String content){ SpannableString spannableString = new SpannableString(priFix + content); spannableString.setSpan(new ForegroundColorSpan(mContext.getResources().getColor(R.color.black33)), priFix.length(), spannableString.length(), Spanned.SPAN_EXCLUSIVE_INCLUSIVE); spannableString.setSpan(new RelativeSizeSpan(1.33f), priFix.length(), spannableString.length(), Spanned.SPAN_EXCLUSIVE_INCLUSIVE); return spannableString; } /** * set监听 * * @param onDateSelectFinished 完成监听 */ public void setOnDateSelectFinished(OnDateSelectFinished onDateSelectFinished) { this.onDateSelectFinished = onDateSelectFinished; } /** * 监听接口 */ public interface OnDateSelectFinished { /** * 监听方法 * * @param startTime 开始时间 * @param endTime 结束时间 */ void onSelectFinished(String startTime, String endTime); } }
popwindow_bottom_layout.xml
xml version="1.0" encoding="utf-8"?>xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/withe" android:orientation="vertical"> android:layout_width="match_parent" android:layout_height="64dp" android:gravity="center_vertical" android:padding="12dp"> android:id="@+id/tv_tclCancel" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" android:text="@string/cancel" android:textColor="@color/black99" android:textSize="17sp" /> android:id="@+id/tv_tclOk" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentEnd="true" android:layout_alignParentRight="true" android:layout_centerVertical="true" android:text="@string/ok" android:textColor="@color/blue_bg" android:textSize="17sp" /> android:id="@+id/ll_tclTimeToTime" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@mipmap/begin_time_bg"> android:id="@+id/tv_tclBeginTime" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:gravity="center_vertical" android:paddingLeft="40dp" android:text="结束于\n2017.05.08" /> android:id="@+id/tv_tclEndTime" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:gravity="center_vertical" android:paddingLeft="40dp" android:text="结束于\n2017.05.08" /> android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center"> android:id="@+id/view_dataSelect" layout="@layout/data_time_layout" />
data_time_layout.xml
xml version="1.0" encoding="utf-8"?>xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/tv_time_title" android:layout_centerHorizontal="true" android:layout_marginBottom="12dp" android:layout_marginTop="12dp" android:orientation="horizontal"> android:id="@+id/year" android:layout_width="92dp" android:layout_height="wrap_content" android:layout_weight="0.2" /> android:id="@+id/tv_yearUnit" android:layout_width="wrap_content" android:layout_height="match_parent" android:gravity="center_vertical" android:text="年" /> android:layout_width="1px" android:layout_height="fill_parent" android:background="#e8e8e8" /> android:id="@+id/month" android:layout_width="92dp" android:layout_height="wrap_content" android:layout_weight="0.2" /> android:id="@+id/tv_monthUnit" android:layout_width="wrap_content" android:layout_height="match_parent" android:gravity="center_vertical" android:text="月" /> android:id="@+id/v_monthAndDay" android:layout_width="1px" android:layout_height="fill_parent" android:background="#e8e8e8" /> android:id="@+id/day" android:layout_width="92dp" android:layout_height="wrap_content" android:layout_weight="0.2" /> android:id="@+id/tv_dayUnit" android:layout_width="wrap_content" android:layout_height="match_parent" android:gravity="center_vertical" android:text="日" /> android:id="@+id/v_dayAndHour" android:layout_width="1px" android:layout_height="fill_parent" android:background="#e8e8e8" /> android:id="@+id/hour" android:layout_width="92dp" android:layout_height="wrap_content" android:layout_weight="0.2" /> android:id="@+id/tv_hourUnit" android:layout_width="wrap_content" android:layout_height="match_parent" android:gravity="center_vertical" android:text="时" /> android:id="@+id/v_dayAndMinute" android:layout_width="1px" android:layout_height="fill_parent" android:background="#e8e8e8" /> android:id="@+id/minute" android:layout_width="92dp" android:layout_height="wrap_content" android:layout_weight="0.2" /> android:id="@+id/tv_minuteUnit" android:layout_width="wrap_content" android:layout_height="match_parent" android:gravity="center_vertical" android:text="分" />
关于WheelView后面再说,时间控件常见的轮子
弹框样式
弹框动画
popwindow_enter
xml version="1.0" encoding="utf-8"?>popwindow_exitxmlns:android="http://schemas.android.com/apk/res/android"> android:duration="200" android:fromYDelta="100%" android:toYDelta="0" />
xml version="1.0" encoding="utf-8"?>关于时间滑动的逻辑处理和一般时间控件一样xmlns:android="http://schemas.android.com/apk/res/android"> android:duration="200" android:fromYDelta="0" android:toYDelta="100%" />
(这里先这么写,后面补上)
最后使用
一个方法:
public void showCustomTimePicker() { String beginDeadTime = "2017-01-01"; if (mDoubleTimeSelectDialog == null) { mDoubleTimeSelectDialog = new DoubleTimeSelectDialog(this, beginDeadTime, defaultWeekBegin, defaultWeekEnd); mDoubleTimeSelectDialog.setOnDateSelectFinished(new DoubleTimeSelectDialog.OnDateSelectFinished() { @Override public void onSelectFinished(String startTime, String endTime) { ui_button1.setText(startTime.replace("-", ".") + "至\n" + endTime.replace("-", ".")); } }); mDoubleTimeSelectDialog.setOnDismissListener(new DialogInterface.OnDismissListener() { @Override public void onDismiss(DialogInterface dialog) { } }); } if (!mDoubleTimeSelectDialog.isShowing()) { mDoubleTimeSelectDialog.recoverButtonState(); mDoubleTimeSelectDialog.show(); } }
原计划写篇Android时间控件总结,后发现以这篇开头不大合适,先这样,后面补上
四丶跪求关注下载源码,200粉小目标
github开源代码分享,原文链接见上文
源码下载记得顺便Star哦~
下载链接:https://github.com/JinBoy23520/CoderToDeveloperByTCLer
1.修复楼下问题ChenYao_0407
问题分析:之前只做了2017没跨年的情况,年选择时间没有带到月监听里,导致2017年月份,依然无法当天对应月份
解决办法:将年选择带到月份选择里
步骤:
(1)添加参数
/** * 选择年 */ private int choiceYearView;
(2)在年选择监听赋值
int year_num = newValue + START_YEAR; choiceYearView = year_num;
(3)在月监听添加判断
if (month_num == (month + 1) && choiceYearView == year) { mDayView.setAdapter(new NumericWheelAdapter(1, day)); } else {OK,问题解决