文章目录
- 重复造轮子?
- 说一说实现过程
- 集成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>