{"id":1625,"date":"2023-03-25T21:06:50","date_gmt":"2023-03-25T13:06:50","guid":{"rendered":"https:\/\/www.appblog.cn\/?p=1625"},"modified":"2023-04-23T22:03:26","modified_gmt":"2023-04-23T14:03:26","slug":"android-custom-scrolling-date-time-selector","status":"publish","type":"post","link":"https:\/\/www.appblog.cn\/index.php\/2023\/03\/25\/android-custom-scrolling-date-time-selector\/","title":{"rendered":"Android\u81ea\u5b9a\u4e49\u6eda\u52a8\u65e5\u671f\u65f6\u95f4\u9009\u62e9\u5668"},"content":{"rendered":"<h2>\u57fa\u672c\u4f7f\u7528<\/h2>\n<pre><code class=\"language-java\">private TextView mTvBirthday;\nprivate MyDatePicker mBirthdayPicker;\nprivate String today;\n\n@Override\nprotected void onCreate(Bundle savedInstanceState) {\n    super.onCreate(savedInstanceState);\n    setContentView(R.layout.activity_main);\n\n    mTvBirthday = findViewById(R.id.tv_birthday);\n    mTvBirthday.setOnClickListener(new View.OnClickListener() {\n        @Override\n        public void onClick(View v) {\n            mBirthdayPicker.show(today);\n        }\n    });\n\n    SimpleDateFormat sdf = new SimpleDateFormat(&quot;yyyy-MM-dd HH:mm&quot;, Locale.CHINA);\n    String now = sdf.format(new Date());\n    today = now.split(&quot; &quot;)[0];\n    mTvBirthday.setText(today);\n\n    mBirthdayPicker = new MyDatePicker(this, new MyDatePicker.ResultHandler() {\n        @Override\n        public void handle(String time) { \/\/ \u56de\u8c03\u63a5\u53e3, \u83b7\u5f97\u9009\u4e2d\u7684\u65f6\u95f4\n            mTvBirthday.setText(time.split(&quot; &quot;)[0]);\n        }\n    }, &quot;1900-01-01 00:00&quot;, now); \/\/ \u521d\u59cb\u5316\u65e5\u671f\u683c\u5f0f\u8bf7\u7528: yyyy-MM-dd HH:mm, \u5426\u5219\u4e0d\u80fd\u6b63\u5e38\u8fd0\u884c\n    mBirthdayPicker.showSpecificTime(false); \/\/ \u4e0d\u663e\u793a\u65f6\u548c\u5206\n    mBirthdayPicker.setIsLoop(false); \/\/ \u4e0d\u5141\u8bb8\u5faa\u73af\u6eda\u52a8\n}<\/code><\/pre>\n<p><!-- more --><\/p>\n<h2>\u81ea\u5b9a\u4e49MyDatePicker<\/h2>\n<h3>MyDatePicker.java<\/h3>\n<pre><code class=\"language-java\">public class MyDatePicker {\n\n    \/**\n     * \u5b9a\u4e49\u7ed3\u679c\u56de\u8c03\u63a5\u53e3\n     *\/\n    public interface ResultHandler {\n        void handle(String time);\n    }\n\n    public enum SCROLL_TYPE {\n        HOUR(1),\n        MINUTE(2);\n\n        SCROLL_TYPE(int value) {\n            this.value = value;\n        }\n\n        public int value;\n    }\n\n    private int scrollUnits = SCROLL_TYPE.HOUR.value + SCROLL_TYPE.MINUTE.value;\n    private ResultHandler handler;\n    private Context context;\n    private boolean canAccess = false;\n\n    private Dialog datePickerDialog;\n    private WheelPickerView yearPickerView, monthPickerView, dayPickerView, hourPickerView, minutePickerView;\n\n    private static final int MAX_MINUTE = 59;\n    private static final int MAX_HOUR = 23;\n    private static final int MIN_MINUTE = 0;\n    private static final int MIN_HOUR = 0;\n    private static final int MAX_MONTH = 12;\n\n    private ArrayList&lt;String&gt; year, month, day, hour, minute;\n    private int startYear, startMonth, startDay, startHour, startMinute, endYear, endMonth, endDay, endHour, endMinute;\n    private boolean spanYear, spanMon, spanDay, spanHour, spanMin;\n    private Calendar selectedCalender, startCalendar, endCalendar;\n    private TextView tvCancle, tvSelect, tvHour, tvMinute;\n\n    public MyDatePicker(Context context, ResultHandler resultHandler, String startDate, String endDate) {\n        if (isValidDate(startDate, &quot;yyyy-MM-dd HH:mm&quot;) &amp;&amp; isValidDate(endDate, &quot;yyyy-MM-dd HH:mm&quot;)) {\n            canAccess = true;\n            this.context = context;\n            this.handler = resultHandler;\n            selectedCalender = Calendar.getInstance();\n            startCalendar = Calendar.getInstance();\n            endCalendar = Calendar.getInstance();\n            SimpleDateFormat sdf = new SimpleDateFormat(&quot;yyyy-MM-dd HH:mm&quot;, Locale.CHINA);\n            try {\n                startCalendar.setTime(sdf.parse(startDate));\n                endCalendar.setTime(sdf.parse(endDate));\n            } catch (ParseException e) {\n                e.printStackTrace();\n            }\n            initDialog();\n            initView();\n        }\n    }\n\n    private void initDialog() {\n        if (datePickerDialog == null) {\n            datePickerDialog = new Dialog(context, R.style.date_picker_dialog);\n            datePickerDialog.setCancelable(false);\n            datePickerDialog.requestWindowFeature(Window.FEATURE_NO_TITLE);\n            datePickerDialog.setContentView(R.layout.layout_date_picker);\n            Window window = datePickerDialog.getWindow();\n            window.setGravity(Gravity.BOTTOM);\n            WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);\n            DisplayMetrics dm = new DisplayMetrics();\n            manager.getDefaultDisplay().getMetrics(dm);\n            WindowManager.LayoutParams lp = window.getAttributes();\n            lp.width = dm.widthPixels;\n            window.setAttributes(lp);\n        }\n    }\n\n    private void initView() {\n        yearPickerView = datePickerDialog.findViewById(R.id.year_pv);\n        monthPickerView = datePickerDialog.findViewById(R.id.month_pv);\n        dayPickerView = datePickerDialog.findViewById(R.id.day_pv);\n        hourPickerView = datePickerDialog.findViewById(R.id.hour_pv);\n        minutePickerView = datePickerDialog.findViewById(R.id.minute_pv);\n        tvCancle = datePickerDialog.findViewById(R.id.tv_cancle);\n        tvSelect = datePickerDialog.findViewById(R.id.tv_select);\n        tvHour = datePickerDialog.findViewById(R.id.hour_text);\n        tvMinute = datePickerDialog.findViewById(R.id.minute_text);\n\n        tvCancle.setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View view) {\n                datePickerDialog.dismiss();\n            }\n        });\n\n        tvSelect.setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View view) {\n                SimpleDateFormat sdf = new SimpleDateFormat(&quot;yyyy-MM-dd HH:mm&quot;, Locale.CHINA);\n                handler.handle(sdf.format(selectedCalender.getTime()));\n                datePickerDialog.dismiss();\n            }\n        });\n    }\n\n    private void initParameter() {\n        startYear = startCalendar.get(Calendar.YEAR);\n        startMonth = startCalendar.get(Calendar.MONTH) + 1;\n        startDay = startCalendar.get(Calendar.DAY_OF_MONTH);\n        startHour = startCalendar.get(Calendar.HOUR_OF_DAY);\n        startMinute = startCalendar.get(Calendar.MINUTE);\n        endYear = endCalendar.get(Calendar.YEAR);\n        endMonth = endCalendar.get(Calendar.MONTH) + 1;\n        endDay = endCalendar.get(Calendar.DAY_OF_MONTH);\n        endHour = endCalendar.get(Calendar.HOUR_OF_DAY);\n        endMinute = endCalendar.get(Calendar.MINUTE);\n        spanYear = startYear != endYear;\n        spanMon = (!spanYear) &amp;&amp; (startMonth != endMonth);\n        spanDay = (!spanMon) &amp;&amp; (startDay != endDay);\n        spanHour = (!spanDay) &amp;&amp; (startHour != endHour);\n        spanMin = (!spanHour) &amp;&amp; (startMinute != endMinute);\n        selectedCalender.setTime(startCalendar.getTime());\n    }\n\n    private void initTimer() {\n        initArrayList();\n        if (spanYear) {\n            for (int i = startYear; i &lt;= endYear; i++) {\n                year.add(String.valueOf(i));\n            }\n            for (int i = startMonth; i &lt;= MAX_MONTH; i++) {\n                month.add(formatTimeUnit(i));\n            }\n            for (int i = startDay; i &lt;= startCalendar.getActualMaximum(Calendar.DAY_OF_MONTH); i++) {\n                day.add(formatTimeUnit(i));\n            }\n\n            if ((scrollUnits &amp; SCROLL_TYPE.HOUR.value) != SCROLL_TYPE.HOUR.value) {\n                hour.add(formatTimeUnit(startHour));\n            } else {\n                for (int i = startHour; i &lt;= MAX_HOUR; i++) {\n                    hour.add(formatTimeUnit(i));\n                }\n            }\n\n            if ((scrollUnits &amp; SCROLL_TYPE.MINUTE.value) != SCROLL_TYPE.MINUTE.value) {\n                minute.add(formatTimeUnit(startMinute));\n            } else {\n                for (int i = startMinute; i &lt;= MAX_MINUTE; i++) {\n                    minute.add(formatTimeUnit(i));\n                }\n            }\n        } else if (spanMon) {\n            year.add(String.valueOf(startYear));\n            for (int i = startMonth; i &lt;= endMonth; i++) {\n                month.add(formatTimeUnit(i));\n            }\n            for (int i = startDay; i &lt;= startCalendar.getActualMaximum(Calendar.DAY_OF_MONTH); i++) {\n                day.add(formatTimeUnit(i));\n            }\n\n            if ((scrollUnits &amp; SCROLL_TYPE.HOUR.value) != SCROLL_TYPE.HOUR.value) {\n                hour.add(formatTimeUnit(startHour));\n            } else {\n                for (int i = startHour; i &lt;= MAX_HOUR; i++) {\n                    hour.add(formatTimeUnit(i));\n                }\n            }\n\n            if ((scrollUnits &amp; SCROLL_TYPE.MINUTE.value) != SCROLL_TYPE.MINUTE.value) {\n                minute.add(formatTimeUnit(startMinute));\n            } else {\n                for (int i = startMinute; i &lt;= MAX_MINUTE; i++) {\n                    minute.add(formatTimeUnit(i));\n                }\n            }\n        } else if (spanDay) {\n            year.add(String.valueOf(startYear));\n            month.add(formatTimeUnit(startMonth));\n            for (int i = startDay; i &lt;= endDay; i++) {\n                day.add(formatTimeUnit(i));\n            }\n\n            if ((scrollUnits &amp; SCROLL_TYPE.HOUR.value) != SCROLL_TYPE.HOUR.value) {\n                hour.add(formatTimeUnit(startHour));\n            } else {\n                for (int i = startHour; i &lt;= MAX_HOUR; i++) {\n                    hour.add(formatTimeUnit(i));\n                }\n            }\n\n            if ((scrollUnits &amp; SCROLL_TYPE.MINUTE.value) != SCROLL_TYPE.MINUTE.value) {\n                minute.add(formatTimeUnit(startMinute));\n            } else {\n                for (int i = startMinute; i &lt;= MAX_MINUTE; i++) {\n                    minute.add(formatTimeUnit(i));\n                }\n            }\n        } else if (spanHour) {\n            year.add(String.valueOf(startYear));\n            month.add(formatTimeUnit(startMonth));\n            day.add(formatTimeUnit(startDay));\n\n            if ((scrollUnits &amp; SCROLL_TYPE.HOUR.value) != SCROLL_TYPE.HOUR.value) {\n                hour.add(formatTimeUnit(startHour));\n            } else {\n                for (int i = startHour; i &lt;= endHour; i++) {\n                    hour.add(formatTimeUnit(i));\n                }\n            }\n\n            if ((scrollUnits &amp; SCROLL_TYPE.MINUTE.value) != SCROLL_TYPE.MINUTE.value) {\n                minute.add(formatTimeUnit(startMinute));\n            } else {\n                for (int i = startMinute; i &lt;= MAX_MINUTE; i++) {\n                    minute.add(formatTimeUnit(i));\n                }\n            }\n        } else if (spanMin) {\n            year.add(String.valueOf(startYear));\n            month.add(formatTimeUnit(startMonth));\n            day.add(formatTimeUnit(startDay));\n            hour.add(formatTimeUnit(startHour));\n\n            if ((scrollUnits &amp; SCROLL_TYPE.MINUTE.value) != SCROLL_TYPE.MINUTE.value) {\n                minute.add(formatTimeUnit(startMinute));\n            } else {\n                for (int i = startMinute; i &lt;= endMinute; i++) {\n                    minute.add(formatTimeUnit(i));\n                }\n            }\n        }\n        loadComponent();\n    }\n\n    \/**\n     * \u5c06\u201c0-9\u201d\u8f6c\u6362\u4e3a\u201c00-09\u201d\n     *\/\n    private String formatTimeUnit(int unit) {\n        return unit &lt; 10 ? &quot;0&quot; + String.valueOf(unit) : String.valueOf(unit);\n    }\n\n    private void initArrayList() {\n        if (year == null) year = new ArrayList&lt;&gt;();\n        if (month == null) month = new ArrayList&lt;&gt;();\n        if (day == null) day = new ArrayList&lt;&gt;();\n        if (hour == null) hour = new ArrayList&lt;&gt;();\n        if (minute == null) minute = new ArrayList&lt;&gt;();\n        year.clear();\n        month.clear();\n        day.clear();\n        hour.clear();\n        minute.clear();\n    }\n\n    private void loadComponent() {\n        yearPickerView.setData(year);\n        monthPickerView.setData(month);\n        dayPickerView.setData(day);\n        hourPickerView.setData(hour);\n        minutePickerView.setData(minute);\n        yearPickerView.setSelected(0);\n        monthPickerView.setSelected(0);\n        dayPickerView.setSelected(0);\n        hourPickerView.setSelected(0);\n        minutePickerView.setSelected(0);\n        executeScroll();\n    }\n\n    private void addListener() {\n        yearPickerView.setOnSelectListener(new WheelPickerView.OnSelectListener() {\n            @Override\n            public void onSelect(int index, String text) {\n                selectedCalender.set(Calendar.YEAR, Integer.parseInt(text));\n                monthChange();\n            }\n        });\n\n        monthPickerView.setOnSelectListener(new WheelPickerView.OnSelectListener() {\n            @Override\n            public void onSelect(int index, String text) {\n                selectedCalender.set(Calendar.DAY_OF_MONTH, 1);\n                selectedCalender.set(Calendar.MONTH, Integer.parseInt(text) - 1);\n                dayChange();\n            }\n        });\n\n        dayPickerView.setOnSelectListener(new WheelPickerView.OnSelectListener() {\n            @Override\n            public void onSelect(int index, String text) {\n                selectedCalender.set(Calendar.DAY_OF_MONTH, Integer.parseInt(text));\n                hourChange();\n            }\n        });\n\n        hourPickerView.setOnSelectListener(new WheelPickerView.OnSelectListener() {\n            @Override\n            public void onSelect(int index, String text) {\n                selectedCalender.set(Calendar.HOUR_OF_DAY, Integer.parseInt(text));\n                minuteChange();\n            }\n        });\n\n        minutePickerView.setOnSelectListener(new WheelPickerView.OnSelectListener() {\n            @Override\n            public void onSelect(int index, String text) {\n                selectedCalender.set(Calendar.MINUTE, Integer.parseInt(text));\n            }\n        });\n    }\n\n    private void monthChange() {\n        month.clear();\n        int selectedYear = selectedCalender.get(Calendar.YEAR);\n        if (selectedYear == startYear) {\n            for (int i = startMonth; i &lt;= MAX_MONTH; i++) {\n                month.add(formatTimeUnit(i));\n            }\n        } else if (selectedYear == endYear) {\n            for (int i = 1; i &lt;= endMonth; i++) {\n                month.add(formatTimeUnit(i));\n            }\n        } else {\n            for (int i = 1; i &lt;= MAX_MONTH; i++) {\n                month.add(formatTimeUnit(i));\n            }\n        }\n        selectedCalender.set(Calendar.MONTH, Integer.parseInt(month.get(0)) - 1);\n        monthPickerView.setData(month);\n        monthPickerView.setSelected(0);\n        executeAnimator(monthPickerView);\n\n        monthPickerView.postDelayed(new Runnable() {\n            @Override\n            public void run() {\n                dayChange();\n            }\n        }, 100);\n    }\n\n    private void dayChange() {\n        day.clear();\n        int selectedYear = selectedCalender.get(Calendar.YEAR);\n        int selectedMonth = selectedCalender.get(Calendar.MONTH) + 1;\n        if (selectedYear == startYear &amp;&amp; selectedMonth == startMonth) {\n            for (int i = startDay; i &lt;= selectedCalender.getActualMaximum(Calendar.DAY_OF_MONTH); i++) {\n                day.add(formatTimeUnit(i));\n            }\n        } else if (selectedYear == endYear &amp;&amp; selectedMonth == endMonth) {\n            for (int i = 1; i &lt;= endDay; i++) {\n                day.add(formatTimeUnit(i));\n            }\n        } else {\n            for (int i = 1; i &lt;= selectedCalender.getActualMaximum(Calendar.DAY_OF_MONTH); i++) {\n                day.add(formatTimeUnit(i));\n            }\n        }\n        selectedCalender.set(Calendar.DAY_OF_MONTH, Integer.parseInt(day.get(0)));\n        dayPickerView.setData(day);\n        dayPickerView.setSelected(0);\n        executeAnimator(dayPickerView);\n\n        dayPickerView.postDelayed(new Runnable() {\n            @Override\n            public void run() {\n                hourChange();\n            }\n        }, 100);\n    }\n\n    private void hourChange() {\n        if ((scrollUnits &amp; SCROLL_TYPE.HOUR.value) == SCROLL_TYPE.HOUR.value) {\n            hour.clear();\n            int selectedYear = selectedCalender.get(Calendar.YEAR);\n            int selectedMonth = selectedCalender.get(Calendar.MONTH) + 1;\n            int selectedDay = selectedCalender.get(Calendar.DAY_OF_MONTH);\n            if (selectedYear == startYear &amp;&amp; selectedMonth == startMonth &amp;&amp; selectedDay == startDay) {\n                for (int i = startHour; i &lt;= MAX_HOUR; i++) {\n                    hour.add(formatTimeUnit(i));\n                }\n            } else if (selectedYear == endYear &amp;&amp; selectedMonth == endMonth &amp;&amp; selectedDay == endDay) {\n                for (int i = MIN_HOUR; i &lt;= endHour; i++) {\n                    hour.add(formatTimeUnit(i));\n                }\n            } else {\n                for (int i = MIN_HOUR; i &lt;= MAX_HOUR; i++) {\n                    hour.add(formatTimeUnit(i));\n                }\n            }\n            selectedCalender.set(Calendar.HOUR_OF_DAY, Integer.parseInt(hour.get(0)));\n            hourPickerView.setData(hour);\n            hourPickerView.setSelected(0);\n            executeAnimator(hourPickerView);\n        }\n\n        hourPickerView.postDelayed(new Runnable() {\n            @Override\n            public void run() {\n                minuteChange();\n            }\n        }, 100);\n    }\n\n    private void minuteChange() {\n        if ((scrollUnits &amp; SCROLL_TYPE.MINUTE.value) == SCROLL_TYPE.MINUTE.value) {\n            minute.clear();\n            int selectedYear = selectedCalender.get(Calendar.YEAR);\n            int selectedMonth = selectedCalender.get(Calendar.MONTH) + 1;\n            int selectedDay = selectedCalender.get(Calendar.DAY_OF_MONTH);\n            int selectedHour = selectedCalender.get(Calendar.HOUR_OF_DAY);\n            if (selectedYear == startYear &amp;&amp; selectedMonth == startMonth &amp;&amp; selectedDay == startDay &amp;&amp; selectedHour == startHour) {\n                for (int i = startMinute; i &lt;= MAX_MINUTE; i++) {\n                    minute.add(formatTimeUnit(i));\n                }\n            } else if (selectedYear == endYear &amp;&amp; selectedMonth == endMonth &amp;&amp; selectedDay == endDay &amp;&amp; selectedHour == endHour) {\n                for (int i = MIN_MINUTE; i &lt;= endMinute; i++) {\n                    minute.add(formatTimeUnit(i));\n                }\n            } else {\n                for (int i = MIN_MINUTE; i &lt;= MAX_MINUTE; i++) {\n                    minute.add(formatTimeUnit(i));\n                }\n            }\n            selectedCalender.set(Calendar.MINUTE, Integer.parseInt(minute.get(0)));\n            minutePickerView.setData(minute);\n            minutePickerView.setSelected(0);\n            executeAnimator(minutePickerView);\n        }\n        executeScroll();\n    }\n\n    private void executeAnimator(View view) {\n        PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat(&quot;alpha&quot;, 1f, 0f, 1f);\n        PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat(&quot;scaleX&quot;, 1f, 1.3f, 1f);\n        PropertyValuesHolder pvhZ = PropertyValuesHolder.ofFloat(&quot;scaleY&quot;, 1f, 1.3f, 1f);\n        ObjectAnimator.ofPropertyValuesHolder(view, pvhX, pvhY, pvhZ).setDuration(200).start();\n    }\n\n    private void executeScroll() {\n        yearPickerView.setCanScroll(year.size() &gt; 1);\n        monthPickerView.setCanScroll(month.size() &gt; 1);\n        dayPickerView.setCanScroll(day.size() &gt; 1);\n        hourPickerView.setCanScroll(hour.size() &gt; 1 &amp;&amp; (scrollUnits &amp; SCROLL_TYPE.HOUR.value) == SCROLL_TYPE.HOUR.value);\n        minutePickerView.setCanScroll(minute.size() &gt; 1 &amp;&amp; (scrollUnits &amp; SCROLL_TYPE.MINUTE.value) == SCROLL_TYPE.MINUTE.value);\n    }\n\n    private int disScrollUnit(SCROLL_TYPE... scroll_types) {\n        if (scroll_types == null || scroll_types.length == 0) {\n            scrollUnits = SCROLL_TYPE.HOUR.value + SCROLL_TYPE.MINUTE.value;\n        } else {\n            for (SCROLL_TYPE scroll_type : scroll_types) {\n                scrollUnits ^= scroll_type.value;\n            }\n        }\n        return scrollUnits;\n    }\n\n    public void show(String time) {\n        if (canAccess) {\n            if (isValidDate(time, &quot;yyyy-MM-dd&quot;)) {\n                if (startCalendar.getTime().getTime() &lt; endCalendar.getTime().getTime()) {\n                    canAccess = true;\n                    initParameter();\n                    initTimer();\n                    addListener();\n                    setSelectedTime(time);\n                    datePickerDialog.show();\n                }\n            } else {\n                canAccess = false;\n            }\n        }\n    }\n\n    \/**\n     * \u8bbe\u7f6e\u65e5\u671f\u63a7\u4ef6\u662f\u5426\u663e\u793a\u65f6\u548c\u5206\n     *\/\n    public void showSpecificTime(boolean show) {\n        if (canAccess) {\n            if (show) {\n                disScrollUnit();\n                hourPickerView.setVisibility(View.VISIBLE);\n                tvHour.setVisibility(View.VISIBLE);\n                minutePickerView.setVisibility(View.VISIBLE);\n                tvMinute.setVisibility(View.VISIBLE);\n            } else {\n                disScrollUnit(SCROLL_TYPE.HOUR, SCROLL_TYPE.MINUTE);\n                hourPickerView.setVisibility(View.GONE);\n                tvHour.setVisibility(View.GONE);\n                minutePickerView.setVisibility(View.GONE);\n                tvMinute.setVisibility(View.GONE);\n            }\n        }\n    }\n\n    \/**\n     * \u8bbe\u7f6e\u65e5\u671f\u63a7\u4ef6\u662f\u5426\u53ef\u4ee5\u5faa\u73af\u6eda\u52a8\n     *\/\n    public void setIsLoop(boolean isLoop) {\n        if (canAccess) {\n            this.yearPickerView.setIsLoop(isLoop);\n            this.monthPickerView.setIsLoop(isLoop);\n            this.dayPickerView.setIsLoop(isLoop);\n            this.hourPickerView.setIsLoop(isLoop);\n            this.minutePickerView.setIsLoop(isLoop);\n        }\n    }\n\n    \/**\n     * \u8bbe\u7f6e\u65e5\u671f\u63a7\u4ef6\u9ed8\u8ba4\u9009\u4e2d\u7684\u65f6\u95f4\n     *\/\n    public void setSelectedTime(String time) {\n        if (canAccess) {\n            String[] str = time.split(&quot; &quot;);\n            String[] dateStr = str[0].split(&quot;-&quot;);\n\n            yearPickerView.setSelected(dateStr[0]);\n            selectedCalender.set(Calendar.YEAR, Integer.parseInt(dateStr[0]));\n\n            month.clear();\n            int selectedYear = selectedCalender.get(Calendar.YEAR);\n            if (selectedYear == startYear) {\n                for (int i = startMonth; i &lt;= MAX_MONTH; i++) {\n                    month.add(formatTimeUnit(i));\n                }\n            } else if (selectedYear == endYear) {\n                for (int i = 1; i &lt;= endMonth; i++) {\n                    month.add(formatTimeUnit(i));\n                }\n            } else {\n                for (int i = 1; i &lt;= MAX_MONTH; i++) {\n                    month.add(formatTimeUnit(i));\n                }\n            }\n            monthPickerView.setData(month);\n            monthPickerView.setSelected(dateStr[1]);\n            selectedCalender.set(Calendar.MONTH, Integer.parseInt(dateStr[1]) - 1);\n            executeAnimator(monthPickerView);\n\n            day.clear();\n            int selectedMonth = selectedCalender.get(Calendar.MONTH) + 1;\n            if (selectedYear == startYear &amp;&amp; selectedMonth == startMonth) {\n                for (int i = startDay; i &lt;= selectedCalender.getActualMaximum(Calendar.DAY_OF_MONTH); i++) {\n                    day.add(formatTimeUnit(i));\n                }\n            } else if (selectedYear == endYear &amp;&amp; selectedMonth == endMonth) {\n                for (int i = 1; i &lt;= endDay; i++) {\n                    day.add(formatTimeUnit(i));\n                }\n            } else {\n                for (int i = 1; i &lt;= selectedCalender.getActualMaximum(Calendar.DAY_OF_MONTH); i++) {\n                    day.add(formatTimeUnit(i));\n                }\n            }\n            dayPickerView.setData(day);\n            dayPickerView.setSelected(dateStr[2]);\n            selectedCalender.set(Calendar.DAY_OF_MONTH, Integer.parseInt(dateStr[2]));\n            executeAnimator(dayPickerView);\n\n            if (str.length == 2) {\n                String[] timeStr = str[1].split(&quot;:&quot;);\n\n                if ((scrollUnits &amp; SCROLL_TYPE.HOUR.value) == SCROLL_TYPE.HOUR.value) {\n                    hour.clear();\n                    int selectedDay = selectedCalender.get(Calendar.DAY_OF_MONTH);\n                    if (selectedYear == startYear &amp;&amp; selectedMonth == startMonth &amp;&amp; selectedDay == startDay) {\n                        for (int i = startHour; i &lt;= MAX_HOUR; i++) {\n                            hour.add(formatTimeUnit(i));\n                        }\n                    } else if (selectedYear == endYear &amp;&amp; selectedMonth == endMonth &amp;&amp; selectedDay == endDay) {\n                        for (int i = MIN_HOUR; i &lt;= endHour; i++) {\n                            hour.add(formatTimeUnit(i));\n                        }\n                    } else {\n                        for (int i = MIN_HOUR; i &lt;= MAX_HOUR; i++) {\n                            hour.add(formatTimeUnit(i));\n                        }\n                    }\n                    hourPickerView.setData(hour);\n                    hourPickerView.setSelected(timeStr[0]);\n                    selectedCalender.set(Calendar.HOUR_OF_DAY, Integer.parseInt(timeStr[0]));\n                    executeAnimator(hourPickerView);\n                }\n\n                if ((scrollUnits &amp; SCROLL_TYPE.MINUTE.value) == SCROLL_TYPE.MINUTE.value) {\n                    minute.clear();\n                    int selectedDay = selectedCalender.get(Calendar.DAY_OF_MONTH);\n                    int selectedHour = selectedCalender.get(Calendar.HOUR_OF_DAY);\n                    if (selectedYear == startYear &amp;&amp; selectedMonth == startMonth &amp;&amp; selectedDay == startDay &amp;&amp; selectedHour == startHour) {\n                        for (int i = startMinute; i &lt;= MAX_MINUTE; i++) {\n                            minute.add(formatTimeUnit(i));\n                        }\n                    } else if (selectedYear == endYear &amp;&amp; selectedMonth == endMonth &amp;&amp; selectedDay == endDay &amp;&amp; selectedHour == endHour) {\n                        for (int i = MIN_MINUTE; i &lt;= endMinute; i++) {\n                            minute.add(formatTimeUnit(i));\n                        }\n                    } else {\n                        for (int i = MIN_MINUTE; i &lt;= MAX_MINUTE; i++) {\n                            minute.add(formatTimeUnit(i));\n                        }\n                    }\n                    minutePickerView.setData(minute);\n                    minutePickerView.setSelected(timeStr[1]);\n                    selectedCalender.set(Calendar.MINUTE, Integer.parseInt(timeStr[1]));\n                    executeAnimator(minutePickerView);\n                }\n            }\n            executeScroll();\n        }\n    }\n\n    \/**\n     * \u9a8c\u8bc1\u5b57\u7b26\u4e32\u662f\u5426\u662f\u4e00\u4e2a\u5408\u6cd5\u7684\u65e5\u671f\u683c\u5f0f\n     *\/\n    private boolean isValidDate(String date, String template) {\n        boolean convertSuccess = true;\n        \/\/ \u6307\u5b9a\u65e5\u671f\u683c\u5f0f\n        SimpleDateFormat format = new SimpleDateFormat(template, Locale.CHINA);\n        try {\n            \/\/ \u8bbe\u7f6elenient\u4e3afalse. \u5426\u5219SimpleDateFormat\u4f1a\u6bd4\u8f83\u5bbd\u677e\u5730\u9a8c\u8bc1\u65e5\u671f\uff0c\u6bd4\u59822015\/02\/29\u4f1a\u88ab\u63a5\u53d7\uff0c\u5e76\u8f6c\u6362\u62102015\/03\/01\n            format.setLenient(false);\n            format.parse(date);\n        } catch (Exception e) {\n            \/\/ \u5982\u679cthrow java.text.ParseException\u6216\u8005NullPointerException\uff0c\u5c31\u8bf4\u660e\u683c\u5f0f\u4e0d\u5bf9\n            convertSuccess = false;\n        }\n        return convertSuccess;\n    }\n\n}<\/code><\/pre>\n<h3>date_picker_dialog.xml<\/h3>\n<pre><code class=\"language-xml\">&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;\n&lt;LinearLayout xmlns:android=&quot;http:\/\/schemas.android.com\/apk\/res\/android&quot;\n    android:layout_width=&quot;match_parent&quot;\n    android:layout_height=&quot;wrap_content&quot;\n    android:orientation=&quot;vertical&quot;&gt;\n\n    &lt;RelativeLayout\n        android:layout_width=&quot;match_parent&quot;\n        android:layout_height=&quot;wrap_content&quot;\n        android:background=&quot;#FFF&quot;\n        android:padding=&quot;10dp&quot;&gt;\n\n        &lt;TextView\n            android:id=&quot;@+id\/tv_cancle&quot;\n            android:layout_width=&quot;wrap_content&quot;\n            android:layout_height=&quot;wrap_content&quot;\n            android:layout_centerVertical=&quot;true&quot;\n            android:padding=&quot;10dp&quot;\n            android:text=&quot;@string\/cancel&quot;\n            android:textColor=&quot;@color\/text1&quot;\n            android:textSize=&quot;16sp&quot; \/&gt;\n\n        &lt;TextView\n            android:id=&quot;@+id\/tv_title&quot;\n            android:layout_width=&quot;wrap_content&quot;\n            android:layout_height=&quot;wrap_content&quot;\n            android:layout_centerInParent=&quot;true&quot;\n            android:text=&quot;@string\/date_picker_title&quot;\n            android:textColor=&quot;@color\/text1&quot;\n            android:textSize=&quot;20sp&quot; \/&gt;\n\n        &lt;TextView\n            android:id=&quot;@+id\/tv_select&quot;\n            android:layout_width=&quot;wrap_content&quot;\n            android:layout_height=&quot;wrap_content&quot;\n            android:layout_alignParentRight=&quot;true&quot;\n            android:layout_centerVertical=&quot;true&quot;\n            android:padding=&quot;10dp&quot;\n            android:text=&quot;@string\/ok&quot;\n            android:textColor=&quot;@color\/text1&quot;\n            android:textSize=&quot;16sp&quot; \/&gt;\n\n    &lt;\/RelativeLayout&gt;\n\n    &lt;View\n        android:layout_width=&quot;match_parent&quot;\n        android:layout_height=&quot;0.5dp&quot;\n        android:background=&quot;#11112233&quot; \/&gt;\n\n    &lt;RelativeLayout\n        android:layout_width=&quot;match_parent&quot;\n        android:layout_height=&quot;wrap_content&quot;&gt;\n\n        &lt;LinearLayout\n            android:layout_width=&quot;match_parent&quot;\n            android:layout_height=&quot;wrap_content&quot;\n            android:layout_marginBottom=&quot;5dp&quot;\n            android:background=&quot;#FFF&quot;\n            android:orientation=&quot;horizontal&quot;\n            android:paddingBottom=&quot;15dp&quot;\n            android:paddingLeft=&quot;20dp&quot;\n            android:paddingRight=&quot;20dp&quot;\n            android:paddingTop=&quot;15dp&quot;&gt;\n\n            &lt;me.yezhou.lib.widget.WheelPickerView\n                android:id=&quot;@+id\/year_pv&quot;\n                android:layout_width=&quot;0dp&quot;\n                android:layout_height=&quot;160dp&quot;\n                android:layout_weight=&quot;2&quot; \/&gt;\n\n            &lt;TextView\n                android:layout_width=&quot;0dp&quot;\n                android:layout_height=&quot;match_parent&quot;\n                android:layout_weight=&quot;1.3&quot;\n                android:gravity=&quot;center&quot;\n                android:text=&quot;@string\/year&quot;\n                android:textColor=&quot;@color\/text2&quot;\n                android:textSize=&quot;18sp&quot; \/&gt;\n\n            &lt;me.yezhou.lib.widget.WheelPickerView\n                android:id=&quot;@+id\/month_pv&quot;\n                android:layout_width=&quot;0dp&quot;\n                android:layout_height=&quot;160dp&quot;\n                android:layout_weight=&quot;2&quot; \/&gt;\n\n            &lt;TextView\n                android:layout_width=&quot;0dp&quot;\n                android:layout_height=&quot;match_parent&quot;\n                android:layout_weight=&quot;1.7&quot;\n                android:gravity=&quot;center&quot;\n                android:text=&quot;@string\/month&quot;\n                android:textColor=&quot;@color\/text2&quot;\n                android:textSize=&quot;18sp&quot; \/&gt;\n\n            &lt;me.yezhou.lib.widget.WheelPickerView\n                android:id=&quot;@+id\/day_pv&quot;\n                android:layout_width=&quot;0dp&quot;\n                android:layout_height=&quot;160dp&quot;\n                android:layout_weight=&quot;2&quot; \/&gt;\n\n            &lt;TextView\n                android:layout_width=&quot;0dp&quot;\n                android:layout_height=&quot;match_parent&quot;\n                android:layout_weight=&quot;1&quot;\n                android:gravity=&quot;center&quot;\n                android:text=&quot;@string\/day&quot;\n                android:textColor=&quot;@color\/text2&quot;\n                android:textSize=&quot;18sp&quot; \/&gt;\n\n            &lt;me.yezhou.lib.widget.WheelPickerView\n                android:id=&quot;@+id\/hour_pv&quot;\n                android:layout_width=&quot;0dp&quot;\n                android:layout_height=&quot;160dp&quot;\n                android:layout_weight=&quot;2&quot; \/&gt;\n\n            &lt;TextView\n                android:id=&quot;@+id\/hour_text&quot;\n                android:layout_width=&quot;0dp&quot;\n                android:layout_height=&quot;match_parent&quot;\n                android:layout_weight=&quot;1&quot;\n                android:gravity=&quot;center&quot;\n                android:text=&quot;@string\/hour&quot;\n                android:textColor=&quot;@color\/text2&quot;\n                android:textSize=&quot;18sp&quot; \/&gt;\n\n            &lt;me.yezhou.lib.widget.WheelPickerView\n                android:id=&quot;@+id\/minute_pv&quot;\n                android:layout_width=&quot;0dp&quot;\n                android:layout_height=&quot;160dp&quot;\n                android:layout_weight=&quot;2&quot; \/&gt;\n\n            &lt;TextView\n                android:id=&quot;@+id\/minute_text&quot;\n                android:layout_width=&quot;0dp&quot;\n                android:layout_height=&quot;match_parent&quot;\n                android:layout_weight=&quot;1&quot;\n                android:gravity=&quot;center&quot;\n                android:text=&quot;@string\/minute&quot;\n                android:textColor=&quot;@color\/text2&quot;\n                android:textSize=&quot;18sp&quot; \/&gt;\n\n        &lt;\/LinearLayout&gt;\n\n    &lt;\/RelativeLayout&gt;\n\n&lt;\/LinearLayout&gt;<\/code><\/pre>\n<h3>styles.xml<\/h3>\n<pre><code class=\"language-xml\">&lt;style name=&quot;date_picker_dialog&quot; parent=&quot;android:style\/Theme.Dialog&quot;&gt;\n    &lt;item name=&quot;android:windowFrame&quot;&gt;@null&lt;\/item&gt;\n    &lt;item name=&quot;android:windowNoTitle&quot;&gt;true&lt;\/item&gt;\n    &lt;item name=&quot;android:windowIsFloating&quot;&gt;true&lt;\/item&gt;\n    &lt;item name=&quot;android:windowContentOverlay&quot;&gt;@null&lt;\/item&gt;\n    &lt;item name=&quot;android:windowBackground&quot;&gt;@android:color\/white&lt;\/item&gt;\n&lt;\/style&gt;<\/code><\/pre>\n<h3>colors.xml<\/h3>\n<pre><code class=\"language-xml\">&lt;color name=&quot;text1&quot;&gt;#59B29C&lt;\/color&gt;\n&lt;color name=&quot;text2&quot;&gt;#333333&lt;\/color&gt;<\/code><\/pre>\n<h3>strings.xml<\/h3>\n<pre><code class=\"language-xml\">&lt;string name=&quot;confirm&quot;&gt;\u786e\u5b9a&lt;\/string&gt;\n&lt;string name=&quot;cancel&quot;&gt;\u53d6\u6d88&lt;\/string&gt;\n&lt;string name=&quot;date_picker_title&quot;&gt;\u8bf7\u9009\u62e9\u65e5\u671f&lt;\/string&gt;\n&lt;string name=&quot;year&quot;&gt;\u5e74&lt;\/string&gt;\n&lt;string name=&quot;month&quot;&gt;\u6708&lt;\/string&gt;\n&lt;string name=&quot;day&quot;&gt;\u65e5&lt;\/string&gt;\n&lt;string name=&quot;hour&quot;&gt;\u65f6&lt;\/string&gt;\n&lt;string name=&quot;minute&quot;&gt;\u5206&lt;\/string&gt;<\/code><\/pre>\n<h2>\u81ea\u5b9a\u4e49WheelPickerView<\/h2>\n<pre><code class=\"language-java\">public class WheelPickerView extends View {\n\n    private Context context;\n    \/**\n     * \u65b0\u589e\u5b57\u6bb5 \u63a7\u5236\u662f\u5426\u9996\u5c3e\u76f8\u63a5\u5faa\u73af\u663e\u793a \u9ed8\u8ba4\u4e3a\u5faa\u73af\u663e\u793a\n     *\/\n    private boolean loop = true;\n    \/**\n     * text\u4e4b\u95f4\u95f4\u8ddd\u548cminTextSize\u4e4b\u6bd4\n     *\/\n    public static final float MARGIN_ALPHA = 2.8f;\n    \/**\n     * \u81ea\u52a8\u56de\u6eda\u5230\u4e2d\u95f4\u7684\u901f\u5ea6\n     *\/\n    public static final float SPEED = 10;\n    private List&lt;String&gt; mDataList;\n    \/**\n     * \u9009\u4e2d\u7684\u4f4d\u7f6e\uff0c\u8fd9\u4e2a\u4f4d\u7f6e\u662fmDataList\u7684\u4e2d\u5fc3\u4f4d\u7f6e\uff0c\u4e00\u76f4\u4e0d\u53d8\n     *\/\n    private int mCurrentSelected;\n    private Paint mPaint, nPaint;\n    private float mMaxTextSize = 80;\n    private float mMinTextSize = 40;\n    private float mMaxTextAlpha = 255;\n    private float mMinTextAlpha = 120;\n    private int mViewHeight;\n    private int mViewWidth;\n    private float mLastDownY;\n    \/**\n     * \u6ed1\u52a8\u7684\u8ddd\u79bb\n     *\/\n    private float mMoveLen = 0;\n    private boolean isInit = false;\n    private boolean canScroll = true;\n    private OnSelectListener mSelectListener;\n    private Timer timer;\n    private MyTimerTask mTask;\n\n    private Handler updateHandler = new Handler() {\n        @Override\n        public void handleMessage(Message msg) {\n            if (Math.abs(mMoveLen) &lt; SPEED) {\n                mMoveLen = 0;\n                if (mTask != null) {\n                    mTask.cancel();\n                    mTask = null;\n                    performSelect();\n                }\n            } else {\n                \/\/ \u8fd9\u91ccmMoveLen \/ Math.abs(mMoveLen)\u662f\u4e3a\u4e86\u4fdd\u6709mMoveLen\u7684\u6b63\u8d1f\u53f7\uff0c\u4ee5\u5b9e\u73b0\u4e0a\u6eda\u6216\u4e0b\u6eda\n                mMoveLen = mMoveLen - mMoveLen \/ Math.abs(mMoveLen) * SPEED;\n            }\n            invalidate();\n        }\n    };\n\n    public WheelPickerView(Context context, AttributeSet attrs) {\n        super(context, attrs);\n        this.context = context;\n        init();\n    }\n\n    public void setOnSelectListener(OnSelectListener listener) {\n        mSelectListener = listener;\n    }\n\n    private void performSelect() {\n        if (mSelectListener != null) {\n            mSelectListener.onSelect(mCurrentSelected, mDataList.get(mCurrentSelected));\n        }\n    }\n\n    public void setData(List&lt;String&gt; datas) {\n        mDataList = datas;\n        mCurrentSelected = datas.size() \/ 4;\n        invalidate();\n    }\n\n    \/**\n     * \u9009\u62e9\u9009\u4e2d\u7684item\u7684index\n     *\/\n    public void setSelected(int selected) {\n        mCurrentSelected = selected;\n        if (loop) {\n            int distance = mDataList.size() \/ 2 - mCurrentSelected;\n            if (distance &lt; 0) {\n                for (int i = 0; i &lt; -distance; i++) {\n                    moveHeadToTail();\n                    mCurrentSelected--;\n                }\n            } else if (distance &gt; 0) {\n                for (int i = 0; i &lt; distance; i++) {\n                    moveTailToHead();\n                    mCurrentSelected++;\n                }\n            }\n        }\n        Log.i(&quot;yezhou&quot;, &quot;setSelected: &quot; + mCurrentSelected);\n        invalidate();\n    }\n\n    \/**\n     * \u9009\u62e9\u9009\u4e2d\u7684\u5185\u5bb9\n     *\/\n    public void setSelected(String mSelectItem) {\n        for (int i = 0; i &lt; mDataList.size(); i++) {\n            if (mDataList.get(i).equals(mSelectItem)) {\n                setSelected(i);\n                break;\n            }\n        }\n    }\n\n    private void moveHeadToTail() {\n        if (loop) {\n            String head = mDataList.get(0);\n            mDataList.remove(0);\n            mDataList.add(head);\n        }\n    }\n\n    private void moveTailToHead() {\n        if (loop) {\n            String tail = mDataList.get(mDataList.size() - 1);\n            mDataList.remove(mDataList.size() - 1);\n            mDataList.add(0, tail);\n        }\n    }\n\n    @Override\n    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n        super.onMeasure(widthMeasureSpec, heightMeasureSpec);\n        mViewHeight = getMeasuredHeight();\n        mViewWidth = getMeasuredWidth();\n        \/\/ \u6309\u7167View\u7684\u9ad8\u5ea6\u8ba1\u7b97\u5b57\u4f53\u5927\u5c0f\n        mMaxTextSize = mViewHeight \/ 7f;\n        mMinTextSize = mMaxTextSize \/ 2.2f;\n        isInit = true;\n        invalidate();\n    }\n\n    private void init() {\n        timer = new Timer();\n        mDataList = new ArrayList&lt;&gt;();\n        \/\/\u7b2c\u4e00\u4e2apaint\n        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);\n        mPaint.setStyle(Style.FILL);\n        mPaint.setTextAlign(Align.CENTER);\n        mPaint.setColor(ContextCompat.getColor(context, R.color.text1));\n        \/\/\u7b2c\u4e8c\u4e2apaint\n        nPaint = new Paint(Paint.ANTI_ALIAS_FLAG);\n        nPaint.setStyle(Style.FILL);\n        nPaint.setTextAlign(Align.CENTER);\n        nPaint.setColor(ContextCompat.getColor(context, R.color.text2));\n    }\n\n    @Override\n    protected void onDraw(Canvas canvas) {\n        super.onDraw(canvas);\n        \/\/ \u6839\u636eindex\u7ed8\u5236view\n        if (isInit) {\n            drawData(canvas);\n        }\n    }\n\n    private void drawData(Canvas canvas) {\n        \/\/ \u5148\u7ed8\u5236\u9009\u4e2d\u7684text\u518d\u5f80\u4e0a\u5f80\u4e0b\u7ed8\u5236\u5176\u4f59\u7684text\n        float scale = parabola(mViewHeight \/ 4.0f, mMoveLen);\n        float size = (mMaxTextSize - mMinTextSize) * scale + mMinTextSize;\n        mPaint.setTextSize(size);\n        mPaint.setAlpha((int) ((mMaxTextAlpha - mMinTextAlpha) * scale + mMinTextAlpha));\n        \/\/ text\u5c45\u4e2d\u7ed8\u5236\uff0c\u6ce8\u610fbaseline\u7684\u8ba1\u7b97\u624d\u80fd\u8fbe\u5230\u5c45\u4e2d\uff0cy\u503c\u662ftext\u4e2d\u5fc3\u5750\u6807\n        float x = (float) (mViewWidth \/ 2.0);\n        float y = (float) (mViewHeight \/ 2.0 + mMoveLen);\n        FontMetricsInt fmi = mPaint.getFontMetricsInt();\n        float baseline = (float) (y - (fmi.bottom \/ 2.0 + fmi.top \/ 2.0));\n\n        canvas.drawText(mDataList.get(mCurrentSelected), x, baseline, mPaint);\n        \/\/ \u7ed8\u5236\u4e0a\u65b9data\n        for (int i = 1; (mCurrentSelected - i) &gt;= 0; i++) {\n            drawOtherText(canvas, i, -1);\n        }\n        \/\/ \u7ed8\u5236\u4e0b\u65b9data\n        for (int i = 1; (mCurrentSelected + i) &lt; mDataList.size(); i++) {\n            drawOtherText(canvas, i, 1);\n        }\n    }\n\n    \/**\n     * @param position \u8ddd\u79bbmCurrentSelected\u7684\u5dee\u503c\n     * @param type     1\u8868\u793a\u5411\u4e0b\u7ed8\u5236\uff0c-1\u8868\u793a\u5411\u4e0a\u7ed8\u5236\n     *\/\n    private void drawOtherText(Canvas canvas, int position, int type) {\n        float d = MARGIN_ALPHA * mMinTextSize * position + type * mMoveLen;\n        float scale = parabola(mViewHeight \/ 4.0f, d);\n        float size = (mMaxTextSize - mMinTextSize) * scale + mMinTextSize;\n        nPaint.setTextSize(size);\n        nPaint.setAlpha((int) ((mMaxTextAlpha - mMinTextAlpha) * scale + mMinTextAlpha));\n        float y = (float) (mViewHeight \/ 2.0 + type * d);\n        FontMetricsInt fmi = nPaint.getFontMetricsInt();\n        float baseline = (float) (y - (fmi.bottom \/ 2.0 + fmi.top \/ 2.0));\n        canvas.drawText(mDataList.get(mCurrentSelected + type * position),\n                (float) (mViewWidth \/ 2.0), baseline, nPaint);\n    }\n\n    \/**\n     * \u629b\u7269\u7ebf\n     *\n     * @param zero \u96f6\u70b9\u5750\u6807\n     * @param x    \u504f\u79fb\u91cf\n     *\/\n    private float parabola(float zero, float x) {\n        float f = (float) (1 - Math.pow(x \/ zero, 2));\n        return f &lt; 0 ? 0 : f;\n    }\n\n    @Override\n    public boolean onTouchEvent(MotionEvent event) {\n        switch (event.getActionMasked()) {\n            case MotionEvent.ACTION_DOWN:\n                doDown(event);\n                break;\n\n            case MotionEvent.ACTION_MOVE:\n                mMoveLen += (event.getY() - mLastDownY);\n                if (mMoveLen &gt; MARGIN_ALPHA * mMinTextSize \/ 2) {\n                    if (!loop &amp;&amp; mCurrentSelected == 0) {\n                        mLastDownY = event.getY();\n                        invalidate();\n                        return true;\n                    }\n                    if (!loop) {\n                        mCurrentSelected--;\n                    }\n                    \/\/ \u5f80\u4e0b\u6ed1\u8d85\u8fc7\u79bb\u5f00\u8ddd\u79bb\n                    moveTailToHead();\n                    mMoveLen = mMoveLen - MARGIN_ALPHA * mMinTextSize;\n                } else if (mMoveLen &lt; -MARGIN_ALPHA * mMinTextSize \/ 2) {\n                    if (mCurrentSelected == mDataList.size() - 1) {\n                        mLastDownY = event.getY();\n                        invalidate();\n                        return true;\n                    }\n                    if (!loop) {\n                        mCurrentSelected++;\n                    }\n                    \/\/ \u5f80\u4e0a\u6ed1\u8d85\u8fc7\u79bb\u5f00\u8ddd\u79bb\n                    moveHeadToTail();\n                    mMoveLen = mMoveLen + MARGIN_ALPHA * mMinTextSize;\n                }\n                mLastDownY = event.getY();\n                invalidate();\n                break;\n\n            case MotionEvent.ACTION_UP:\n                doUp();\n                break;\n        }\n        return true;\n    }\n\n    private void doDown(MotionEvent event) {\n        if (mTask != null) {\n            mTask.cancel();\n            mTask = null;\n        }\n        mLastDownY = event.getY();\n    }\n\n    private void doUp() {\n        \/\/ \u62ac\u8d77\u624b\u540emCurrentSelected\u7684\u4f4d\u7f6e\u7531\u5f53\u524d\u4f4d\u7f6emove\u5230\u4e2d\u95f4\u9009\u4e2d\u4f4d\u7f6e\n        if (Math.abs(mMoveLen) &lt; 0.0001) {\n            mMoveLen = 0;\n            return;\n        }\n        if (mTask != null) {\n            mTask.cancel();\n            mTask = null;\n        }\n        mTask = new MyTimerTask(updateHandler);\n        timer.schedule(mTask, 0, 10);\n    }\n\n    class MyTimerTask extends TimerTask {\n        Handler handler;\n\n        public MyTimerTask(Handler handler) {\n            this.handler = handler;\n        }\n\n        @Override\n        public void run() {\n            handler.sendMessage(handler.obtainMessage());\n        }\n    }\n\n    public interface OnSelectListener {\n        void onSelect(int index, String text);\n    }\n\n    public void setCanScroll(boolean canScroll) {\n        this.canScroll = canScroll;\n    }\n\n    @Override\n    public boolean dispatchTouchEvent(MotionEvent event) {\n        return canScroll &amp;&amp; super.dispatchTouchEvent(event);\n    }\n\n    \/**\n     * \u63a7\u5236\u5185\u5bb9\u662f\u5426\u9996\u5c3e\u76f8\u8fde\n     *\/\n    public void setIsLoop(boolean isLoop) {\n        loop = isLoop;\n    }\n\n}<\/code><\/pre>\n<h2>\u6548\u679c\u56fe<\/h2>\n<p><img decoding=\"async\" src=\"http:\/\/www.yezhou.me\/AppBlog\/images\/Android\/\u6eda\u52a8DatePicker.png\" alt=\"\u6eda\u52a8DatePicker\" \/><\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u57fa\u672c\u4f7f\u7528 private TextView mTvBirthday; private MyDatePicker [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[119],"tags":[],"class_list":["post-1625","post","type-post","status-publish","format-standard","hentry","category-android-ui"],"_links":{"self":[{"href":"https:\/\/www.appblog.cn\/index.php\/wp-json\/wp\/v2\/posts\/1625","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.appblog.cn\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.appblog.cn\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.appblog.cn\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.appblog.cn\/index.php\/wp-json\/wp\/v2\/comments?post=1625"}],"version-history":[{"count":0,"href":"https:\/\/www.appblog.cn\/index.php\/wp-json\/wp\/v2\/posts\/1625\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.appblog.cn\/index.php\/wp-json\/wp\/v2\/media?parent=1625"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.appblog.cn\/index.php\/wp-json\/wp\/v2\/categories?post=1625"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.appblog.cn\/index.php\/wp-json\/wp\/v2\/tags?post=1625"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}