NumberPicker是android3.0以后引入的一个数字展示控件,不是很常用。这篇文章主要介绍一下它的基本用法,并且用它来做个简单的日期选择器
首先在布局文件中引入控件
<NumberPicker
android:id="@+id/picker_year"
android:layout_width="match_parent" android:layout_height="300dp"
android:visibility="gone"/>
在代码中实例化控件并且设置控件的一些基本属性
picker_year.setMinValue(1970);//设置最小值,接收整数类型
picker_year.setMaxValue(2050);//设置最大值,整数
picker_year.setValue(2020);//设置当前显示的值
picker_year.setWrapSelectorWheel(false);//是否开启循环滚动
picker_year.setDescendantFocusability(DatePicker.FOCUS_BLOCK_DESCENDANTS);//是否允许编辑,FOCUS_BLOCK_DESCENDANTS代表不允许编辑
NunberPicker直到API29的时候才提供了修改字体颜色的方法setTextColor(),那么在API29以下要想修改字体颜色改怎么处理呢?可以通过自定义NunberPicker的方式来实现。
自定义NunberPicker,重写其AddView方法
public class CustomNumberPicker extends NumberPicker {
public CustomNumberPicker(Context context) {
super(context);
}
public CustomNumberPicker(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomNumberPicker(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public CustomNumberPicker(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@Override
public void addView(View child) {
super.addView(child);
updateView(child);
}
@Override
public void addView(View child, int index) {
super.addView(child, index);
updateView(child);
}
@Override
public void addView(View child, int width, int height) {
super.addView(child, width, height);
updateView(child);
}
@Override
public void addView(View child, ViewGroup.LayoutParams params) {
super.addView(child, params);
updateView(child);
}
@Override
public void addView(View child, int index, ViewGroup.LayoutParams params) {
super.addView(child, index, params);
updateView(child);
}
//修改颜色和大小
public void updateView(View view) {
if (view instanceof EditText) {
//这里修改显示字体的属性,主要修改颜色和大小
((EditText) view).setTextColor(Color.parseColor("#5CACEE"));
((EditText) view).setTextSize(20);
}
}
使用PopWindow结合上面刚刚自定义的NumberPicker做一个简单的日期选择器
- 首先创建布局
<?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"
android:background="@android:color/white"
xmlns:app="http://schemas.android.com/apk/res-auto">
<LinearLayout
android:id="@+id/rl_title"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_marginTop="15dp"
android:orientation="horizontal"
android:weightSum="3">
<TextView
android:id="@+id/tv_title_left"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:text="取消"/>
<TextView
android:id="@+id/tv_title_center"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="取消"
android:layout_weight="1"
android:gravity="center" />
<TextView
android:id="@+id/tv_title_right"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="取消"
android:gravity="center"/>
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_below="@+id/rl_title"
android:background="@color/color_text33"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@+id/rl_title"
android:orientation="horizontal"
android:weightSum="3">
<com.henkun.todolistjava.custom.CustomNumberPicker
android:id="@+id/picker_year"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"/>
<com.henkun.todolistjava.custom.CustomNumberPicker
android:id="@+id/picker_month"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"/>
<com.henkun.todolistjava.custom.CustomNumberPicker
android:id="@+id/picker_days"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"/>
</LinearLayout>
</RelativeLayout>
- 实现日期控件类
package com.henkun.todolistjava.custom;
import android.app.Activity;
import android.content.Context;
import android.graphics.drawable.BitmapDrawable;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.DatePicker;
import android.widget.NumberPicker;
import android.widget.PopupWindow;
import android.widget.TextView;
import com.henkun.todolistjava.R;
public class MpickView {
private Context context;
private String title;//标题
private String leftTxt;//左边文字
private String rightTxt;//右边问题
private int startYear;//开始年份
private int endYear;//结束年份
private int currentYear;//当前年份
private int startMonth;//开始月份
private int endMonth;//结束月份
private int currentMonth;//当前月份
private int startDay;//开始日期
private int endDay;//结束日期
private int currentDay;//当前日期
private onKeyPressListener lisenter;
private boolean outsideTouchable;
private PopupWindow timerPop;
private CustomNumberPicker picker_year;
private CustomNumberPicker picker_month;
private CustomNumberPicker picker_day;
// private String[] months = new String[]{"01","02","03","04","05","06","07","08","09","10","11","12"};
// private String[] days = new String[]{"01","02","03","04","05","06","07","08","09","10","11","12","13","14","15",
// "16","17","18","19","20","21","22","23","24","25","26","27","28","29","30","31"};
private MpickView(Context context, Builder builder) {
this.title = builder.title;
this.leftTxt = builder.leftTxt;
this.rightTxt = builder.rightTxt;
this.outsideTouchable = builder.outsideTouchable;
this.startYear = builder.startYear;
this.endYear = builder.endYear;
this.startMonth = builder.startMonth;
this.endMonth = builder.endMonth;
this.startDay = builder.startDay;
this.endDay = builder.endDay;
this.currentYear = builder.currentYear;
this.currentMonth = builder.currentMonth;
this.currentDay = builder.currentDay;
this.context = context;
this.lisenter = builder.lisenter;
}
public static class Builder {
private String title;
private String leftTxt;
private String rightTxt;
private int startYear;
private int endYear;
private int currentYear;
private int startMonth;
private int endMonth;
private int currentMonth;
private int startDay;
private int endDay;
private int currentDay;
private boolean outsideTouchable;
private onKeyPressListener lisenter;
// private LeftClickListener leftClickListener;
//设置标题
public Builder setTitle(String title) {
this.title = title;
return this;
}
//标题左边问题
public Builder setLeftTxt(String leftTxt) {
this.leftTxt = leftTxt;
return this;
}
//标题右边文字
public Builder setRightTxt(String rightTxt) {
this.rightTxt = rightTxt;
return this;
}
//年份最小值
public Builder setStartYear(int startYear) {
this.startYear = startYear;
return this;
}
//年份最大值
public Builder setEndYear(int endYear) {
this.endYear = endYear;
return this;
}
//当前年份
public Builder setCurrentYear(int currentYear) {
this.currentYear = currentYear;
return this;
}
//月份最小值
public Builder setStartMonth(int startMonth) {
this.startMonth = startMonth;
return this;
}
//月份最大值
public Builder setEndMonth(int endMonth) {
this.endMonth = endMonth;
return this;
}
//当前月份
public Builder setCurrentMonth(int currentMonth) {
this.currentMonth = currentMonth;
return this;
}
//日期最小值
public Builder setStartDay(int startDay) {
this.startDay = startDay;
return this;
}
//日期最大值
public Builder setEndDay(int endDay) {
this.endDay = endDay;
return this;
}
//当前日期
public Builder setCurrentDay(int currentDay) {
this.currentDay = currentDay;
return this;
}
//是否允许点击外围空白处消失
public Builder setOutsideTouchable(boolean outsideTouchable) {
this.outsideTouchable = outsideTouchable;
return this;
}
public Builder setOnKeyPressLisenter(onKeyPressListener lisenter) {
this.lisenter = lisenter;
return this;
}
public Builder() {
}
public MpickView build(Context context) {
return new MpickView(context, this);
}
}
/***
* 显示
*/
public void show() {
WindowManager.LayoutParams lp = ((Activity)context).getWindow()
.getAttributes();
lp.alpha = 0.4f;
((Activity)context).getWindow().addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
((Activity)context).getWindow().setAttributes(lp);
View view = LayoutInflater.from(context).inflate(R.layout.layout_popup_pickerview, null);
timerPop = new PopupWindow(view, ViewGroup.LayoutParams.MATCH_PARENT, 700, true);
// timerPop.setBackgroundDrawable(new BitmapDrawable());
TextView left = view.findViewById(R.id.tv_title_left);
left.setText(leftTxt);
left.setOnClickListener(v -> {
dismiss();
});
TextView center = view.findViewById(R.id.tv_title_center);
center.setText(title);
TextView right = view.findViewById(R.id.tv_title_right);
right.setText(rightTxt);
right.setOnClickListener(v -> {
rightPress(this.lisenter);
});
picker_year = view.findViewById(R.id.picker_year);
picker_month = view.findViewById(R.id.picker_month);
picker_day = view.findViewById(R.id.picker_days);
picker_year.setVisibility(View.VISIBLE);
picker_month.setVisibility(View.VISIBLE);
picker_day.setVisibility(View.VISIBLE);
picker_year.setMinValue(startYear);
picker_year.setMaxValue(endYear);
picker_year.setValue(currentYear);
picker_year.setWrapSelectorWheel(false);
picker_year.setDescendantFocusability(DatePicker.FOCUS_BLOCK_DESCENDANTS);
// setNumberPickerDivider(picker_year);
//接收字符串
// picker_month.setDisplayedValues(months);
picker_month.setMinValue(startMonth);
picker_month.setMaxValue(endMonth);
picker_month.setValue(currentMonth);
//不可编辑状态
picker_month.setDescendantFocusability(DatePicker.FOCUS_BLOCK_DESCENDANTS);
// picker_day.setDisplayedValues(days);
picker_day.setMinValue(startDay);
picker_day.setMaxValue(endDay);
picker_day.setValue(currentDay);
picker_day.setDescendantFocusability(DatePicker.FOCUS_BLOCK_DESCENDANTS);
timerPop.setOutsideTouchable(outsideTouchable);
timerPop.setOnDismissListener(() -> {
lp.alpha=1.0f;
((Activity)context).getWindow().setAttributes(lp);
});
timerPop.showAtLocation(view, Gravity.BOTTOM, 0, 0);
}
private void dismiss(){
if (timerPop!=null){
timerPop.dismiss();
}
}
private void rightPress(onKeyPressListener lisenter){
String year = String.valueOf(picker_year.getValue());
String month = String.valueOf(picker_month.getValue());
String day = String.valueOf(picker_day.getValue());
lisenter.pressOK(year,month,day);
dismiss();
}
//接口回调,给宿主控件赋值
public interface onKeyPressListener{
void pressOK(String year,String month,String day);
}
}
- 使用
new MpickView.Builder()
.setTitle("请选择日期")
.setLeftTxt("取消")
.setRightTxt("确定")
.setCurrentYear(1970)
.setEndYear(2050)
.setCurrentYear(2020)
.setStartMonth(1)
.setEndMonth(12).
setCurrentMonth(mMonth)
.setStartDay(1)
.setEndDay(31)
.setCurrentDay(mDay)
.setOnKeyPressLisenter((year,month,day) -> {
edt_task_date.setText(String.format(getResources().getString(R.string.current_time),year,month,day));
})
.build(this)
.show();
- 关于分割线
API29开始已经可以修改分割线粗细和颜色,但是29以下无法修改,试过通过反射的方法修改粗细和颜色,但是没有成功,获取不到相关属性。
- 关于字体颜色
也是在API29开始支持修改字体颜色,但是旧版本可以使用上面介绍的自定义方式达到效果。
- 关于minValue、maxValue、value三个方法的调用顺序
最好是按照minValue、maxValue、value的顺序调用,不然可能会数据错乱,尤其不能将value放到中间调用。
- 关于赋值时的数据类型
NumberPicker在赋值时默认只支持整数类型,不过也不是不能使用字符串。具体的步骤是
1. 定义一个字符串数组,比如月份
private String[] months = new String[]{"01","02","03","04","05","06","07","08","09","10","11","12"};
2. 给控件赋值之前先调用setDisplayedValues,使其支持字符串类型,然后再赋值
picker_month.setDisplayedValues(months);
picker_month.setMinValue(0);
picker_month.setMaxValue(months.length-1);
picker_month.setValue(currentMonth);