Android基于wheelView實現(xiàn)自定義日期選擇器
本文實例為大家分享了Android實現(xiàn)自定義日期選擇器的具體代碼,供大家參考,具體內(nèi)容如下
項目要求效果圖:

要求 “6月20 星期五” 這一項作為一個整體可以滑動,”7時”、”48分”分別作為一個滑動整體。
系統(tǒng)自帶的DatePicker、TimePicker大家都知道,只有這種效果:

百度了很多,試了NumberPicker等都不行,本來打算自己寫。網(wǎng)友推薦了一個開源組件WheelView,下下來試了試,發(fā)現(xiàn)他已經(jīng)定義的很完善了,在他的基礎(chǔ)上拓展很容易。
現(xiàn)將基于wheelView自定義日期選擇器記錄如下:
一.首先要了解WheelView為我們提供了什么:

除了我寫的”DateObject”與”StringWheelAdapter”,其余都是WheelView提供的,
1. WheelView.java :可滾動的組件
主要方法:
setAdapter(new StringWheelAdapter(dateList, 7)); //設(shè)置Adapter setVisibleItems(3); //設(shè)置顯示幾行數(shù)據(jù) setCyclic(true); //設(shè)置是否循環(huán)顯示數(shù)據(jù) addChangingListener(onDaysChangedListener) //設(shè)置滑動監(jiān)聽器
2. WheelAdapter.java : 滑動組件的適配器的接口,子類適配器用于裝載數(shù)據(jù)
public interface WheelAdapter {
/**
* Gets items count
* @return the count of wheel items
*/
public int getItemsCount();
/**
* Gets a wheel item by index.
*
* @param index the item index
* @return the wheel item text or null
*/
public String getItem(int index);
/**
* Gets maximum item length. It is used to determine the wheel width.
* If -1 is returned there will be used the default wheel width.
*
* @return the maximum item length or -1
*/
public int getMaximumLength();
}
3. OnWheelChangedListener.java : 滑動監(jiān)聽器接口
public interface OnWheelChangedListener {
/**
* Callback method to be invoked when current item changed
* @param wheel the wheel view whose state has changed
* @param oldValue the old value of current item
* @param newValue the new value of current item
*/
void onChanged(WheelView wheel, int oldValue, int newValue);
}
4.OnWheelScrollListener.java :滾動監(jiān)聽器接口(暫時沒用到)
5.NumericWheelAdapter.java : 當(dāng)滾動內(nèi)容為純數(shù)字時調(diào)用的適配器
6.DateObject.java : 日期實體類,用于存儲、獲取選擇的數(shù)據(jù)
package kankan.wheel.widget;
import java.util.Calendar;
public class DateObject extends Object{
private int year ;
private int month;
private int day;
private int week;
private int hour;
private int minute;
private String listItem;
/**
* 日期對象的4個參數(shù)構(gòu)造器,用于設(shè)置日期
* @param year
* @param month
* @param day
* @author sxzhang
*/
public DateObject(int year2, int month2, int day2,int week2) {
super();
this.year = year2;
int maxDayOfMonth = Calendar.getInstance().getActualMaximum(Calendar.DAY_OF_MONTH);
if(day2 > maxDayOfMonth){
this.month = month2 + 1;
this.day = day2 % maxDayOfMonth;
}else{
this.month = month2;
this.day = day2;
}
this.week = week2 % 7 == 0 ? 7 : week2 % 7;
if(day == Calendar.getInstance().get(Calendar.DAY_OF_MONTH)){
this.listItem = String.format("%02d", this.month) +"月" + String.format("%02d", this.day) +
"日 "+ " 今天 ";
}else{
this.listItem = String.format("%02d", this.month) +"月" + String.format("%02d", this.day) +
"日 "+ getDayOfWeekCN(week);
}
}
/**
* 日期對象的2個參數(shù)構(gòu)造器,用于設(shè)置時間
* @param hour2
* @param minute2
* @param isHourType true:傳入的是hour; false: 傳入的是minute
* @author sxzhang
*/
public DateObject(int hour2,int minute2,boolean isHourType) {
super();
if(isHourType == true && hour2 != -1){ //設(shè)置小時
if(hour2 > 24){
this.hour = hour2 % 24;
}else
this.hour = hour2;
this.listItem = this.hour + "時";
}else if(isHourType == false && minute2 != -1){ //設(shè)置分鐘
if(minute2 > 60)
this.minute = minute2 % 60;
else
this.minute = minute2;
this.listItem = this.minute + "分";
}
}
public int getHour() {
return hour;
}
public void setHour(int hour) {
this.hour = hour;
}
public int getMinute() {
return minute;
}
public void setMinute(int minute) {
this.minute = minute;
}
public int getWeek() {
return week;
}
public void setWeek(int week) {
this.week = week;
}
public int getYear() {
return year;
}
public void setYear(int year) {
this.year = year;
}
public int getMonth() {
return month;
}
public void setMonth(int month) {
this.month = month;
}
public int getDay() {
return day;
}
public void setDay(int day) {
this.day = day;
}
public String getListItem() {
return listItem;
}
public void setListItem(String listItem) {
this.listItem = listItem;
}
/**
* 根據(jù)day_of_week得到漢字星期
* @return
*/
public static String getDayOfWeekCN(int day_of_week){
String result = null;
switch(day_of_week){
case 1:
result = "星期日";
break;
case 2:
result = "星期一";
break;
case 3:
result = "星期二";
break;
case 4:
result = "星期三";
break;
case 5:
result = "星期四";
break;
case 6:
result = "星期五";
break;
case 7:
result = "星期六";
break;
default:
break;
}
return result;
}
}
7.StringWheelAdapter.java :一會兒將定義的滾動內(nèi)容為字符串的適配器,當(dāng)內(nèi)容為字符串時我們就可以隨意拓展滑動部分的內(nèi)容
package kankan.wheel.widget;
import java.util.ArrayList;
/**
* The simple String Array wheel adapter
*
*/
public class StringWheelAdapter implements WheelAdapter {
/** The default items length */
public static final int DEFAULT_LENGTH = -1;
// items
private ArrayList<DateObject> list;
// length
private int length;
/**
* Constructor
* @param items the items
* @param length the max items length
*/
public StringWheelAdapter(ArrayList<DateObject> list, int length) {
this.list = list;
this.length = length;
}
@Override
public String getItem(int index) {
if (index >= 0 && index < list.size()) {
return list.get(index).getListItem();
}
return null;
}
@Override
public int getItemsCount() {
return list.size();
}
@Override
public int getMaximumLength() {
return length;
}
}
二.了解以后就可以使用他定義我們需要的了。
1.首先要做的是這個效果的部分:

