常用控件系列(一)NumberPicker

邹斌
2023-12-01

简介

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做一个简单的日期选择器

  1. 首先创建布局
<?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>
  1. 实现日期控件类
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);
    }
}

  1. 使用
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();

常见问题

  1. 关于分割线

API29开始已经可以修改分割线粗细和颜色,但是29以下无法修改,试过通过反射的方法修改粗细和颜色,但是没有成功,获取不到相关属性。

  1. 关于字体颜色

也是在API29开始支持修改字体颜色,但是旧版本可以使用上面介绍的自定义方式达到效果。

  1. 关于minValue、maxValue、value三个方法的调用顺序

最好是按照minValue、maxValue、value的顺序调用,不然可能会数据错乱,尤其不能将value放到中间调用。

  1. 关于赋值时的数据类型

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);
 类似资料: