自定义时间选择器

自定义时间选择器

这里使用符合控件的形式来编写,过于复杂这难于理解,能够高效实现需求。

一、时间选择器需求分析

这里主要作用是明确却所需要的功能,才能确定所需要的的技术,方便提前阅读相关资料


dialog0.png

二、技术分析

dialog1.png
  1. 创建显示弹窗,采用的DialogFragment,PopWindow的话,太多问题了,对于有虚拟按键的剧中显示,背景透明度等不好控制,而且官方推荐使用DialogFragment
    能够有效关系生命周期
 Bundle bundle = new Bundle();
        bundle.putString(TimerPickDialogFragment.TITLE, "行程时间");
        bundle.putLong(TimerPickDialogFragment.TIME_START, System.currentTimeMillis());
        bundle.putLong(TimerPickDialogFragment.TIME_END, System.currentTimeMillis());
        new TimerPickDialogFragment().setOnTimeClickListener(new TimerPickDialogFragment.OnTimeClickListener() {
            @Override
            public void onClick(View view, TimerPickDialogFragment timerPickDialogFragment) {
                switch (view.getId()) {
                    case R.id.tvTimerPickerCancel:
                        timerPickDialogFragment.dismiss();
                        break;
                    case R.id.tvTimerPickerSure:
                        timerPickDialogFragment.dismiss();
                        break;
                    default:
                        break;
                }
            }
        }).setOnStartCurrentDateListener(new TimerPickDialogFragment.OnStartCurrentDateListener() {
            @Override
            public void onStartCurrentDate(View view, long date, TimerPickDialogFragment timerPickDialogFragment) {

            }
        }).setOnEndCurrentDateListener(new TimerPickDialogFragment.OnEndCurrentDateListener() {
            @Override
            public void onEndCurrentDate(View view, long date, TimerPickDialogFragment timerPickDialogFragment) {

            }
        }).addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {

            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {

            }

            @Override
            public void afterTextChanged(Editable s) {

            }
        }).isShowRemark(true)
                .updateText("时间选择器")
                .setBundle(bundle)
                .show(getFragmentManager(), "Time");

这里首先要对DialogFragment控件熟知,不懂得同学可以先移步官方文档,这里也对一些背景设定做下说明

public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
        // 修改显示的背景色
        Window window = getDialog().getWindow();
        window.setBackgroundDrawable(new ColorDrawable(ContextCompat.getColor(getActivity(), R.color.c_99000000)));
        // 修改输入法对弹窗的影响
        window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
        View view = inflater.inflate(R.layout.popwin_time_picker, container, false);
        unbinder = ButterKnife.bind(this, view);

        if (bundle != null) {
            timeStart = bundle.getLong(TIME_START);
            timeEnd = bundle.getLong(TIME_END);
            tvTimerPickerTitle.setText(bundle.getString(TITLE));
        } else if (timeStart <= 0 && timeEnd <= 0) {
            timeStart = timeEnd = Calendar.getInstance().getTimeInMillis();
        }
        if (textWatcher != null) {
            edTimerPickerRemark.addTextChangedListener(textWatcher);
        }

        if (isShow) {
            if (!TextUtils.isEmpty(remark)) {
                edTimerPickerRemark.setText(remark);
            }
            tvTimerPickerRemarkTitle.setVisibility(View.VISIBLE);
            edTimerPickerRemark.setVisibility(View.VISIBLE);
        } else {
            tvTimerPickerRemarkTitle.setVisibility(View.GONE);
            edTimerPickerRemark.setVisibility(View.GONE);
        }
        /**
         * 设置不可点击
         */
        //    pvTimerPickerStart.setWheelEnabled(false);
        //时间回调
        pvTimerPickerStart.setOnCurrentDateListener(new PickView.OnCurrentDateListener() {
            @Override
            public void onCurrentDate(View view, long date) {
                if (onStartCurrentDateListener != null) {
                    onStartCurrentDateListener.onStartCurrentDate(view, date, TimerPickDialogFragment.this);
                }
            }
        });
        pvTimerPickerEnd.setOnCurrentDateListener(new PickView.OnCurrentDateListener() {
            @Override
            public void onCurrentDate(View view, long date) {
                if (onEndCurrentDateListener != null) {
                    onEndCurrentDateListener.onEndCurrentDate(view, date, TimerPickDialogFragment.this);
                }
            }
        });

        pvTimerPickerStart.updateDate(timeStart);
        pvTimerPickerEnd.updateDate(timeEnd);
        return view;
    }
  1. 设计具体的滚动Item
    时间选择器有年月日时分,五个大Item 组合而成