我們將其命名為DatePicker:
package com.sxkeji.timeswitch.widget;
import java.util.ArrayList;
import java.util.Calendar;
import kankan.wheel.widget.DateObject;
import kankan.wheel.widget.OnWheelChangedListener;
import kankan.wheel.widget.StringWheelAdapter;
import kankan.wheel.widget.WheelView;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.LinearLayout;
/**
* 自定義的日期選擇器
* @author sxzhang
*
*/
public class DatePicker extends LinearLayout {
private Calendar calendar = Calendar.getInstance();
private WheelView newDays;
private ArrayList<DateObject> dateList ;
private OnChangeListener onChangeListener; //onChangeListener
private final int MARGIN_RIGHT = 20;
private DateObject dateObject; //日期數(shù)據(jù)對象
//Constructors
public DatePicker(Context context) {
super(context);
init(context);
}
public DatePicker(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
/**
* 初始化
* @param context
*/
private void init(Context context){
int year = calendar.get(Calendar.YEAR);
int month = calendar.get(Calendar.MONTH) + 1;
int day = calendar.get(Calendar.DAY_OF_MONTH);
int week = calendar.get(Calendar.DAY_OF_WEEK);
dateList = new ArrayList<DateObject>();
for (int i = 0; i < 7; i++) {
dateObject = new DateObject(year, month, day+i, week+i);
dateList.add(dateObject);
}
newDays = new WheelView(context);
LayoutParams newDays_param = new LayoutParams(300,LayoutParams.WRAP_CONTENT);
newDays_param.setMargins(0, 0, MARGIN_RIGHT, 0);
newDays.setLayoutParams(newDays_param);
newDays.setAdapter(new StringWheelAdapter(dateList, 7));
newDays.setVisibleItems(3);
newDays.setCyclic(true);
newDays.addChangingListener(onDaysChangedListener);
addView(newDays);
}
/**
* 滑動改變監(jiān)聽器
*/
private OnWheelChangedListener onDaysChangedListener = new OnWheelChangedListener(){
@Override
public void onChanged(WheelView mins, int oldValue, int newValue) {
calendar.set(Calendar.DAY_OF_MONTH, newValue + 1);
change();
}
};
/**
* 滑動改變監(jiān)聽器回調(diào)的接口
*/
public interface OnChangeListener {
void onChange(int year, int month, int day, int day_of_week);
}
/**
* 設(shè)置滑動改變監(jiān)聽器
* @param onChangeListener
*/
public void setOnChangeListener(OnChangeListener onChangeListener){
this.onChangeListener = onChangeListener;
}
/**
* 滑動最終調(diào)用的方法
*/
private void change(){
if(onChangeListener!=null){
onChangeListener.onChange(
dateList.get(newDays.getCurrentItem()).getYear(),
dateList.get(newDays.getCurrentItem()).getMonth(),
dateList.get(newDays.getCurrentItem()).getDay(),
dateList.get(newDays.getCurrentItem()).getWeek());
}
}
/**
* 根據(jù)day_of_week得到漢字星期
* @return
*/
public static String getDayOfWeekCN(int day_of_week){
String result = null;
switch(day_of_week){
case 1:
result = "星期日";
break;
case 2:
result = "星期一";
break;
case 3:
result = "星期二";
break;
case 4:
result = "星期三";
break;
case 5:
result = "星期四";
break;
case 6:
result = "星期五";
break;
case 7:
result = "星期六";
break;
default:
break;
}
return result;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
2.然后要做的是這個效果的部分

我們命名為TimePicker:
package com.sxkeji.timeswitch.widget;
import java.util.ArrayList;
import java.util.Calendar;
import kankan.wheel.widget.DateObject;
import kankan.wheel.widget.OnWheelChangedListener;
import kankan.wheel.widget.StringWheelAdapter;
import kankan.wheel.widget.WheelView;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.LinearLayout;
/**
* 自定義的時間選擇器
* @author sxzhang
*
*/
public class TimePicker extends LinearLayout{
private Calendar calendar = Calendar.getInstance();
private WheelView hours, mins; //Wheel picker
private OnChangeListener onChangeListener; //onChangeListener
private final int MARGIN_RIGHT = 15; //調(diào)整文字右端距離
private ArrayList<DateObject> hourList,minuteList;
private DateObject dateObject; //時間數(shù)據(jù)對象
//Constructors
public TimePicker(Context context) {
super(context);
init(context);
}
public TimePicker(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
/**
* 初始化
* @param context
*/
private void init(Context context){
int hour = calendar.get(Calendar.HOUR_OF_DAY);
int minute = calendar.get(Calendar.MINUTE);
hourList = new ArrayList<DateObject>();
minuteList = new ArrayList<DateObject>();
for (int i = 0; i < 24; i++) {
dateObject = new DateObject(hour+i,-1,true);
hourList.add(dateObject);
}
for (int j = 0; j < 60; j++) {
dateObject = new DateObject(-1,minute+j,false);
minuteList.add(dateObject);
}
//小時選擇器
hours = new WheelView(context);
LayoutParams lparams_hours = new LayoutParams(80,LayoutParams.WRAP_CONTENT);
lparams_hours.setMargins(0, 0, MARGIN_RIGHT, 0);
hours.setLayoutParams(lparams_hours);
hours.setAdapter(new StringWheelAdapter(hourList, 24));
hours.setVisibleItems(3);
hours.setCyclic(true);
hours.addChangingListener(onHoursChangedListener);
addView(hours);
//分鐘選擇器
mins = new WheelView(context);
mins.setLayoutParams(new LayoutParams(80,LayoutParams.WRAP_CONTENT));
mins.setAdapter(new StringWheelAdapter(minuteList,60));
mins.setVisibleItems(3);
mins.setCyclic(true);
mins.addChangingListener(onMinsChangedListener);
addView(mins);
}
//listeners
private OnWheelChangedListener onHoursChangedListener = new OnWheelChangedListener(){
@Override
public void onChanged(WheelView hours, int oldValue, int newValue) {
calendar.set(Calendar.HOUR_OF_DAY, newValue);
change();
}
};
private OnWheelChangedListener onMinsChangedListener = new OnWheelChangedListener(){
@Override
public void onChanged(WheelView mins, int oldValue, int newValue) {
calendar.set(Calendar.MINUTE, newValue);
change();
}
};
/**
* 滑動改變監(jiān)聽器回調(diào)的接口
*/
public interface OnChangeListener {
void onChange(int hour, int munite);
}
/**
* 設(shè)置滑動改變監(jiān)聽器
* @param onChangeListener
*/
public void setOnChangeListener(OnChangeListener onChangeListener){
this.onChangeListener = onChangeListener;
}
/**
* 滑動最終調(diào)用的方法
*/
private void change(){
if(onChangeListener!=null){
onChangeListener.onChange(getHourOfDay(), getMinute());
}
}
/**
* 獲取小時
* @return
*/
public int getHourOfDay(){
return hourList.get(hours.getCurrentItem()).getHour();
}
/**
* 獲取分鐘
* @return
*/
public int getMinute(){
return minuteList.get(mins.getCurrentItem()).getMinute();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
3.最后就可以直接使用了,我這里主界面是一個button,點擊后彈出popupWindow顯示日期選擇器。布局文件及主Activity如下:
popupWindow布局文件:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" android:background="#FFF"> <View android:layout_width="match_parent" android:layout_height="1dp" android:background="#f5f5f5"/> <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:padding="10dp"> <TextView android:id="@+id/tv_cancel" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="取消" android:layout_marginLeft="10dp" android:layout_alignParentLeft="true" android:textColor="#000000" android:textSize="20sp" /> <TextView android:id="@+id/tv_ok" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="確定" android:layout_marginRight="10dp" android:layout_alignParentRight="true" android:textColor="#000000" android:textSize="20sp" /> </RelativeLayout> <View android:layout_width="match_parent" android:layout_height="1dp" android:background="#f5f5f5"/> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:layout_marginTop="10dp" android:padding="20dp"> <com.sxkeji.timeswitch.widget.DatePicker android:id="@+id/dp_test" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="3" android:gravity="center" android:layout_gravity="center_horizontal"/> <com.sxkeji.timeswitch.widget.TimePicker android:id="@+id/tp_test" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="2" android:gravity="center" android:layout_gravity="center_horizontal"/> </LinearLayout> </LinearLayout>
主界面布局文件:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:background="#FFF" android:id="@+id/Rl_all"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:layout_alignParentBottom="true" android:padding="10dp" android:gravity="center"> <View android:layout_width="match_parent" android:layout_height="1dp" android:background="#f5f5f5"/> <Button android:id="@+id/btn_naozhong" android:layout_width="30dp" android:layout_height="30dp" android:background="@drawable/naozhong" /> </LinearLayout> </RelativeLayout>
Activity代碼:
package com.sxkeji.timeswitch.activity;
import java.util.Calendar;
import org.unism.wang.R;
import com.sxkeji.timeswitch.widget.DatePicker;
import com.sxkeji.timeswitch.widget.TimePicker;
import android.app.Activity;
import android.os.Bundle;
import android.view.Gravity;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.PopupWindow;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
/**
* 主頁面
* @author sxzhang
*/
public class MyPickerActivity extends Activity {
private Calendar calendar;
private DatePicker dp_test;
private TimePicker tp_test;
private TextView tv_ok,tv_cancel; //確定、取消button
private Button btn_naozhong;
private PopupWindow pw;
private String selectDate,selectTime;
//選擇時間與當(dāng)前時間,用于判斷用戶選擇的是否是以前的時間
private int currentHour,currentMinute,currentDay,selectHour,selectMinute,selectDay;
//整體布局
private RelativeLayout Rl_all;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Rl_all = (RelativeLayout) findViewById(R.id.Rl_all);
btn_naozhong = (Button) findViewById(R.id.btn_naozhong);
calendar = Calendar.getInstance();
btn_naozhong.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
View view = View.inflate(MyPickerActivity.this, R.layout.dialog_select_time, null);
selectDate = calendar.get(Calendar.YEAR) + "年" + calendar.get(Calendar.MONTH) + "月"
+ calendar.get(Calendar.DAY_OF_MONTH) + "日"
+ DatePicker.getDayOfWeekCN(calendar.get(Calendar.DAY_OF_WEEK));
//選擇時間與當(dāng)前時間的初始化,用于判斷用戶選擇的是否是以前的時間,如果是,彈出toss提示不能選擇過去的時間
selectDay = currentDay = calendar.get(Calendar.DAY_OF_MONTH);
selectMinute = currentMinute = calendar.get(Calendar.MINUTE);
selectHour = currentHour = calendar.get(Calendar.HOUR_OF_DAY);
selectTime = currentHour + "點" + ((currentMinute < 10)?("0"+currentMinute):currentMinute) + "分";
dp_test = (DatePicker)view.findViewById(R.id.dp_test);
tp_test = (TimePicker)view.findViewById(R.id.tp_test);
tv_ok = (TextView) view.findViewById(R.id.tv_ok);
tv_cancel = (TextView) view.findViewById(R.id.tv_cancel);
//設(shè)置滑動改變監(jiān)聽器
dp_test.setOnChangeListener(dp_onchanghelistener);
tp_test.setOnChangeListener(tp_onchanghelistener);
pw = new PopupWindow(view, LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT, true);
// //設(shè)置這2個使得點擊pop以外區(qū)域可以去除pop
// pw.setOutsideTouchable(true);
// pw.setBackgroundDrawable(new BitmapDrawable());
//出現(xiàn)在布局底端
pw.showAtLocation(Rl_all, 0, 0, Gravity.END);
//點擊確定
tv_ok.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
if(selectDay == currentDay ){ //在當(dāng)前日期情況下可能出現(xiàn)選中過去時間的情況
if(selectHour < currentHour){
Toast.makeText(getApplicationContext(), "不能選擇過去的時間\n 請重新選擇", 0).show();
}else if( (selectHour == currentHour) && (selectMinute < currentMinute) ){
Toast.makeText(getApplicationContext(), "不能選擇過去的時間\n 請重新選擇", 0).show();
}else{
Toast.makeText(getApplicationContext(), selectDate+selectTime, 0).show();
pw.dismiss();
}
}else{
Toast.makeText(getApplicationContext(), selectDate+selectTime, 0).show();
pw.dismiss();
}
}
});
//點擊取消
tv_cancel.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
pw.dismiss();
}
});
}
});
}
//listeners
DatePicker.OnChangeListener dp_onchanghelistener = new DatePicker.OnChangeListener() {
@Override
public void onChange(int year, int month, int day, int day_of_week) {
selectDay = day;
selectDate = year + "年" + month + "月" + day + "日" + DatePicker.getDayOfWeekCN(day_of_week);
}
};
TimePicker.OnChangeListener tp_onchanghelistener = new TimePicker.OnChangeListener() {
@Override
public void onChange(int hour, int minute) {
selectTime = hour + "點" + ((minute < 10)?("0"+minute):minute) + "分";
selectHour = hour;
selectMinute = minute;
}
};
}
最終效果圖:

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Android ViewPager無限循環(huán)滑動并可自動滾動完整實例
對于Android ViewPager廣告頁可無限循環(huán)滑動并可自動滾動帶有小圓點的這個功能很多APP都有這個功能,這里為大家提供了完整的實例代碼2018-03-03
自定義toast外形,多次點擊不會總是彈出toast的實現(xiàn)方法
下面小編就為大家?guī)硪黄远xtoast外形,多次點擊不會總是彈出toast的實現(xiàn)方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-04-04
Android自定義ProgressBar實現(xiàn)漂亮的進度提示框
這篇文章主要為大家詳細介紹了Android自定義ProgressBar實現(xiàn)漂亮的進度提示框,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-06-06
Android代碼實現(xiàn)AdapterViews和RecyclerView無限滾動
這篇文章主要為大家詳細介紹了Android代碼實現(xiàn)AdapterViews和RecyclerView無限滾動的相關(guān)資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-07-07
深入理解與運行Android Jetpack組件之ViewModel
ViewModel是Android Jetpack組件之一,是一種用于管理UI相關(guān)數(shù)據(jù)的架構(gòu)組件,它能夠幫助開發(fā)者實現(xiàn)優(yōu)雅的數(shù)據(jù)驅(qū)動和生命周期管理,本文將深入淺出地介紹ViewModel的使用和原理,帶你一步步掌握這個強大的組件2023-08-08

