android 日期选择器
来源:互联网 发布:手机淘宝店铺怎么分享 编辑:程序博客网 时间:2024/05/02 11:19
第一次写博客,写不惯
android系统自带的日期选择器因为系统原因样式各不相同,可是客户要求在各系统差不多就自己写了一个
在网上找了很久找了一个滚轮代码,效果不是很好就自己进行了优化
先对原作者说声对不起,已经找不到你的文章了,如果看到了请联系我
PickerView.java
public class PickerView extends View { public final String TAG = "PickerView"; /** * 线程池 */ private ExecutorService executorService; /** * text之间间距和minTextSize之比 */ public final float MARGIN_ALPHA = 2.8f; /** * 自动回滚到中间的速度 */ public int SPEED = 2; /** * 数组 */ private List<String> mDataList; /** * 是否添加数据 */ private boolean isAddData = false; /** * 选中的位置,这个位置是mDataList的中心位置,一直不变 */ private int mCurrentSelected; private Paint mPaint; private float mMaxTextSize = 80; private float mMinTextSize = 40; private float mMaxTextAlpha = 255; private float mMinTextAlpha = 120; private int mColorText = 0x333333; private int mViewHeight; private int mViewWidth; private float mLastDownY; /** * 滑动的距离 */ private float mMoveLen = 0; private boolean isInit = false; private OnPickerSelectListener mSelectListener; /** * 记录手指抬起时的速度 */ private VelocityTracker vTracker = null; /** * 滑动距离是否大于该值来判断是否滑动 */ private int mTouchSlop;// private Scroller mScroller; /** * true为循环模式 false为无限模式 */ private boolean isLoop = true; /** * 最大值和最小值 */ private String maxText; private String minText; /** * 设置最大值和最小值 是否应该抛异常? */ public void setMaxText(String maxText) { if(!TextUtils.isEmpty(minText)){ int max = Integer.parseInt(maxText); int min = Integer.parseInt(minText); if(max < min){ Log.e("setMaxText","最大值不能小于最小值"); return; } if(max - min < 20){ Log.e("setMaxText","最小值和最大值不能少于20"); return; } } this.maxText = maxText; } public void setMinText(String minText) { if(!TextUtils.isEmpty(maxText)){ int max = Integer.parseInt(maxText); int min = Integer.parseInt(minText); if(max < min){ Log.e("setMinText","最小值不能大于最大值"); return; } if(max - min < 20){ Log.e("setMinText","最小值和最大值不能少于20"); return; } } this.minText = minText; } /** * 根据当前值和最大值最小值来判断是否继续滑动 */ private boolean isHead = true; private boolean isTail = true; public PickerView(Context context){ super(context); init(); } public PickerView(Context context, AttributeSet attrs){ super(context, attrs); init(); } public void setOnSelectListener(OnPickerSelectListener listener) { mSelectListener = listener; } /** * 设置模式 */ public void setLoop(boolean isLoop){ this.isLoop = isLoop; } /** * 滑动停止时回传当前值,和滚动中有没有改变数据结构,有改变调用addPickerData */ private void performSelect(){ if (mSelectListener != null) { mSelectListener.onPickerSelect(mDataList.get(mCurrentSelected), getId()); if(isAddData){ isAddData = false; mSelectListener.addPickerData(getId()); } } } /** * 是否在滚动 */ public boolean isAnimation(){ if(runnable != null || isMove){ isAddData = true; } return runnable != null || isMove; } /** * 修改数据结构 */ public void setData(List<String> datas){ if(!isAnimation()){ mDataList = datas; mCurrentSelected = datas.size() / 2; if(runnable == null)invalidate(); } } /** * 添加上边数据 */ private void moveHeadToTail(){ String head; if(isLoop){ head = mDataList.get(0); }else{ if (mSelectListener != null){ head= mSelectListener.onPickerHead(mDataList.get(mDataList.size() - 1),getId()); }else{ head = mDataList.get(0); } } mDataList.remove(0); mDataList.add(head); } /** * 添加下边边数据 */ private void moveTailToHead(){ String tail; if(isLoop){ tail = mDataList.get(mDataList.size() - 1); }else{ if (mSelectListener != null){ tail= mSelectListener.onPickerTail(mDataList.get(0),getId()); }else{ 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 / 6.0f; mMinTextSize = mMaxTextSize / 2.0f; isInit = true; invalidate(); } private void init(){ //考虑到要用线程实现动画,而且动画太频繁就使用线程池 executorService = Executors.newFixedThreadPool(1); ViewConfiguration configuration = ViewConfiguration.get(getContext()); mTouchSlop = configuration.getScaledTouchSlop(); mDataList = new ArrayList<>(); mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mPaint.setStyle(Paint.Style.FILL); mPaint.setTextAlign(Paint.Align.CENTER); //设置字体颜色 mPaint.setColor(Color.parseColor("#000000")); } @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); Paint.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() - 1; 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 = MARGIN_ALPHA * mMinTextSize * position + type * mMoveLen; float scale = parabola(mViewHeight / 4.0f, d); float size = (mMaxTextSize - mMinTextSize) * scale + mMinTextSize; mPaint.setTextSize(size); mPaint.setAlpha((int) ((mMaxTextAlpha - mMinTextAlpha) * scale + mMinTextAlpha)); float y = (float) (mViewHeight / 2.0 + type * d); Paint.FontMetricsInt fmi = mPaint.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, mPaint); } /** * 抛物线 * * @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; } /** * 判断是否在滑动 */ private boolean isMove = false; @Override public boolean onTouchEvent(MotionEvent event){ switch (event.getActionMasked()){ case MotionEvent.ACTION_DOWN: doDown(event); break; case MotionEvent.ACTION_MOVE: float moveY = event.getRawY(); if(mLastDownY - moveY > 0 && !isHead){ mLastDownY = moveY; return true; } if(mLastDownY - moveY < 0 && !isTail){ mLastDownY = moveY; return true; } if(Math.abs(mLastDownY - moveY) >= mTouchSlop && !isMove){ isMove = true; } if(!isMove){ return true; } doMove(event); break; case MotionEvent.ACTION_UP: doUp(event); break; case MotionEvent.ACTION_POINTER_UP: if(vTracker != null){ vTracker.recycle(); vTracker = null; } break; } return true; } private void doDown(MotionEvent event){ //记录手指速度用 if(vTracker == null){ vTracker = VelocityTracker.obtain(); }else{ vTracker.clear(); } vTracker.addMovement(event); isMove = false; //停止当前动画 if(runnable != null){ runnable.stopAnimation(); } mLastDownY = event.getRawY(); } private void doMove(MotionEvent event){ //记录手指速度用 if(vTracker == null){ vTracker = VelocityTracker.obtain(); } vTracker.addMovement(event); mMoveLen += (event.getRawY() - mLastDownY); moverContent(); mLastDownY = event.getRawY(); invalidate(); } /** * 根据距离计算当前显示的数据 * */ private void moverContent(){ if (mMoveLen > MARGIN_ALPHA * mMinTextSize / 2){ // 往下滑超过离开距离 if(!TextUtils.isEmpty(minText)){ int min = Integer.parseInt(minText); if(Integer.parseInt(mDataList.get(mCurrentSelected)) <= min){ isTail = false; mMoveLen = 0; }else{ isTail = true; } } if(isTail){ moveTailToHead(); mMoveLen = mMoveLen - MARGIN_ALPHA * mMinTextSize; } } else if (mMoveLen < -MARGIN_ALPHA * mMinTextSize / 2){ // 往上滑超过离开距离 if(!TextUtils.isEmpty(maxText)){ int max = Integer.parseInt(maxText); if(Integer.parseInt(mDataList.get(mCurrentSelected)) >= max){ mMoveLen = 0; isHead = false; }else{ isHead = true; } } if(isHead){ moveHeadToTail(); mMoveLen = mMoveLen + MARGIN_ALPHA * mMinTextSize; } } if(mMoveLen > 0 && mMoveLen < MARGIN_ALPHA * mMinTextSize / 2){ if(!TextUtils.isEmpty(minText)){ int min = Integer.parseInt(minText); if(Integer.parseInt(mDataList.get(mCurrentSelected)) <= min){ isTail = false; mMoveLen = 0; }else{ isTail = true; } } } if(mMoveLen < 0 && mMoveLen >-MARGIN_ALPHA * mMinTextSize / 2){ if(!TextUtils.isEmpty(maxText)){ int max = Integer.parseInt(maxText); if(Integer.parseInt(mDataList.get(mCurrentSelected)) >= max){ mMoveLen = 0; isHead = false; }else{ isHead = true; } } } } private int s = 0; private void doUp(MotionEvent event){ //没有速度 if(vTracker == null){ isMove = false; return; } if(!isMove){ //当动画中手指点下后 if(mMoveLen > 0){ SPEED = -2; }else if(mMoveLen < 0){ SPEED = 2; }else{ SPEED = 0; } }else{ //获得手指速度 vTracker.computeCurrentVelocity(1000); vTracker.addMovement(event); int velocityY = (int) vTracker.getYVelocity() ; //对速度进行计算,数学不好,用笨办法 SPEED = velocityY/400; if(SPEED < 0){ if(SPEED < -20)SPEED = -20; if(SPEED > -2) SPEED = -2; }else if(SPEED > 0){ if(SPEED > 20)SPEED = 20; if(SPEED < 2) SPEED = 2; }else{ if(mMoveLen > 0){ SPEED = -2; }else if(mMoveLen < 0){ SPEED = 2; } } } //根据速度来进行动画 if(SPEED != 0){ s = SPEED*10; isMove = false; runnable = new AnimationRunnable(); executorService.submit(runnable); }else{ if(isMove){ isMove = false; performSelect(); } } if (vTracker != null) { vTracker.recycle(); vTracker = null; } } /** * 线程 * */ private AnimationRunnable runnable; private class AnimationRunnable implements Runnable{ public AnimationRunnable(){ isAnimation = true; } public void stopAnimation(){ isAnimation = false; } @Override public void run() { while (scrollAnimation()){ try { Thread.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); } } runnable = null; } private boolean isAnimation = false; private boolean scrollAnimation(){ if(isAnimation){ //笨办法,一点一点的减速 mMoveLen += SPEED; if(SPEED < -2){ if(s == SPEED*10){ SPEED++; } s++; }else if(SPEED > 2){ if(s == SPEED*10){ SPEED--; } s--; }else { if (Math.abs(mMoveLen) < 2) { mMoveLen = 0; isAnimation = false; post(new Runnable() { @Override public void run() { performSelect(); } }); } } moverContent(); }else{ return false; } postInvalidate(); return true; } } public interface OnPickerSelectListener{ //动画结束后返回当前值 void onPickerSelect(String text, int viewId); //在无限模式中去获得最上端要显示的下一条 String onPickerHead(String head, int viewId); //在无限模式中去获得最下端要显示的下一条 String onPickerTail(String tail, int viewId); //动画中数据结构变化时,动画结束后调用,重新添加数据 void addPickerData(int viewId); }}PickerFragment.java
public class PickerFragment extends Fragment implements PickerView.OnPickerSelectListener { private PickerView picker_year; private PickerView picker_month; private PickerView picker_day; //时间直接拼起来算了 //String date = YEAR+"-"+((MONTH+1)<10?"0"+(MONTH+1):""+(MONTH+1))+"-"+(DAY<10?"0"+DAY:""+DAY); private int YEAR; private int MONTH; private int DAY; private String date; private View rootView; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { if (null != rootView) { ViewGroup parent = (ViewGroup) rootView.getParent(); if (null != parent) { parent.removeView(rootView); } } else { rootView = inflater.inflate(R.layout.fragment_picker, container, false); initView(rootView); initData(); } return rootView; } private void initView(View view){ picker_year = (PickerView) view.findViewById(R.id.picker_year); picker_month = (PickerView) view.findViewById(R.id.picker_month); picker_day = (PickerView) view.findViewById(R.id.picker_day); picker_year.setOnSelectListener(this); picker_month.setOnSelectListener(this); picker_day.setOnSelectListener(this); } public void setDate(String date){ this.date = date; } private void initData(){ if(TextUtils.isEmpty(date)){ Calendar calendar = Calendar.getInstance(); YEAR = calendar.get(Calendar.YEAR); MONTH = calendar.get(Calendar.MONTH); DAY = calendar.get(Calendar.DATE); }else{ String[] strings = date.split("-"); YEAR = Integer.parseInt(strings[0]); MONTH = Integer.parseInt(strings[1]) - 1; DAY = Integer.parseInt(strings[2]); } initYearData(); initMonthData(); initDayData(); } private void initYearData(){ picker_year.setLoop(false); picker_year.setMinText((YEAR - 10) + ""); List<String> year = new ArrayList<String>(); for (int i = YEAR - 5; i < YEAR + 5; i++){ year.add("" + i); } picker_year.setData(year); } private void initMonthData(){ List<String> month = new ArrayList<String>(); for (int i = 1; i < 13; i++){ month.add(i < 10 ? "0" + i : "" + i); } if(MONTH < 6){ for (int i = 0; i < 6 - MONTH; i++){ String tail = month.get(month.size() - 1); month.remove(month.size() - 1); month.add(0, tail); } }else if(MONTH > 6){ for (int i = 0; i < MONTH - 6; i++){ String head = month.get(0); month.remove(0); month.add(head); } } picker_month.setData(month); } private void initDayData(){ if(picker_day.isAnimation()){ return; } List<String> day = new ArrayList<String>(); int monthDay = getDaysByYearMonth(YEAR,MONTH); for (int i = 1; i <= monthDay; i++) { day.add(i < 10 ? "0" + i : "" + i); } if(DAY > monthDay){ DAY = monthDay; } int now = DAY - 1; if(now < monthDay/2){ for (int i = 0; i < monthDay/2 - now; i++){ String tail = day.get(day.size() - 1); day.remove(day.size() - 1); day.add(0, tail); } }else if(now > monthDay/2){ for (int i = 0; i < now - monthDay/2; i++){ String head = day.get(0); day.remove(0); day.add(head); } } picker_day.setData(day); } private void initDay(){ List<String> day = new ArrayList<String>(); int monthDay = getDaysByYearMonth(YEAR,MONTH); for (int i = 1; i <= monthDay; i++) { day.add(i < 10 ? "0" + i : "" + i); } picker_day.setData(day); } private int getDaysByYearMonth(int year, int month) { Calendar a = Calendar.getInstance(); a.set(Calendar.YEAR, year); a.set(Calendar.MONTH, month); a.set(Calendar.DATE, 1); a.roll(Calendar.DATE, -1); return a.get(Calendar.DATE); } @Override public void onPickerSelect(String text,int viewId) { int date = Integer.parseInt(text); switch (viewId){ case R.id.picker_year:{ YEAR = date; if(MONTH == 1){ int day = getDaysByYearMonth(YEAR,MONTH); if(DAY > day){ DAY = day; } } initDayData(); break; } case R.id.picker_month:{ MONTH = date - 1; int day = getDaysByYearMonth(YEAR,MONTH); if(DAY > day){ DAY = day; } initDayData(); break; } case R.id.picker_day:{ DAY = date; break; } } } @Override public String onPickerHead(String head,int viewId) { int year = Integer.parseInt(head); year++; return ""+year; } @Override public String onPickerTail(String tail,int viewId) { int year = Integer.parseInt(tail); year--; return ""+year; } @Override public void addPickerData(int viewId) { initDayData(); }}fragment_picker.xml
<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <LinearLayout android:layout_width="match_parent" android:layout_alignParentBottom="true" android:layout_height="300dp" android:orientation="horizontal"> <com.picker.view.PickerView android:id="@+id/picker_year" android:layout_width="0px" android:layout_height="match_parent" android:layout_weight="4" /> <com.picker.view.PickerView android:id="@+id/picker_month" android:layout_width="0px" android:layout_height="match_parent" android:layout_weight="3" /> <com.picker.view.PickerView android:id="@+id/picker_day" android:layout_width="0px" android:layout_height="match_parent" android:layout_weight="3" /> </LinearLayout></RelativeLayout>
因为是日期选择器,对时间进行计算都放在fragment里了
下面是效果
0 0
- Android 日期选择器、日期范围选择器:MaterialDateRangePicker
- android 日期选择器
- android 日期滑动选择器
- Android中的日期选择器
- Android日期选择器
- android 日期选择器
- Android 万年历日期选择器
- Android日期选择器用法
- Android的日期选择器
- Android 之 日期选择器
- android 日期时间选择器
- Android自定义日期选择器
- Android 日期时间选择器
- android 日期时间选择器
- android 日期选择器
- Android 自定义日期选择器
- Android 时间日期选择器
- Android 日期时间选择器
- 2882: 美食节之感恩父母
- [DROOLS]规则引擎drools常用依赖--Gradle
- xcode 的编译时提醒报错,但却没有提示错误的位置
- 3D打印切片软件Cura及CuraEngine原理分析
- GDI+(C#)
- android 日期选择器
- UIImagePickerController详解
- 九度考研机试教程 21-1433:FatMouse 贪心算法
- 黑马程序员--反射的理解
- JAVA中方法和变量在继承中的覆盖和隐藏
- hdu2897 邂逅明下
- The maximum number of cell styles was exceeded. You can define up to 4000 styles
- 开发中的一些杂散乱的知识点(持续添加中..............)
- 初学Hadoop之MapReduce