Android NumberPicker使用

Android NumberPicker使用

NumberPicker是一个滑动的控件,和spinner差不多。

数字选择器NumberPicker是Android3.0之后出的一个控件,所以如果要兼容3.0之前的版本就需要用到GitHub上的开源项目,下载地址是https://github.com/SimonVT/android-numberpicker

使用

1.首先在xml文件中引入NumberPicker控件

复制代码
<NumberPicker
    android:id="@+id/np"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
   app:layout_constraintTop_toBottomOf="@+id/number_picker_use" />

2.在activity中找到NumberPicker,设置方法

复制代码
NumberPicker numberPicker = findViewById(R.id.np);
String[] datas = new String[]{"北京", "上海", "广州", "深圳"};
numberPicker.setDisplayedValues(datas); //设置文字
numberPicker.setMaxValue(datas.length - 1); //设置最大值,最大值是datas[3]
numberPicker.setMinValue(0);
numberPicker.setDescendantFocusability(DatePicker.FOCUS_BEFORE_DESCENDANTS);
numberPicker.setValue(1);

常用方法

设置最大值
复制代码
mNumberPicker.setMaxValue(10); //设置最大值,只能传入int
设置最小值
复制代码
 mNumberPicker.setMinValue(0); //设置最小值
设置当前值
复制代码
mNumberPicker.setValue(5); //设置当前值
获取当前值
复制代码
int value = mNumberPicker.getValue(); //获取当前值
getMaxValue()获取最大值
getMinValue()获取最小值

有一点需要注意的数值不能用负数。

获取设置的数据
复制代码
String[] data = numberPicker.getDisplayedValues();//返回一组数据
获取/设置文本颜色、文本大小、固定颜色
复制代码
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            numberPicker.setTextColor(Color.RED);//设置所有的文本都是红色
            numberPicker.setTextSize(20);//文字大小,这里最好使用px转dp
            numberPicker.setSelectionDividerHeight(40);//两道分割线的高度
        }
        
getTextColor        
getTextSize
getSelectionDividerHeight
设置监听

NumberPicker有三个监听分别是OnValueChangeListener、OnScrollListener、Formatter

OnValueChangeListener值改变监听
复制代码
mNumberPicker.setOnValueChangedListener(new NumberPicker.OnValueChangeListener() {
            /**
             * 每当选择的值改变时都会调用一次
             * @param oldVal 改变前的值
             * @param newVal 改变后的值
             */
            @Override
            public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
                //做想做的事
            }
        });

每当选择的值改变都会调用一次这个方法,选择的值改变也就是显示在中间的那个值改变。

OnScrollListener滑动事件

实例代码如下

复制代码
mNumberPicker.setOnScrollListener(new NumberPicker.OnScrollListener() {
            @Override
            public void onScrollStateChange(NumberPicker view, int scrollState) {
                switch (scrollState){
                    case SCROLL_STATE_FLING:
                        //手离开之后还在滑动
                        break;
                    case SCROLL_STATE_IDLE:
                        //停止滑动
                        break;
                    case SCROLL_STATE_TOUCH_SCROLL:
                        //正在滑动
                        break;
                }
            }
        });

滑动事件就是监听控件滑动时的状态

滑动事件有三个状态

  1. SCROLL_STATE_FLING ------>手离开之后还在滑动
  2. SCROLL_STATE_IDLE ------>停止滑动
  3. SCROLL_STATE_TOUCH_SCROLL ------>正在滑动
Formatter监听设置格式化程序用于格式化当前值

实例代码如下

复制代码
mNumberPicker.setFormatter(new NumberPicker.Formatter() {
            @Override
            public String format(int value) {
                //做一些格式转换
                return "返回转换后要显示的内容";
            }
        });

这个监听是用于做一些格式转换的,比如把10以下的数前面加个0显示(01、02、03......),也能理解为转换器,下面我会为大家介绍如何利用Formatter监听设置10以下的数前面加0显示。
我们需要注意一下不能返回空

复制代码
mNumberPicker.setMaxValue(20);
mNumberPicker.setMinValue(0);
mNumberPicker.setValue(0);
mNumberPicker.setFormatter(new NumberPicker.Formatter() {
        @Override
        public String format(int value) {
            String data;
            if (value < 10) {
                data = "0" + value;//让小于10的数前面加个0再输出
            } else {
                data = String.valueOf(value); //大于10的数就不变
            }
            return data;
        }
    });
循环滚动

要设置是否循环滚动只需要使用一个方法就可以了

复制代码
mNumberPicker.setWrapSelectorWheel(true); //设置循环滚动

设置false代表不循环滚动,true代表循环滚动

该方法需要注意的是

  • 一定要设置好最大值才有效果
  • 最大值必须大于等于3
禁止编辑

java代码