package wudiplk.com.mycustomview.custom_view;

import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.View;
import android.widget.LinearLayout;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

/**
 * @author Wudi
 * @date 2018/1/16
 */

public class PickView extends LinearLayout {
    private Context context;
    private WheelView rvYear, rvMonth, rvDay, rvHour, rvMinute;

    private List<String> yearList = new ArrayList<>();
    private List<String> monthList = new ArrayList<>();
    private List<String> dayList = new ArrayList<>();
    private List<String> hourList = new ArrayList<>();
    private List<String> minuteList = new ArrayList<>();
    private Calendar calendar;
    private int yearPosition, monthPosition, dayPosition, hourPosition, minutePosition;

    public PickView(Context context) {
        super(context);
        this.context = context;
    }

    public PickView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        this.context = context;
    }

    public PickView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        this.context = context;
    }


    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        if (calendar == null) {
            calendar = Calendar.getInstance();
        }
        Handler handler = new Handler(new Handler.Callback() {
            @Override
            public boolean handleMessage(Message msg) {
                initView();
                return false;
            }
        });
        handler.sendEmptyMessage(1);
    }


    private void initView() {

        setOrientation(HORIZONTAL);

        // 初始化时间
        initDate();
        // 初始化控件
        rvYear = new WheelView(context, calendar, Calendar.YEAR, getMeasuredWidth(), getMeasuredHeight(), yearList, yearPosition);
        rvMonth = new WheelView(context, calendar, Calendar.MONTH, getMeasuredWidth(), getMeasuredHeight(), monthList, monthPosition);
        rvDay = new WheelView(context, calendar, Calendar.DAY_OF_MONTH, getMeasuredWidth(), getMeasuredHeight(), dayList, dayPosition);
        rvHour = new WheelView(context, calendar, Calendar.HOUR_OF_DAY, getMeasuredWidth(), getMeasuredHeight(), hourList, hourPosition);
        rvMinute = new WheelView(context, calendar, Calendar.MINUTE, getMeasuredWidth(), getMeasuredHeight(), minuteList, minutePosition);

        rvYear.setOnCurrentItemListener(new WheelView.OnCurrentItemListener() {
            @Override
            public void onCurrent(int position, String content) {
                if (onCurrentDateListener != null) {
                    int year = Integer.parseInt(content.replace("年", ""));
                    calendar.set(Calendar.YEAR, year);
                    onCurrentDateListener.onCurrentDate(PickView.this, calendar.getTimeInMillis());
                }
            }
        });

        rvMonth.setOnCurrentItemListener(new WheelView.OnCurrentItemListener() {
            @Override
            public void onCurrent(int position, String content) {
                if (onCurrentDateListener != null) {
                    // -1 由于系统计算月份是由0开始
                    int month = Integer.valueOf(content.replace("月", "")) - 1;
                    calendar.set(Calendar.MONTH, month);

                    // 更新这个月的天数
                    dayPosition = 0;
                    getDas(calendar.get(Calendar.YEAR), month + 1, calendar.get(Calendar.DAY_OF_MONTH));
                    rvDay.updateData(dayList, dayPosition);

                    onCurrentDateListener.onCurrentDate(PickView.this, calendar.getTimeInMillis());
                }

            }
        });
        rvDay.setOnCurrentItemListener(new WheelView.OnCurrentItemListener() {
            @Override
            public void onCurrent(int position, String content) {
                if (onCurrentDateListener != null) {
                    int day = Integer.valueOf(content.replace("日", ""));
                    calendar.set(Calendar.DAY_OF_MONTH, day);
                    onCurrentDateListener.onCurrentDate(PickView.this, calendar.getTimeInMillis());
                }

            }
        });
        rvHour.setOnCurrentItemListener(new WheelView.OnCurrentItemListener() {
            @Override
            public void onCurrent(int position, String content) {
                if (onCurrentDateListener != null) {
                    int hour = Integer.valueOf(content.replace("时", ""));
                    calendar.set(Calendar.HOUR_OF_DAY, hour);
                    onCurrentDateListener.onCurrentDate(PickView.this, calendar.getTimeInMillis());
                }

            }
        });
        rvMinute.setOnCurrentItemListener(new WheelView.OnCurrentItemListener() {
            @Override
            public void onCurrent(int position, String content) {
                if (onCurrentDateListener != null) {
                    int minute = Integer.valueOf(content.replace("分", ""));
                    calendar.set(Calendar.MINUTE, minute);
                    onCurrentDateListener.onCurrentDate(PickView.this, calendar.getTimeInMillis());
                }

            }
        });
        // 添加所有初始化后的页面

        addView(rvYear, 0);
        addView(rvMonth, 1);
        addView(rvDay, 2);
        addView(rvHour, 3);
        addView(rvMinute, 4);

        if (!isEnable) {
            rvYear.setWheelEnable(isEnable);
            rvMonth.setWheelEnable(isEnable);
            rvDay.setWheelEnable(isEnable);
            rvHour.setWheelEnable(isEnable);
            rvMinute.setWheelEnable(isEnable);
        }
    }

    /**
     * 设置初始时间
     *
     * @param updateDate
     */
    public void updateDate(long updateDate) {
        if (updateDate != 0) {
            calendar = Calendar.getInstance();
            calendar.setTime(new Date(updateDate));
        }
    }

    /**
     * 获取获取具体年份的天数,如果是初始化 当前的年月日
     * 如果是获取某年某月的天数day 默认为1
     *
     * @param year
     * @param month
     * @return
     */
    public void getDas(int year, int month, int day) {
        Calendar newCalendar = Calendar.getInstance();
        newCalendar.set(Calendar.YEAR, year);
        newCalendar.set(Calendar.MONTH, month - 1);
        newCalendar.set(Calendar.DATE, 1);
        newCalendar.roll(Calendar.DATE, -1);
        int days = newCalendar.get(Calendar.DATE);
        dayList.clear();
        for (int i = 1; i <= days; i++) {
            if (i < 10) {
                dayList.add("0" + i + "日");
            } else {
                dayList.add(i + "日");
            }
            if (i == day) {
                dayPosition = i - 1;
            }
        }
    }

    /**
     * 初始化时间数据
     */
    private void initDate() {

        int year = calendar.get(Calendar.YEAR);
        int month = calendar.get(Calendar.MONTH);
        int day = calendar.get(Calendar.DAY_OF_MONTH);
        int hour = calendar.get(Calendar.HOUR_OF_DAY);
        int minute = calendar.get(Calendar.MINUTE);
        // 初始化年数 和当前年
        for (int i = year - 50; i <= (year + 100); i++) {
            if (i == year) {
                yearPosition = yearList.size();
            }
            yearList.add(i + "年");

        }
        // 初始化月份 和当前月
        for (int i = 1; i <= 12; i++) {
            if (i == month) {
                monthPosition = i;
            }
            monthList.add(i + "月");
        }

        // 初始化天数 和当前天
        getDas(year, month + 1, day);

        // 初始化小时数 和当小时
        for (int i = 0; i < 24; i++) {
            if (hour == i) {
                hourPosition = i;
            }
            if (i < 10) {
                hourList.add("0" + i + "时");
            } else {
                hourList.add(i + "时");
            }
        }
        // 初始化分钟数 和当分钟
        for (int i = 0; i < 12; i++) {

            if (minute / 5 == i) {
                minutePosition = i;
                calendar.set(Calendar.MINUTE, i * 5);
            }
            if (i < 2) {
                minuteList.add("0" + i * 5 + "分");
            } else {
                minuteList.add(i * 5 + "分");
            }
        }

    }

    public interface OnCurrentDateListener {
        /**
         * 获取当前的日期
         *
         * @param view
         * @param date
         */
        void onCurrentDate(View view, long date);
    }

    private OnCurrentDateListener onCurrentDateListener;

    public void setOnCurrentDateListener(OnCurrentDateListener onCurrentDateListener) {
        this.onCurrentDateListener = onCurrentDateListener;
    }

    private boolean isEnable = true;

    public void setWheelEnabled(boolean b) {
        this.isEnable = b;
    }
}

