文章目錄
- 重複造輪子?
- 說一說實作過程
-
- 內建WheelView庫
- DatePicker的實作
-
- DatePicker
- Builder
- OnDatePickedListener
- WheelView
- DatePicker的使用
- 附以完整源碼
-
- DatePicker.class
- layout_datepicker.xml
明日複明日,明日何其多————困了,但不能把工作推到明天了,拭心大佬是我學習的榜樣。
重複造輪子?
也不算是。
Bigkoo的Android-PickerView庫,很好的支援了日期、時間、地區等各種類型的關聯選擇。目前的需求是隻需要年月二級關聯查詢,不想內建整個Android-PickerView,畢竟大部分功能用不到,白白增加代碼量,于是就基于Bigkoo的WheelView庫實作了一個簡便的年月選擇器DatePicker。
說一說實作過程
內建WheelView庫
implementation 'com.contrarywind:wheelview:4.0.9'
Bigkoo的Github位址:Bigkoo Github
他的Android-PickerView庫寫的很棒,功能全,性能好,單看看Star數,就知道此庫得到了Android開發者的廣泛認可。
WheelView内置于Android-PickerView,也可單獨內建。它是一個自定義View,800行源碼,立一個flag:讀它的源碼,下周内出一篇源碼分析的文章。
DatePicker的實作
DatePicker
DatePicker繼承于DialogFragment。采用Fragment彈出框,更便于自定義DatePicker布局,以及在任意頁面進行調用,并可像管理Fragment一樣管理DatePicker,很友善。
Builder
在DatePicker中用了一個靜态内部類Builder,用于建構DatePicker及初始化。在Builder中開放了幾個方法:
-
setYearRange(int minYear, int maxYear)
設定年份可選擇的範圍
-
setCancelable(boolean cancelable)
設定點選DatePicker外區域是否可取消DatePicker
-
loopYear(boolean loop)
設定年份是否可循環展示,預設為true
-
loopMonth(boolean loop)
設定月份是否可循環展示,預設為true
-
build()
建構并初始化DatePicker
public static class Builder {
private OnDatePickedListener listener;
public Builder(OnDatePickedListener listener) {
this.listener = listener;
}
private int minYear;
private int maxYear;
private boolean loopYear;
private boolean loopMonth;
private boolean cancelable;
public Builder setYearRange(int minYear, int maxYear) {
this.minYear = minYear;
this.maxYear = maxYear;
return this;
}
public Builder setCancelable(boolean cancelable) {
this.cancelable = cancelable;
return this;
}
public Builder loopYear(boolean loop) {
this.loopYear = loop;
return this;
}
public Builder loopMonth(boolean loop) {
this.loopMonth = loop;
return this;
}
public DatePicker build() {
if (minYear > maxYear) {
throw new IllegalArgumentException();//不允許minYear > maxYear
}
return DatePicker.newInstance(this);
}
}
OnDatePickedListener
DatePicker的監聽接口,内有一回調方法onDatePickCompleted(int year, int month),用于年月選擇後的選擇值回傳。
public interface OnDatePickedListener {
void onDatePickCompleted(int year, int month);
}
WheelView
xml布局:
<com.contrarywind.view.WheelView
android:id="@+id/wheelview"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
Java代碼:
WheelView wheelView = findViewById(R.id.wheelview);
wheelView.setCyclic(false); //是否循環展示
final List<String> mOptionsItems = new ArrayList<>();
mOptionsItems.add("item0");
mOptionsItems.add("item1");
mOptionsItems.add("item2");
wheelView.setAdapter(new ArrayWheelAdapter(mOptionsItems));
wheelView.setOnItemSelectedListener(new OnItemSelectedListener() {
@Override
public void onItemSelected(int index) {
Toast.makeText(MainActivity.this, "" + mOptionsItems.get(index), Toast.LENGTH_SHORT).show();
}
});
DatePicker的使用
private String TAG = DemoActivity.class.getName();
DatePicker datePicker = new DatePicker.Builder(new DatePicker.OnDatePickedListener() {
@Override
public void onDatePickCompleted(int year, int month) {
Toast.makeText(LoginStatisticsActivity.this, "selectedYear:" + year + "--selectedMonth" + month, Toast.LENGTH_SHORT).show();
}
})
.setYearRange(1990, Calendar.getInstance().get(Calendar.YEAR))//1990——目前年份
.setCancelable(true)
.loopYear(false)
.loopMonth(false)
.build();
datePicker.showPicker(DemoActivity.this, TAG);
附以完整源碼
DatePicker.class
package com.gd.terminalmanager.view;
import android.app.Activity;
import android.app.DialogFragment;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.DisplayMetrics;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.TextView;
import com.contrarywind.adapter.WheelAdapter;
import com.contrarywind.listener.OnItemSelectedListener;
import com.contrarywind.view.WheelView;
import com.gd.terminalmanager.R;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
/**
* 隻提供年和月的選擇
*
* @author ZuoHailong
* @date 2019/3/19.
*/
public class DatePicker extends DialogFragment {
private static final int DEFAULT_MAX_YEAR = 2050;
private static final int DEFAULT_MIN_YEAR = 1900;
private WheelView wheelViewYear, wheelViewMonth;
private TextView tv_sure, tv_cancle;
private int minYear; // min year
private int maxYear; // max year
private boolean cancelable, loopYear, loopMonth;
private OnDatePickedListener listener;
private int selectedYear, selectedMonth;
private static Calendar calendar;
//private,隻允許通過Builder建構DatePicker
private static DatePicker newInstance(Builder builder) {
calendar = Calendar.getInstance();
DatePicker datePicker = new DatePicker();
datePicker.minYear = builder.minYear < DatePicker.DEFAULT_MIN_YEAR ? DatePicker.DEFAULT_MIN_YEAR : builder.minYear;
datePicker.maxYear = builder.maxYear > DatePicker.DEFAULT_MAX_YEAR ? DatePicker.DEFAULT_MAX_YEAR : builder.maxYear;
datePicker.cancelable = builder.cancelable;
datePicker.loopYear = builder.loopYear;
datePicker.loopMonth = builder.loopMonth;
datePicker.listener = builder.listener;
return datePicker;
}
@Override
public void onStart() {
super.onStart();
DisplayMetrics dm = new DisplayMetrics();
getActivity().getWindowManager().getDefaultDisplay().getMetrics(dm);
getDialog().getWindow().setLayout(dm.widthPixels, getDialog().getWindow().getAttributes().height);
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.layout_datepicker, container);
tv_cancle = view.findViewById(R.id.tv_cancle);
tv_cancle.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
dismiss();
}
});
tv_sure = view.findViewById(R.id.tv_sure);
tv_sure.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
listener.onDatePickCompleted(selectedYear, selectedMonth);
dismiss();
}
});
wheelViewYear = view.findViewById(R.id.wheelViewYear);
wheelViewMonth = view.findViewById(R.id.wheelViewMonth);
//設定DialogFragment所在視窗的背景透明
getDialog().getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
getDialog().requestWindowFeature(Window.FEATURE_NO_TITLE);
initView();
return view;
}
private void initView() {
setCancelable(cancelable);
initYear(minYear, maxYear);
initMonth();
//預設選擇目前年份
selectedYear = calendar.get(Calendar.YEAR);
//position = 目前年份 - minYear
wheelViewYear.setCurrentItem(selectedYear - minYear);
//設定是否循環顯示年份
wheelViewYear.setCyclic(loopYear);
wheelViewYear.setOnItemSelectedListener(new OnItemSelectedListener() {
@Override
public void onItemSelected(int position) {
selectedYear = yearList.get(position);
}
});
//月份是從0到11,實際選擇月份要+1
selectedMonth = calendar.get(Calendar.MONTH) + 1;
//position = 實際選擇月份 - 1
wheelViewMonth.setCurrentItem(selectedMonth - 1);
//設定是否循環顯示月份
wheelViewMonth.setCyclic(loopMonth);
wheelViewMonth.setOnItemSelectedListener(new OnItemSelectedListener() {
@Override
public void onItemSelected(int position) {
selectedMonth = monthList.get(position);
}
});
}
//存儲可供選擇的 年份/月份 集合
private List<Integer> yearList, monthList;
private void initYear(int minYear, int maxYear) {
//避免傳入順序有誤
minYear = Math.min(minYear, maxYear);
maxYear = Math.max(minYear, maxYear);
yearList = new ArrayList<>();
for (int i = 0; i < maxYear + 1 - minYear; i++) {
yearList.add(minYear + i);
}
wheelViewYear.setAdapter(new WheelAdapter() {
@Override
public int getItemsCount() {
return yearList.size();
}
@Override
public Object getItem(int index) {
return yearList.get(index);
}
@Override
public int indexOf(Object o) {
return yearList.indexOf(o);
}
});
}
private void initMonth() {
monthList = new ArrayList<>();
monthList.add(1);
monthList.add(2);
monthList.add(3);
monthList.add(4);
monthList.add(5);
monthList.add(6);
monthList.add(7);
monthList.add(8);
monthList.add(9);
monthList.add(10);
monthList.add(11);
monthList.add(12);
wheelViewMonth.setAdapter(new WheelAdapter() {
@Override
public int getItemsCount() {
return monthList.size();
}
@Override
public Object getItem(int index) {
return monthList.get(index);
}
@Override
public int indexOf(Object o) {
return monthList.indexOf(o);
}
});
}
//顯示DatePicker
public void showPicker(Activity mActivity, String TAG) {
show(mActivity.getFragmentManager(), TAG);
}
public interface OnDatePickedListener {
void onDatePickCompleted(int year, int month);
}
public static class Builder {
private OnDatePickedListener listener;
public Builder(OnDatePickedListener listener) {
this.listener = listener;
}
private int minYear;
private int maxYear;
private boolean loopYear;
private boolean loopMonth;
private boolean cancelable;
public Builder setYearRange(int minYear, int maxYear) {
this.minYear = minYear;
this.maxYear = maxYear;
return this;
}
public Builder setCancelable(boolean cancelable) {
this.cancelable = cancelable;
return this;
}
public Builder loopYear(boolean loop) {
this.loopYear = loop;
return this;
}
public Builder loopMonth(boolean loop) {
this.loopMonth = loop;
return this;
}
public DatePicker build() {
if (minYear > maxYear) {
throw new IllegalArgumentException();//不允許minYear > maxYear
}
return DatePicker.newInstance(this);
}
}
}
layout_datepicker.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">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:background="#ffffff"
android:orientation="vertical">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="40dp">
<TextView
android:id="@+id/tv_sure"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_alignParentRight="true"
android:gravity="center"
android:paddingLeft="20dp"
android:paddingRight="20dp"
android:text="确定"
android:textSize="17sp" />
<TextView
android:id="@+id/tv_cancle"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_alignParentLeft="true"
android:gravity="center"
android:paddingLeft="20dp"
android:paddingRight="20dp"
android:text="取消"
android:textSize="17sp" />
</RelativeLayout>
<View
android:layout_width="match_parent"
android:layout_height="0.5dp"
android:background="#eeeeee" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<com.contrarywind.view.WheelView
android:id="@+id/wheelViewYear"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1" />
<com.contrarywind.view.WheelView
android:id="@+id/wheelViewMonth"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1" />
</LinearLayout>
</LinearLayout>
</RelativeLayout>