复制代码
mNumberPicker.setDescendantFocusability(NumberPicker.FOCUS_BLOCK_DESCENDANTS); //禁止输入

xml设置

复制代码
android:descendantFocusability="blocksDescendants"
28的版本以前修改分界线
复制代码
/**
 * 设置picker分割线的颜色
 */
private void setDividerColor(NumberPicker picker) {
    Field field = null;
    try {
        field = NumberPicker.class.getDeclaredField("mSelectionDivider");
        if (field != null) {
            field.setAccessible(true);
            field.set(picker, new ColorDrawable(Color.RED));
        }
    } catch (NoSuchFieldException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    }

}

/**
 * 设置picker分割线的宽度(分割线的粗细)
 */
private void setNumberPickerDivider(NumberPicker picker) {
    Field[] fields = NumberPicker.class.getDeclaredFields();
    for (Field f : fields) {
        if (f.getName().equals("mSelectionDividerHeight")) {
            f.setAccessible(true);
            try {
                f.set(picker, 1);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
            break;
        }
    }
}
案例:日期选择器
xml布局
复制代码
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <TextView
        android:gravity="center"
        android:padding="10dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="请选择时间"
        android:textSize="18sp"
        android:textStyle="bold"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="@+id/button_confirm"/>

    <Button
        android:layout_margin="10dp"
        android:id="@+id/button_confirm"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="确认"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <LinearLayout
        android:id="@+id/item_linear_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:layout_marginEnd="16dp"
        android:gravity="fill_horizontal"
        app:layout_constraintTop_toBottomOf="@+id/button_confirm"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent">

        <NumberPicker
            android:id="@+id/number_picker_year"
            android:layout_width="0dp"
            android:layout_height="200dp"
            android:layout_weight="0.2"
            android:descendantFocusability="blocksDescendants" />

        <TextView
            android:id="@+id/text_view_year"
            android:layout_width="0dp"
            android:layout_height="200dp"
            android:layout_weight="0.05"
            android:gravity="center"
            android:text="@string/text_year" />

        <NumberPicker
            android:id="@+id/number_picker_month"
            android:layout_width="0dp"
            android:layout_height="200dp"
            android:layout_weight="0.15"
            android:descendantFocusability="blocksDescendants" />

        <TextView
            android:id="@+id/text_view_month"
            android:layout_width="0dp"
            android:layout_height="200dp"
            android:layout_weight="0.05"
            android:gravity="center"
            android:text="@string/text_month" />

        <NumberPicker
            android:id="@+id/number_picker_date"
            android:layout_width="0dp"
            android:layout_height="200dp"
            android:layout_weight="0.15"
            android:descendantFocusability="blocksDescendants" />

        <TextView
            android:id="@+id/text_view_date"
            android:layout_width="0dp"
            android:layout_height="200dp"
            android:layout_weight="0.05"
            android:gravity="center"
            android:text="@string/text_date" />

        <NumberPicker
            android:id="@+id/number_picker_hour"
            android:layout_width="0dp"
            android:layout_height="200dp"
            android:layout_weight="0.15"
            android:descendantFocusability="blocksDescendants" />

        <TextView
            android:id="@+id/text_view_hour"
            android:layout_width="0dp"
            android:layout_height="200dp"
            android:layout_weight="0.05"
            android:gravity="center"
            android:text="@string/text_time_symbol" />

        <NumberPicker
            android:id="@+id/number_picker_minute"
            android:layout_width="0dp"
            android:layout_height="200dp"
            android:layout_weight="0.15"
            android:descendantFocusability="blocksDescendants" />

    </LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
dialogfragment
复制代码
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.NumberPicker;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.DialogFragment;

import com.zg.pdfdemo.R;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Locale;

/***
 * @Description:
 * //[Android构建自定义日期时间选择器](https://www.jianshu.com/p/05dade729e28)
 */
public class TimePickerDialog extends DialogFragment implements NumberPicker.OnValueChangeListener {

    private NumberPicker yearPicker;
    private NumberPicker monthPicker;
    private NumberPicker datePicker;
    private NumberPicker hourPicker;
    private NumberPicker minutePicker;

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.dialog_fragment_date_time_picker, container, false);
        return view;
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        Calendar calendar = Calendar.getInstance();
        yearPicker = view.findViewById(R.id.number_picker_year);
        monthPicker = view.findViewById(R.id.number_picker_month);
        datePicker = view.findViewById(R.id.number_picker_date);
        hourPicker = view.findViewById(R.id.number_picker_hour);
        minutePicker = view.findViewById(R.id.number_picker_minute);

        //限制年份范围为前后五年
        int yearNow = calendar.get(Calendar.YEAR);
        yearPicker.setMinValue(yearNow - 5);
        yearPicker.setMaxValue(yearNow + 5);
        yearPicker.setValue(yearNow);
        yearPicker.setWrapSelectorWheel(true);  //关闭选择器循环

        //设置月份范围为1~12
        monthPicker.setMinValue(1);
        monthPicker.setMaxValue(12);
        monthPicker.setValue(calendar.get(Calendar.MONTH) + 1);
        monthPicker.setWrapSelectorWheel(true);

        //日期限制存在变化,需要根据当月最大天数来调整
        datePicker.setMinValue(1);
        datePicker.setMaxValue(calendar.getActualMaximum(Calendar.DAY_OF_MONTH));
        datePicker.setValue(calendar.get(Calendar.DATE));
        datePicker.setWrapSelectorWheel(true);

        //24小时制,限制小时数为0~23
        hourPicker.setMinValue(0);
        hourPicker.setMaxValue(23);
        hourPicker.setValue(calendar.get(Calendar.HOUR_OF_DAY));
        hourPicker.setWrapSelectorWheel(true);

        //限制分钟数为0~59
        minutePicker.setMinValue(0);
        minutePicker.setMaxValue(59);
        minutePicker.setValue(calendar.get(Calendar.MINUTE));
        minutePicker.setWrapSelectorWheel(true);

        //为年份和月份设置监听
        yearPicker.setOnValueChangedListener(this);
        monthPicker.setOnValueChangedListener(this);

        view.findViewById(R.id.button_confirm).setOnClickListener(v -> {
            //获取的日期时间结果
            String result = String.format(Locale.CHINA, "%d-%d-%d %d:%d",
                    yearPicker.getValue(), monthPicker.getValue(), datePicker.getValue(),
                    hourPicker.getValue(), minutePicker.getValue());
            Log.i("zxd", "日期时间: " + result);
            dismiss();
        });
    }

    @Override
    public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
        String dateStr = String.format(Locale.CHINA, "%d-%d", yearPicker.getValue(), monthPicker.getValue());
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM", Locale.CHINA);
        Calendar calendar = Calendar.getInstance();
        try {
            calendar.setTime(simpleDateFormat.parse(dateStr));
        } catch (ParseException e) {
            e.printStackTrace();
        }
        int dateValue = datePicker.getValue();
        int maxValue = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
        datePicker.setMaxValue(maxValue);
        //重设日期值,防止月份变动时超过最大值
        datePicker.setValue(Math.min(dateValue, maxValue));
    }
}

