irpas技术客

Android 类似Ios的时间滚轮控件_liuhaha1015_android 滚轮控件

网络 1915

之前项目使用的,参考了别人的一个demo,但是原文链接找不到了。。。。

先说如何使用:

//参数1:上下文 //参数2:选中是的ResultHandler回调 //参数3:选择器最早时间 //参数4:选择器的最晚时间(本文用的当前时间) //参数5:选择器的默认展示时间 val currentCalender = Calendar.getInstance() val currentDate = DateUtil.format(currentCalender.getTime(), "yyyy/MM/dd") val timeSelector2 = TimeSelector(this,object :TimeSelector.ResultHandler{ override fun handle(time: String?) { birthday_tv.setText(time) } },"1971/01/01", currentDate, "2001/01/01") //需要显示可选择的小时数与分钟数时,在后面增加对应的两个参数。这时选择器的小时与分钟也会自动显示 val timeSelector2 = TimeSelector(this,object :TimeSelector.ResultHandler{ override fun handle(time: String?) { birthday_tv.setText(time) } },"1971/01/01", currentDate, "2001/01/01","08:30","20:30") timeSelector.setMode(TimeSelector.MODE.YMD) timeSelector.show()

滚动控件是自定义的view,PickerView:

R.styleable.PickerView public class PickerView extends View { /** * 新增字段 控制是否首尾相接循环显示 默认为循环显示 */ private boolean loop = true; /** * text之间间距和minTextSize之比 */ public static final float MARGIN_ALPHA = 2.8f; /** * 自动回滚到中间的速度 */ public static final float SPEED = 10; private List<String> mDataList; /** * 选中的位置,这个位置是mDataList的中心位置,一直不变 */ private int mCurrentSelected; private Paint mPaint, nPaint; private float mMaxTextSize = 80; private float mMinTextSize = 40; private float mMaxTextAlpha = 255; private float mMinTextAlpha = 120; private int mColorText = 0x333333; private int nColorText = 0x666666; private int mViewHeight; private int mViewWidth; private float mLastDownY; /** * 滑动的距离 */ private float mMoveLen = 0; private boolean isInit = false; private onSelectListener mSelectListener; private Timer timer; private MyTimerTask mTask; Handler updateHandler = new Handler() { @Override public void handleMessage(Message msg) { if (Math.abs(mMoveLen) < SPEED) { mMoveLen = 0; if (mTask != null) { mTask.cancel(); mTask = null; performSelect(); } } else // 这里mMoveLen / Math.abs(mMoveLen)是为了保有mMoveLen的正负号,以实现上滚或下滚 mMoveLen = mMoveLen - mMoveLen / Math.abs(mMoveLen) * SPEED; invalidate(); } }; private boolean canScroll = true; public PickerView(Context context) { super(context); init(); } public PickerView(Context context, AttributeSet attrs) { super(context, attrs); TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.PickerView); loop = typedArray.getBoolean(R.styleable.PickerView_isLoop, loop); init(); } public void setOnSelectListener(onSelectListener listener) { mSelectListener = listener; } private void performSelect() { if (mSelectListener != null) mSelectListener.onSelect(mDataList.get(mCurrentSelected)); } public void setData(List<String> datas) { mDataList = datas; mCurrentSelected = datas.size() / 4; invalidate(); } /** * 选择选中的item的index * * @param selected */ public void setSelected(int selected) { mCurrentSelected = selected; if (loop) { int distance = mDataList.size() / 2 - mCurrentSelected; if (distance < 0) for (int i = 0; i < -distance; i++) { moveHeadToTail(); mCurrentSelected--; } else if (distance > 0) for (int i = 0; i < distance; i++) { moveTailToHead(); mCurrentSelected++; } } invalidate(); } /** * 选择选中的内容 * * @param mSelectItem */ public void setSelected(String mSelectItem) { for (int i = 0; i < mDataList.size(); i++) if (mDataList.get(i).equals(mSelectItem)) { setSelected(i); break; } } private void moveHeadToTail() { if (loop) { String head = mDataList.get(0); mDataList.remove(0); mDataList.add(head); } } private void moveTailToHead() { if (loop) { String tail = mDataList.get(mDataList.size() - 1); mDataList.remove(mDataList.size() - 1); mDataList.add(0, tail); } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); mViewHeight = getMeasuredHeight(); mViewWidth = getMeasuredWidth(); // 按照View的高度计算字体大小 mMaxTextSize = mViewHeight / 7f; mMinTextSize = mMaxTextSize / 2.2f; isInit = true; invalidate(); } private void init() { timer = new Timer(); mDataList = new ArrayList<String>(); //第一个paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mPaint.setStyle(Style.FILL); mPaint.setTextAlign(Align.CENTER); mPaint.setColor(getResources().getColor(R.color.main_color)); //第二个paint nPaint = new Paint(Paint.ANTI_ALIAS_FLAG); nPaint.setStyle(Style.FILL); nPaint.setTextAlign(Align.CENTER); nPaint.setColor(mColorText); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); // 根据index绘制view if (isInit) drawData(canvas); } private void drawData(Canvas canvas) { // 先绘制选中的text再往上往下绘制其余的text float scale = parabola(mViewHeight / 4.0f, mMoveLen); float size = (mMaxTextSize - mMinTextSize) * scale + mMinTextSize; mPaint.setTextSize(size); mPaint.setAlpha((int) ((mMaxTextAlpha - mMinTextAlpha) * scale + mMinTextAlpha)); // text居中绘制,注意baseline的计算才能达到居中,y值是text中心坐标 float x = (float) (mViewWidth / 2.0); float y = (float) (mViewHeight / 2.0 + mMoveLen); FontMetricsInt fmi = mPaint.getFontMetricsInt(); float baseline = (float) (y - (fmi.bottom / 2.0 + fmi.top / 2.0)); canvas.drawText(mDataList.get(mCurrentSelected), x, baseline, mPaint); // 绘制上方data for (int i = 1; (mCurrentSelected - i) >= 0; i++) { drawOtherText(canvas, i, -1); } // 绘制下方data for (int i = 1; (mCurrentSelected + i) < mDataList.size(); i++) { drawOtherText(canvas, i, 1); } } /** * @param canvas * @param position 距离mCurrentSelected的差值 * @param type 1表示向下绘制,-1表示向上绘制 */ private void drawOtherText(Canvas canvas, int position, int type) { float d = (float) (MARGIN_ALPHA * mMinTextSize * position + type * mMoveLen); float scale = parabola(mViewHeight / 4.0f, d); float size = (mMaxTextSize - mMinTextSize) * scale + mMinTextSize; nPaint.setTextSize(size); nPaint.setAlpha((int) ((mMaxTextAlpha - mMinTextAlpha) * scale + mMinTextAlpha)); float y = (float) (mViewHeight / 2.0 + type * d); FontMetricsInt fmi = nPaint.getFontMetricsInt(); float baseline = (float) (y - (fmi.bottom / 2.0 + fmi.top / 2.0)); canvas.drawText(mDataList.get(mCurrentSelected + type * position), (float) (mViewWidth / 2.0), baseline, nPaint); } /** * 抛物线 * * @param zero 零点坐标 * @param x 偏移量 * @return scale */ private float parabola(float zero, float x) { float f = (float) (1 - Math.pow(x / zero, 2)); return f < 0 ? 0 : f; } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getActionMasked()) { case MotionEvent.ACTION_DOWN: doDown(event); break; case MotionEvent.ACTION_MOVE: mMoveLen += (event.getY() - mLastDownY); if (mMoveLen > MARGIN_ALPHA * mMinTextSize / 2) { if (!loop && mCurrentSelected == 0) { mLastDownY = event.getY(); invalidate(); return true; } if (!loop) mCurrentSelected--; // 往下滑超过离开距离 moveTailToHead(); mMoveLen = mMoveLen - MARGIN_ALPHA * mMinTextSize; } else if (mMoveLen < -MARGIN_ALPHA * mMinTextSize / 2) { if (mCurrentSelected == mDataList.size() - 1) { mLastDownY = event.getY(); invalidate(); return true; } if (!loop) mCurrentSelected++; // 往上滑超过离开距离 moveHeadToTail(); mMoveLen = mMoveLen + MARGIN_ALPHA * mMinTextSize; } mLastDownY = event.getY(); invalidate(); break; case MotionEvent.ACTION_UP: doUp(event); break; } return true; } private void doDown(MotionEvent event) { if (mTask != null) { mTask.cancel(); mTask = null; } mLastDownY = event.getY(); } // private void doMove(MotionEvent event) { // // mMoveLen += (event.getY() - mLastDownY); // // if (mMoveLen > MARGIN_ALPHA * mMinTextSize / 2) { // // 往下滑超过离开距离 // moveTailToHead(); // mMoveLen = mMoveLen - MARGIN_ALPHA * mMinTextSize; // } else if (mMoveLen < -MARGIN_ALPHA * mMinTextSize / 2) { // // 往上滑超过离开距离 // moveHeadToTail(); // mMoveLen = mMoveLen + MARGIN_ALPHA * mMinTextSize; // } // // mLastDownY = event.getY(); // invalidate(); // } private void doUp(MotionEvent event) { // 抬起手后mCurrentSelected的位置由当前位置move到中间选中位置 if (Math.abs(mMoveLen) < 0.0001) { mMoveLen = 0; return; } if (mTask != null) { mTask.cancel(); mTask = null; } mTask = new MyTimerTask(updateHandler); timer.schedule(mTask, 0, 10); } class MyTimerTask extends TimerTask { Handler handler; public MyTimerTask(Handler handler) { this.handler = handler; } @Override public void run() { handler.sendMessage(handler.obtainMessage()); } } public interface onSelectListener { void onSelect(String text); } public void setCanScroll(boolean canScroll) { this.canScroll = canScroll; } @Override public boolean dispatchTouchEvent(MotionEvent event) { if (canScroll) return super.dispatchTouchEvent(event); else return false; } /** * 新增字段 控制内容是否首尾相连 * by liuli * * @param isLoop */ public void setIsLoop(boolean isLoop) { loop = isLoop; } }

R.styleable.PickerView: <?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="PickerView"> <attr name="isLoop" format="boolean" /> </declare-styleable> </resources>

?主要操作类是:TimeSelector: ?

public class TimeSelector { public interface ResultHandler { void handle(String time); } public enum SCROLLTYPE { HOUR(1), MINUTE(2); private SCROLLTYPE(int value) { this.value = value; } public int value; } /** * 模式,一种只显示年月日,一种显示年月日时分 */ public enum MODE { YMD(1), YMDHM(2); private MODE(int value) { this.value = value; } public int value; } private int scrollUnits = SCROLLTYPE.HOUR.value + SCROLLTYPE.MINUTE.value;//3 private ResultHandler handler; private Context context; // private final String FORMAT_STR = "yyyy-MM-dd HH:mm"; private final String FORMAT_STR = "yyyy/MM/dd"; private final String RESULT_FORMAT_STR = "dd/MM/yyyy"; private final String YEAR_FORMAT_STR = "yyyy"; private final String MONTH_FORMAT_STR = "MM"; private final String DAY_FORMAT_STR = "DD"; private Dialog seletorDialog;//选择日期与时间弹框 private PickerView year_pv;//控件 年 private PickerView month_pv;//控件 月 private PickerView day_pv;//控件 天 private PickerView hour_pv;//控件 时 private PickerView minute_pv;//控件 分 private final int MAXMINUTE = 59;//最大分钟数 private int MAXHOUR = 23;//最大小时数(结束时间小时数) private final int MINMINUTE = 0;//最小分钟数 private int MINHOUR = 0;//最小小时数(开始时间小时数) private final int MAXMONTH = 12;//最大月份数 private ArrayList<String> year, month, day, hour, minute;//根据设定的开始日期时间、结束日期时间 存储年月日时分的ArrayList //开始年、月、时、分 结束年、月、时、分 private int startYear, startMonth, startDay, startHour, startMininute, endYear, endMonth, endDay, endHour, endMininute, minute_workStart, minute_workEnd, hour_workStart, hour_workEnd; private boolean spanYear, spanMon, spanDay, spanHour, spanMin;//年月日时分是否是时间跨度 private Calendar selectedCalender = Calendar.getInstance();//获取选中的时间日历 private final long ANIMATORDELAY = 200L;//延时动画 private final long CHANGEDELAY = 90L;//修改延时 private String workStart_str;//开始时间 private String workEnd_str;//结束时间 private Calendar startCalendar;//开始日期日历 private Calendar endCalendar;//结束日期日历 private TextView tv_cancle;//取消按钮 private TextView tv_select, tv_title;//选择按钮、弹框标题 private TextView hour_text;//“时” 字样 private TextView minute_text;//“分” 字样 private Calendar defaultCalendar;//默认选择的日历 public TimeSelector(Context context, ResultHandler resultHandler, String startDate, String endDate, String defaultDate) { this.context = context; this.handler = resultHandler; startCalendar = Calendar.getInstance(); endCalendar = Calendar.getInstance(); defaultCalendar = Calendar.getInstance(); startCalendar.setTime(DateUtil.parse(startDate, FORMAT_STR));//开始日期 endCalendar.setTime(DateUtil.parse(endDate, FORMAT_STR));//结束日期 defaultCalendar.setTime(DateUtil.parse(defaultDate, FORMAT_STR));//默认选择日期 initDialog(); initView(); } public TimeSelector(Context context, ResultHandler resultHandler, String startDate, String endDate, String defaultDate, String workStartTime, String workEndTime) { this(context, resultHandler, startDate, endDate,defaultDate); this.workStart_str = workStartTime; this.workEnd_str = workEndTime; } /** * 外部调用,弹出时间选择dialog */ public void show() { if (startCalendar.getTime().getTime() >= endCalendar.getTime().getTime()) { Toast.makeText(context, "start>end", Toast.LENGTH_SHORT).show(); return; } if (defaultCalendar.getTime().getTime() < startCalendar.getTime().getTime()) { Toast.makeText(context, "default<start", Toast.LENGTH_SHORT).show(); return; } if (defaultCalendar.getTime().getTime() > endCalendar.getTime().getTime()) { Toast.makeText(context, "default>end", Toast.LENGTH_SHORT).show(); return; } if (!excuteWorkTime()) return; initParameter(); initTimer(); addListener(); seletorDialog.show(); } /** * 初始化日期时间选择弹框 */ private void initDialog() { if (seletorDialog == null) { seletorDialog = new Dialog(context, R.style.time_dialog); seletorDialog.setCancelable(false); seletorDialog.requestWindowFeature(Window.FEATURE_NO_TITLE); seletorDialog.setContentView(R.layout.dlg_selector); Window window = seletorDialog.getWindow(); window.setGravity(Gravity.BOTTOM); WindowManager.LayoutParams lp = window.getAttributes(); //宽度为屏幕的宽 int width = ScreenUtil.getInstance(context).getScreenWidth(); lp.width = width; window.setAttributes(lp); } } /** * 初始化控件id,设置点击事件 */ private void initView() { year_pv = (PickerView) seletorDialog.findViewById(R.id.year_pv); month_pv = (PickerView) seletorDialog.findViewById(R.id.month_pv); day_pv = (PickerView) seletorDialog.findViewById(R.id.day_pv); hour_pv = (PickerView) seletorDialog.findViewById(R.id.hour_pv); minute_pv = (PickerView) seletorDialog.findViewById(R.id.minute_pv); tv_cancle = (TextView) seletorDialog.findViewById(R.id.tv_cancle); tv_select = (TextView) seletorDialog.findViewById(R.id.tv_select); tv_title = (TextView) seletorDialog.findViewById(R.id.tv_title); hour_text = (TextView) seletorDialog.findViewById(R.id.hour_text); minute_text = (TextView) seletorDialog.findViewById(R.id.minute_text); tv_cancle.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { seletorDialog.dismiss(); } }); tv_select.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { handler.handle(DateUtil.format(selectedCalender.getTime(), RESULT_FORMAT_STR)); seletorDialog.dismiss(); } }); } /** * 此时的startCalendar、endCalendar都是经过检测是,正确时间,来分别获取年月日时分 */ private void initParameter() { startYear = startCalendar.get(Calendar.YEAR); startMonth = startCalendar.get(Calendar.MONTH) + 1; startDay = startCalendar.get(Calendar.DAY_OF_MONTH); startHour = startCalendar.get(Calendar.HOUR_OF_DAY); startMininute = startCalendar.get(Calendar.MINUTE); endYear = endCalendar.get(Calendar.YEAR); endMonth = endCalendar.get(Calendar.MONTH) + 1; endDay = endCalendar.get(Calendar.DAY_OF_MONTH); endHour = endCalendar.get(Calendar.HOUR_OF_DAY); endMininute = endCalendar.get(Calendar.MINUTE); spanYear = startYear != endYear;// spanMon = (!spanYear) && (startMonth != endMonth);//???年等 月不等 spanDay = (!spanMon) && (startDay != endDay);//???月等 日不等 spanHour = (!spanDay) && (startHour != endHour); spanMin = (!spanHour) && (startMininute != endMininute); // selectedCalender.setTime(startCalendar.getTime()); //-------------------------------------- // startYear = defaultCalendar.get(Calendar.YEAR); // startMonth = defaultCalendar.get(Calendar.MONTH) + 1; // startDay = defaultCalendar.get(Calendar.DAY_OF_MONTH); // startHour = defaultCalendar.get(Calendar.HOUR_OF_DAY); // startMininute = defaultCalendar.get(Calendar.MINUTE); //设置初始化时默认选择时间 selectedCalender.setTime(defaultCalendar.getTime()); } /** * ??? * 根据年月日时分开始结束时间,造数据 * */ private void initTimer() { initArrayList(); if (spanYear) {//分别向年月日时分list里添加数据 for (int i = startYear; i <= endYear; i++) { year.add(String.valueOf(i)); } for (int i = startMonth; i <= MAXMONTH; i++) {//<12 month.add(fomatTimeUnit(i)); } for (int i = startDay; i <= startCalendar.getActualMaximum(Calendar.DAY_OF_MONTH); i++) {//startCalendar.getActualMaximum(Calendar.DAY_OF_MONTH) startCalendar中月份的最大值 day.add(fomatTimeUnit(i)); } if ((scrollUnits & SCROLLTYPE.HOUR.value) != SCROLLTYPE.HOUR.value) {//(0 & 1) !=1 hour.add(fomatTimeUnit(startHour)); } else { for (int i = startHour; i <= MAXHOUR; i++) {//MAXHOUR 开始时间的小时数 hour.add(fomatTimeUnit(i)); } } if ((scrollUnits & SCROLLTYPE.MINUTE.value) != SCROLLTYPE.MINUTE.value) {//(0 & 2) !=1 minute.add(fomatTimeUnit(startMininute)); } else { for (int i = startMininute; i <= MAXMINUTE; i++) { minute.add(fomatTimeUnit(i)); } } } else if (spanMon) { year.add(String.valueOf(startYear)); for (int i = startMonth; i <= endMonth; i++) { month.add(fomatTimeUnit(i)); } for (int i = startDay; i <= startCalendar.getActualMaximum(Calendar.DAY_OF_MONTH); i++) { day.add(fomatTimeUnit(i)); } if ((scrollUnits & SCROLLTYPE.HOUR.value) != SCROLLTYPE.HOUR.value) { hour.add(fomatTimeUnit(startHour)); } else { for (int i = startHour; i <= MAXHOUR; i++) { hour.add(fomatTimeUnit(i)); } } if ((scrollUnits & SCROLLTYPE.MINUTE.value) != SCROLLTYPE.MINUTE.value) { minute.add(fomatTimeUnit(startMininute)); } else { for (int i = startMininute; i <= MAXMINUTE; i++) { minute.add(fomatTimeUnit(i)); } } } else if (spanDay) { year.add(String.valueOf(startYear)); month.add(fomatTimeUnit(startMonth)); for (int i = startDay; i <= endDay; i++) { day.add(fomatTimeUnit(i)); } if ((scrollUnits & SCROLLTYPE.HOUR.value) != SCROLLTYPE.HOUR.value) { hour.add(fomatTimeUnit(startHour)); } else { for (int i = startHour; i <= MAXHOUR; i++) { hour.add(fomatTimeUnit(i)); } } if ((scrollUnits & SCROLLTYPE.MINUTE.value) != SCROLLTYPE.MINUTE.value) { minute.add(fomatTimeUnit(startMininute)); } else { for (int i = startMininute; i <= MAXMINUTE; i++) { minute.add(fomatTimeUnit(i)); } } } else if (spanHour) { year.add(String.valueOf(startYear)); month.add(fomatTimeUnit(startMonth)); day.add(fomatTimeUnit(startDay)); if ((scrollUnits & SCROLLTYPE.HOUR.value) != SCROLLTYPE.HOUR.value) { hour.add(fomatTimeUnit(startHour)); } else { for (int i = startHour; i <= endHour; i++) { hour.add(fomatTimeUnit(i)); } } if ((scrollUnits & SCROLLTYPE.MINUTE.value) != SCROLLTYPE.MINUTE.value) { minute.add(fomatTimeUnit(startMininute)); } else { for (int i = startMininute; i <= MAXMINUTE; i++) { minute.add(fomatTimeUnit(i)); } } } else if (spanMin) { year.add(String.valueOf(startYear)); month.add(fomatTimeUnit(startMonth)); day.add(fomatTimeUnit(startDay)); hour.add(fomatTimeUnit(startHour)); if ((scrollUnits & SCROLLTYPE.MINUTE.value) != SCROLLTYPE.MINUTE.value) { minute.add(fomatTimeUnit(startMininute)); } else { for (int i = startMininute; i <= endMininute; i++) { minute.add(fomatTimeUnit(i)); } } } loadComponent(); } /** * 主要为startCalendar、endCalendar设置正确与结束的日期、时间 * 并根据startCalendar、endCalendar设置MINHOUR与MAXHOUR * @return */ private boolean excuteWorkTime() { boolean res = true; //如果设置了开始时间与结束时间 if (!TextUtil.isEmpty(workStart_str) && !TextUtil.isEmpty(workEnd_str)) { String[] start = workStart_str.split(":");//":"分割时间 String[] end = workEnd_str.split(":"); hour_workStart = Integer.parseInt(start[0]);//开始时间的小时 minute_workStart = Integer.parseInt(start[1]);//开始时间的分钟 hour_workEnd = Integer.parseInt(end[0]);//结束时间的小时 minute_workEnd = Integer.parseInt(end[1]);//结束时间的分钟 //1971-01-01T08:00:00.000+0800 此时的workStartCalendar是有小时和分钟的,但startCalendar不一定有,因为在只设置日期不设置时间时不会有小时和分钟 Calendar workStartCalendar = Calendar.getInstance(); workStartCalendar.setTime(startCalendar.getTime());//使用日历,获取初始化时设置开始的日期,再设置给workStartCalendar workStartCalendar.set(Calendar.HOUR_OF_DAY, hour_workStart);//根据获取的开始时间的小时数,设置小时 workStartCalendar.set(Calendar.MINUTE, minute_workStart);//根据获取的开始时间的分钟数,设置分钟 //2022-03-18T20:00:00.000+0800 此时的workEndCalendar是有小时和分钟的,但endCalendar不一定有,因为在只设置日期不设置时间时不会有小时和分钟 Calendar workEndCalendar = Calendar.getInstance(); workEndCalendar.setTime(endCalendar.getTime());//使用日历,获取初始化时设置结束的日期,再设置给workEndCalendar workEndCalendar.set(Calendar.HOUR_OF_DAY, hour_workEnd);//根据获取的结束时间的小时数,设置小时 workEndCalendar.set(Calendar.MINUTE, minute_workEnd);//根据获取的结束时间的分钟数,设置分钟 Calendar startTime = Calendar.getInstance(); Calendar endTime = Calendar.getInstance(); Calendar startWorkTime = Calendar.getInstance(); Calendar endWorkTime = Calendar.getInstance(); //这时的startCalendar、endCalendar 如果没有小时分钟的话,startTime、endTime的小时分钟也是0 startTime.set(Calendar.HOUR_OF_DAY, startCalendar.get(Calendar.HOUR_OF_DAY)); startTime.set(Calendar.MINUTE, startCalendar.get(Calendar.MINUTE)); endTime.set(Calendar.HOUR_OF_DAY, endCalendar.get(Calendar.HOUR_OF_DAY)); endTime.set(Calendar.MINUTE, endCalendar.get(Calendar.MINUTE)); //走到这时,workStartCalendar、workEndCalendar一般都是有小时分钟的,赋值给startWorkTime startWorkTime.set(Calendar.HOUR_OF_DAY, workStartCalendar.get(Calendar.HOUR_OF_DAY)); startWorkTime.set(Calendar.MINUTE, workStartCalendar.get(Calendar.MINUTE)); endWorkTime.set(Calendar.HOUR_OF_DAY, workEndCalendar.get(Calendar.HOUR_OF_DAY)); endWorkTime.set(Calendar.MINUTE, workEndCalendar.get(Calendar.MINUTE)); //比较毫秒值~~不正常的情况 startTime与endTime是否相同 startWorkTime是否小于startTime endWorkTime是否小于startTime if (startTime.getTime().getTime() == endTime.getTime().getTime() || (startWorkTime.getTime().getTime() < startTime.getTime().getTime() && endWorkTime.getTime().getTime() < startTime.getTime().getTime())) { Toast.makeText(context, "Wrong parames!", Toast.LENGTH_LONG).show(); return false; } //比较毫秒值~~正常的情况 startCalendar比workStartCalendar 毫秒值小 取大的?? 1971-01-01T00:00:00.000+0800 < 1971-01-01T08:00:00.000+0800 startCalendar.setTime(startCalendar.getTime().getTime() < workStartCalendar.getTime().getTime() ? workStartCalendar.getTime() : startCalendar.getTime()); //endCalendar比workEndCalendar 毫秒值小 取小的?? 2022-03-18T00:00:00.000+0800 >2022-03-18T20:00:00.000+0800 endCalendar.setTime(endCalendar.getTime().getTime() > workEndCalendar.getTime().getTime() ? workEndCalendar.getTime() : endCalendar.getTime()); MINHOUR = workStartCalendar.get(Calendar.HOUR_OF_DAY); MAXHOUR = workEndCalendar.get(Calendar.HOUR_OF_DAY); } return res; } /** * 格式月日,时分,单位补0 * @param unit * @return */ private String fomatTimeUnit(int unit) { return unit < 10 ? "0" + String.valueOf(unit) : String.valueOf(unit); } /** * 初始化年月日时分list */ private void initArrayList() { if (year == null) year = new ArrayList<>(); if (month == null) month = new ArrayList<>(); if (day == null) day = new ArrayList<>(); if (hour == null) hour = new ArrayList<>(); if (minute == null) minute = new ArrayList<>(); year.clear(); month.clear(); day.clear(); hour.clear(); minute.clear(); } /** * 初始化各个PickerView监听 */ private void addListener() { year_pv.setOnSelectListener(new PickerView.onSelectListener() { @Override public void onSelect(String text) { selectedCalender.set(Calendar.YEAR, Integer.parseInt(text));//根据选择的年份设置selectedCalender monthChange(); } }); month_pv.setOnSelectListener(new PickerView.onSelectListener() { @Override public void onSelect(String text) { selectedCalender.set(Calendar.DAY_OF_MONTH, 1); selectedCalender.set(Calendar.MONTH, Integer.parseInt(text) - 1);//月份的特殊性要-1 dayChange(); } }); day_pv.setOnSelectListener(new PickerView.onSelectListener() { @Override public void onSelect(String text) { selectedCalender.set(Calendar.DAY_OF_MONTH, Integer.parseInt(text)); hourChange(); } }); hour_pv.setOnSelectListener(new PickerView.onSelectListener() { @Override public void onSelect(String text) { selectedCalender.set(Calendar.HOUR_OF_DAY, Integer.parseInt(text)); minuteChange(); } }); minute_pv.setOnSelectListener(new PickerView.onSelectListener() { @Override public void onSelect(String text) { selectedCalender.set(Calendar.MINUTE, Integer.parseInt(text)); } }); } /** * 像年月日时分的PickerView 设置数据 * 可以都设置为开始时间, * 但如果selectedCalender 不为空时,根据selectedCalender 的年月日 设置PickerView默认显示的年月日时分 */ private void loadComponent() { // year_pv.setSelected(0); // month_pv.setSelected(0); // day_pv.setSelected(0); // hour_pv.setSelected(0); // minute_pv.setSelected(0); year_pv.setData(year); month_pv.setData(month); day_pv.setData(day); hour_pv.setData(hour); minute_pv.setData(minute); String yearStr = DateUtil.format(selectedCalender.getTime(), YEAR_FORMAT_STR); String monthStr = DateUtil.format(selectedCalender.getTime(), MONTH_FORMAT_STR); String dayStr = DateUtil.format(selectedCalender.getTime(), DAY_FORMAT_STR); if(year.contains(yearStr)){ int index = year.indexOf(yearStr); year_pv.setSelected(index); }else{ year_pv.setSelected(0); } if(month.contains(monthStr)){ int index = month.indexOf(monthStr); month_pv.setSelected(index); }else{ month_pv.setSelected(0); } if(day.contains(dayStr)){ int index = day.indexOf(dayStr); day_pv.setSelected(index); }else{ day_pv.setSelected(0); } hour_pv.setSelected(0); minute_pv.setSelected(0); excuteScroll(); } private void excuteScroll() { year_pv.setCanScroll(year.size() > 1); month_pv.setCanScroll(month.size() > 1); day_pv.setCanScroll(day.size() > 1); hour_pv.setCanScroll(hour.size() > 1 && (scrollUnits & SCROLLTYPE.HOUR.value) == SCROLLTYPE.HOUR.value); minute_pv.setCanScroll(minute.size() > 1 && (scrollUnits & SCROLLTYPE.MINUTE.value) == SCROLLTYPE.MINUTE.value); } /** * 处理月份变化的数据 */ private void monthChange() { month.clear(); int selectedYear = selectedCalender.get(Calendar.YEAR); if (selectedYear == startYear) {//选择的年份是开始年份,则从开始月份重置monthList for (int i = startMonth; i <= MAXMONTH; i++) { month.add(fomatTimeUnit(i)); } } else if (selectedYear == endYear) {//选择的年份是结束年份,则从开始月份重置monthList for (int i = 1; i <= endMonth; i++) { month.add(fomatTimeUnit(i)); } } else {//既不是开始年也不是结束年,就按12个月处理 for (int i = 1; i <= MAXMONTH; i++) { month.add(fomatTimeUnit(i)); } } selectedCalender.set(Calendar.MONTH, Integer.parseInt(month.get(0)) - 1);//设置selectedCalender的月份,因为Calendar.MONTH的特殊之处,不能直接使用Integer.parseInt(month.get(0)),要-1 month_pv.setData(month); month_pv.setSelected(0); excuteAnimator(ANIMATORDELAY, month_pv); month_pv.postDelayed(new Runnable() { @Override public void run() { dayChange();//月份变化后再处理日变化 } }, CHANGEDELAY); } /** * 处理日变化的数据 */ private void dayChange() { day.clear(); int selectedYear = selectedCalender.get(Calendar.YEAR); int selectedMonth = selectedCalender.get(Calendar.MONTH) + 1; if (selectedYear == startYear && selectedMonth == startMonth) {//选择的年份是年份\月份都是开始时间,则要从开始日期的天数重置dayList for (int i = startDay; i <= selectedCalender.getActualMaximum(Calendar.DAY_OF_MONTH); i++) { day.add(fomatTimeUnit(i)); } } else if (selectedYear == endYear && selectedMonth == endMonth) {//选择的年份是年份\月份都是结束时间,则要从结束日期的天数重置dayList for (int i = 1; i <= endDay; i++) { day.add(fomatTimeUnit(i)); } } else {//既不是开始日也不是结束日,就按正常天数处理 for (int i = 1; i <= selectedCalender.getActualMaximum(Calendar.DAY_OF_MONTH); i++) { day.add(fomatTimeUnit(i)); } } selectedCalender.set(Calendar.DAY_OF_MONTH, Integer.parseInt(day.get(0))); day_pv.setData(day); day_pv.setSelected(0); excuteAnimator(ANIMATORDELAY, day_pv); day_pv.postDelayed(new Runnable() { @Override public void run() { hourChange(); } }, CHANGEDELAY); } /** * 处理小时变化的数据 */ private void hourChange() { if ((scrollUnits & SCROLLTYPE.HOUR.value) == SCROLLTYPE.HOUR.value) { hour.clear(); int selectedYear = selectedCalender.get(Calendar.YEAR); int selectedMonth = selectedCalender.get(Calendar.MONTH) + 1; int selectedDay = selectedCalender.get(Calendar.DAY_OF_MONTH); if (selectedYear == startYear && selectedMonth == startMonth && selectedDay == startDay) {//选择的年份是年份\月份\日都是开始时间,则要从开始日期小时数-最晚小时数重置hourList for (int i = startHour; i <= MAXHOUR; i++) {//结束时间小时数 hour.add(fomatTimeUnit(i)); } } else if (selectedYear == endYear && selectedMonth == endMonth && selectedDay == endDay) {//选择的年份是年份\月份\日都是结束时间,则要从最早小时数-结束日期小时数重置hourList for (int i = MINHOUR; i <= endHour; i++) {//开始时间小时数 hour.add(fomatTimeUnit(i)); } } else { for (int i = MINHOUR; i <= MAXHOUR; i++) {//既不是开始时也不是结束时,就按最小MINHOUR最大MAXHOUR hour.add(fomatTimeUnit(i)); } } selectedCalender.set(Calendar.HOUR_OF_DAY, Integer.parseInt(hour.get(0))); hour_pv.setData(hour); hour_pv.setSelected(0); excuteAnimator(ANIMATORDELAY, hour_pv); } hour_pv.postDelayed(new Runnable() { @Override public void run() { minuteChange(); } }, CHANGEDELAY); } /** * 处理分钟变化的数据 */ private void minuteChange() { if ((scrollUnits & SCROLLTYPE.MINUTE.value) == SCROLLTYPE.MINUTE.value) { minute.clear(); int selectedYear = selectedCalender.get(Calendar.YEAR); int selectedMonth = selectedCalender.get(Calendar.MONTH) + 1; int selectedDay = selectedCalender.get(Calendar.DAY_OF_MONTH); int selectedHour = selectedCalender.get(Calendar.HOUR_OF_DAY); //选择的年份是年份\月份\日\时都是开始时间,则要从开始日期分钟数-最晚分钟数重置minuteList if (selectedYear == startYear && selectedMonth == startMonth && selectedDay == startDay && selectedHour == startHour) { for (int i = startMininute; i <= MAXMINUTE; i++) { minute.add(fomatTimeUnit(i)); } //选择的年份是年份\月份\日\时都是结束时间,则要从最早分钟数-结束日期分钟数重置minuteList } else if (selectedYear == endYear && selectedMonth == endMonth && selectedDay == endDay && selectedHour == endHour) { for (int i = MINMINUTE; i <= endMininute; i++) { minute.add(fomatTimeUnit(i)); } //选择的小时数等于开始时间的小时数,则要从开始分钟数-最晚分钟数重置minuteList } else if (selectedHour == hour_workStart) { for (int i = minute_workStart; i <= MAXMINUTE; i++) { minute.add(fomatTimeUnit(i)); } //选择的小时数等于结束时间的小时数,则要从开始分钟数-最晚分钟数重置minuteList } else if (selectedHour == hour_workEnd) { for (int i = MINMINUTE; i <= minute_workEnd; i++) { minute.add(fomatTimeUnit(i)); } } else { //没有限制,则要从最早分钟数-最晚分钟数重置minuteList for (int i = MINMINUTE; i <= MAXMINUTE; i++) { minute.add(fomatTimeUnit(i)); } } selectedCalender.set(Calendar.MINUTE, Integer.parseInt(minute.get(0))); minute_pv.setData(minute); minute_pv.setSelected(0); excuteAnimator(ANIMATORDELAY, minute_pv); } excuteScroll(); } /** * 给PickerView 设置渐变、缩放动画(非必要) * @param ANIMATORDELAY * @param view */ private void excuteAnimator(long ANIMATORDELAY, View view) { PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("alpha", 1f, 0f, 1f); PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("scaleX", 1f, 1.3f, 1f); PropertyValuesHolder pvhZ = PropertyValuesHolder.ofFloat("scaleY", 1f, 1.3f, 1f); ObjectAnimator.ofPropertyValuesHolder(view,pvhX, pvhY, pvhZ).setDuration(ANIMATORDELAY).start(); } /** * 设置右上角文案 * @param str */ public void setNextBtTip(String str) { tv_select.setText(str); } /** *设置中间文案 * @param str */ public void setTitle(String str) { tv_title.setText(str); } /** * ????? * @param scrolltypes * @return */ public int disScrollUnit(SCROLLTYPE... scrolltypes) { if (scrolltypes == null || scrolltypes.length == 0) scrollUnits = SCROLLTYPE.HOUR.value + SCROLLTYPE.MINUTE.value;//3 for (SCROLLTYPE scrolltype : scrolltypes) { scrollUnits ^= scrolltype.value;//scrollUnits基础上-scrolltype.value } return scrollUnits; } public void setMode(MODE mode) { switch (mode.value) { case 1://只显示年月日的控件 disScrollUnit(SCROLLTYPE.HOUR, SCROLLTYPE.MINUTE); hour_pv.setVisibility(View.GONE); minute_pv.setVisibility(View.GONE); hour_text.setVisibility(View.GONE); minute_text.setVisibility(View.GONE); break; case 2://显示年月日时分的控件 disScrollUnit(); hour_pv.setVisibility(View.VISIBLE); minute_pv.setVisibility(View.VISIBLE); hour_text.setVisibility(View.VISIBLE); minute_text.setVisibility(View.VISIBLE); break; } } public void setIsLoop(boolean isLoop) { this.year_pv.setIsLoop(isLoop); this.month_pv.setIsLoop(isLoop); this.day_pv.setIsLoop(isLoop); this.hour_pv.setIsLoop(isLoop); this.minute_pv.setIsLoop(isLoop); } } R.style.time_dialog: <style name="time_dialog" parent="android:style/Theme.Dialog"> <item name="android:windowFrame">@null</item> <item name="android:windowNoTitle">true</item> <item name="android:windowIsFloating">true</item> <item name="android:windowContentOverlay">@null</item> <item name="android:windowBackground">@color/white</item> </style> R.layout.dlg_selector: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="vertical"> <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:background="#ffffff" android:padding="10dp"> <TextView android:id="@+id/tv_cancle" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" android:padding="10dp" android:text="@string/cancel" android:textColor="@color/main_color" android:textSize="16sp" /> <TextView android:id="@+id/tv_title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:text="@string/chosetime" android:textColor="@color/main_color" android:textSize="20sp" /> <TextView android:id="@+id/tv_select" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:layout_centerVertical="true" android:padding="10dp" android:text="@string/chose" android:textColor="@color/main_color" android:textSize="16sp" /> </RelativeLayout> <View android:layout_width="fill_parent" android:layout_height="0.5dp" android:background="#11112233" /> <RelativeLayout android:layout_width="fill_parent" android:layout_height="wrap_content"> <LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_marginBottom="5dp" android:background="#ffffff" android:orientation="horizontal" android:paddingLeft="20dp" android:paddingTop="15dp" android:paddingRight="20dp" android:paddingBottom="15dp"> <com.sange.cashfloater.cfwidget.timeselector.view.PickerView android:id="@+id/year_pv" android:layout_width="0dp" android:layout_height="160dp" android:layout_weight="3" /> <TextView android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:gravity="center" android:text="年" android:textColor="#333333" android:textSize="18sp" /> <com.sange.cashfloater.cfwidget.timeselector.view.PickerView android:id="@+id/month_pv" android:layout_width="0dp" android:layout_height="160dp" android:layout_weight="2" app:isLoop="false" /> <TextView android:text="月" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:gravity="center" android:textColor="#333333" android:textSize="18sp" /> <com.sange.cashfloater.cfwidget.timeselector.view.PickerView android:id="@+id/day_pv" android:layout_width="0dp" android:layout_height="160dp" android:layout_weight="2" /> <TextView android:text="日" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:gravity="center" android:textColor="#333333" android:textSize="18sp" /> <com.sange.cashfloater.cfwidget.timeselector.view.PickerView android:id="@+id/hour_pv" android:layout_width="0dp" android:layout_height="160dp" android:layout_weight="2" /> <TextView android:id="@+id/hour_text" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:gravity="center" android:text="时" android:textColor="#333333" android:textSize="18sp" /> <com.sange.cashfloater.cfwidget.timeselector.view.PickerView android:id="@+id/minute_pv" android:layout_width="0dp" android:layout_height="160dp" android:layout_weight="2" /> <TextView android:id="@+id/minute_text" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:text="分" android:gravity="center" android:textColor="#333333" android:textSize="18sp" /> </LinearLayout> </RelativeLayout> </LinearLayout> </LinearLayout>

相关工具类: ScreenUtil:

public class ScreenUtil { public static int height; public static int width; private Context context; private static ScreenUtil instance; public static ScreenUtil getInstance(Context context) { if (instance == null) { instance = new ScreenUtil(context); } return instance; } private ScreenUtil(Context context) { this.context = context; WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); DisplayMetrics dm = new DisplayMetrics(); manager.getDefaultDisplay().getMetrics(dm); width = dm.widthPixels; height = dm.heightPixels; } public int getScreenWidth() { return width; } } DateUtil: public class DateUtil { public static Date parse(String strDate, String pattern) { if (TextUtil.isEmpty(strDate)) { return null; } try { SimpleDateFormat df = new SimpleDateFormat(pattern); return df.parse(strDate); } catch (Exception e) { e.printStackTrace(); return null; } } public static String format(Date date, String pattern) { String returnValue = ""; if (date != null) { SimpleDateFormat df = new SimpleDateFormat(pattern); returnValue = df.format(date); } return (returnValue); } } TextUtil: public class TextUtil { public static boolean isEmpty(String str) { if (str == null || "".equals(str) || "null".equalsIgnoreCase(str)) { return true; } for (int i = 0; i < str.length(); i++) { char c = str.charAt(i); if (c != ' ' && c != '\t' && c != '\r' && c != '\n') { return false; } } return true; } }

over~


1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,会注明原创字样,如未注明都非原创,如有侵权请联系删除!;3.作者投稿可能会经我们编辑修改或补充;4.本站不提供任何储存功能只提供收集或者投稿人的网盘链接。

标签: #Android #滚轮控件 #仿IOS时间选择器