每个Item 都是由一个RecycleView(WheelView),关键代码如下

 /**
     * 初始化内部控件
     */
    private void init() {
        currentPosition = initPosition;
        // 初始化滚动器的宽高
        oneHeight = (parentHeight - DensityUtils.dp2px(context, 4)) / 3;
        oneWidth = parentWidth / 5;
        final int padding = dp2px(3);
        LayoutParams params = new LayoutParams(oneWidth, parentHeight);
        setLayoutParams(params);
        this.setPadding(padding, 0, padding, 0);
        // 设置内部控件左右两边的空隙
        mRecyclerView = new RecyclerView(context);
        mRecyclerView.setOverScrollMode(OVER_SCROLL_NEVER);
        LayoutParams layoutParams = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
        layoutParams.addRule(RelativeLayout.CENTER_IN_PARENT, TRUE);
        // 指定显示三行数据
        layoutManager = new LinearLayoutManager(context) {
            @Override
            public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state, int widthSpec, int heightSpec) {
                if (getChildCount() > 0) {
                    View firstChildView = recycler.getViewForPosition(0);
                    measureChild(firstChildView, widthSpec, heightSpec);
                    setMeasuredDimension(MeasureSpec.getSize(widthSpec), firstChildView.getMeasuredHeight() * 3 - DensityUtils.dp2px(context, 4));
                } else {
                    super.onMeasure(recycler, state, widthSpec, heightSpec);
                }
            }
        };
        mRecyclerView.setLayoutManager(layoutManager);
        // 添加数据
        itemAdapter = new ItemAdapter();
        mRecyclerView.setAdapter(itemAdapter);

        // 滑动监听获取当前的item
        mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
            RecyclerView.LayoutManager layoutManager;

            @Override
            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                super.onScrollStateChanged(recyclerView, newState);
                // now State  0静止,没有滚动  1 正在被外部拖拽,一般为用户正在用手指滚动  2 自动滚动开始
                if (newState == 0) {
                    layoutManager = recyclerView.getLayoutManager();
                    LinearLayoutManager linearLayoutManager = (LinearLayoutManager) layoutManager;
                    if (linearLayoutManager.findLastCompletelyVisibleItemPosition() == currentPosition) {
                        // TODO
                    } else if (onCurrentItemListener != null) {
                        int nowPosition = (linearLayoutManager.findLastCompletelyVisibleItemPosition());
                        String text = stringList.get(nowPosition % stringList.size())
                                .replace("年", "")
                                .replace("月", "")
                                .replace("日", "")
                                .replace("时", "")
                                .replace("分", "");
                        // 判断是否是过去的日期
                        if (wheelType == Calendar.MONTH) {
                            selectCalendar.set(wheelType, Integer.valueOf(text) - 1);
                        } else {
                            selectCalendar.set(wheelType, Integer.valueOf(text));
                        }
                        // 判断是否是小于当前时间
                        if (selectCalendar.getTimeInMillis() >= System.currentTimeMillis()) {
                            onCurrentItemListener.onCurrent(nowPosition % stringList.size(), stringList.get(nowPosition % stringList.size()));
                            layoutManager.scrollToPosition(nowPosition + 1);
                            itemAdapter.notifyItemChanged(nowPosition, 0);
                            itemAdapter.notifyItemChanged(currentPosition, 1);
                            currentPosition = nowPosition;
                        } else {
                            // 判断是向上还是线下 isUpOrDown>向下,isUpOrDown<0向上
                            if (isUpOrDown > 0) {
                                currentPosition = initPosition;
                                // 由于滚动有些偏差,使用scrollToPositionWithOffset 来滚动
                                linearLayoutManager.scrollToPositionWithOffset(currentPosition - 1, -DensityUtils.dp2px(context, 3));
                                itemAdapter.notifyItemChanged(currentPosition, 0);
                            } else {
                                currentPosition = initPosition;
                                linearLayoutManager.scrollToPositionWithOffset(currentPosition - 1, -DensityUtils.dp2px(context, 3));
                                itemAdapter.notifyItemChanged(currentPosition, 0);
                            }
                        }
                    }
                } else {
                    itemAdapter.notifyItemChanged(currentPosition, 1);
                }
            }

            @Override
            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);
                isUpOrDown = dy;
            }
        });


        // 添加滚动器的的双杠线
        View topLineView = new View(context);
        View bottomLineView = new View(context);
        topLineView.setBackgroundColor(ContextCompat.getColor(context, R.color.c_e5e5e5));
        bottomLineView.setBackgroundColor(ContextCompat.getColor(context, R.color.c_e5e5e5));

        LayoutParams topLayoutParam = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, dp2px(1));
        topLayoutParam.setMargins(0, oneHeight, 0, 0);
        topLayoutParam.addRule(RelativeLayout.ALIGN_PARENT_TOP, RelativeLayout.TRUE);

        LayoutParams bottomLayoutParams = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, dp2px(1));
        bottomLayoutParams.setMargins(0, oneHeight * 2, 0, 0);
        bottomLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_TOP, RelativeLayout.TRUE);
        // 滑动到初始位置 -1是为了移动到中间位置
        layoutManager.scrollToPosition(stringList.size() * 1000 + initPosition - 1);
        currentPosition = stringList.size() * 1000 + initPosition;
        // 添加所有初始化后的空间
        addView(mRecyclerView, layoutParams);
        addView(topLineView, topLayoutParam);
        addView(bottomLineView, bottomLayoutParams);
    }
    
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容