当选择器日期发生变化时,我们需要判断所显示的日期月份最多有多少天,防止出现不存在日期的情况,这时就需要为选择器值变动监听,因为年份和月份的变动都会影响当月最大天数,因此可以通过实现NumberPicker.OnValueChangeListener,重写onValueChange()方法来实现:

复制代码
@Override
    public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
        String dateStr = String.format(Locale.CHINA, "%d-%d", yearPicker.getValue(), monthPicker.getValue());
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM", Locale.CHINA);
        Calendar calendar = Calendar.getInstance();
        try {
            calendar.setTime(simpleDateFormat.parse(dateStr));
        } catch (ParseException e) {
            e.printStackTrace();
        }
        int dateValue = datePicker.getValue();
        int maxValue = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
        datePicker.setMaxValue(maxValue);
        //重设日期值,防止月份变动时超过最大值
        datePicker.setValue(Math.min(dateValue, maxValue));
    }

这样一个自定义的可以同时选择日期和时间的选择器就基本完工了,以后还能自定义各个控件的样式来实现想要的效果。当然,获取选择器的结果可以通过定义一个Button来监听点击事件来获取

java 复制代码
findViewById(R.id.button_confirm).setOnClickListener(view -> {
    //获取的日期时间结果
    String result = String.format(Locale.CHINA, "%d-%d-%d %d:%d",
            yearPicker.getValue(), monthPicker.getValue(), datePicker.getValue(),
            hourPicker.getValue(), minutePicker.getValue());
}

参考链接

Android构建自定义日期时间选择器

数字选择器NumberPicker使用教程

安卓NumberPicker数字选择器用法

第三方的NumberPicker

Android进阶之路------自定义NumberPicker

NumberPicker源码分析+自定义View简单实现NumberPicker

相关推荐
Kapaseker22 分钟前
实战 Compose 中的 IntrinsicSize
android·kotlin
xq95271 小时前
Andorid Google 登录接入文档
android
黄林晴3 小时前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab15 小时前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿18 小时前
Android MediaPlayer 笔记
android
Jony_18 小时前
Android 启动优化方案
android
阿巴斯甜18 小时前
Android studio 报错:Cause: error=86, Bad CPU type in executable
android
张小潇19 小时前
AOSP15 Input专题InputReader源码分析
android
_小马快跑_1 天前
Kotlin | 协程调度器选择:何时用CoroutineScope配置,何时用launch指定?
android
_小马快跑_1 天前
Kotlin | 从SparseArray、ArrayMap的set操作符看类型检查的不同
android