Android自定义控件之三级联动时间框

230 阅读2分钟

前言:目前随着Android系统得完善,对UI界面要求也相应增高,对于Android自带得时间选择框界面过丑,自定义封装一个与IOS一致的三级联动时间框。

首先看看效果:

微信图片_20220719150448.png

首先介绍一下控件的主要构成

1、PickerView,滚动的View

2、MyDateTimePickDialog 自己定义的时间框dialog,用于处理逻辑

3、xml与资源文件布局

下面看下代码

xml布局的使用

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="30dp"
        android:background="#f2f2f2">

        <TextView
            android:id="@+id/cancel_my_date_time_pick_view"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:gravity="center"
            android:paddingLeft="10dp"
            android:paddingRight="10dp"
            android:text="取消"
            android:textColor="#f74948"/>

        <TextView
            android:id="@+id/ok_my_date_time_pick_view"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_alignParentRight="true"
            android:gravity="center"
            android:paddingLeft="10dp"
            android:paddingRight="10dp"
            android:text="确定"
            android:textColor="#f74948" />
    </RelativeLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="150dp"
        android:orientation="horizontal"
        android:background="#fff">

        <com.example.timeapplication.timeview.PickerView
            android:id="@+id/myPickerView_my_date_time_pick_view"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1" />

        <com.example.timeapplication.timeview.PickerView
            android:id="@+id/myPickerView_my_date_time_pick_view1"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1" />

        <com.example.timeapplication.timeview.PickerView
            android:id="@+id/myPickerView_my_date_time_pick_view2"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1" />

        <com.example.timeapplication.timeview.PickerView
            android:id="@+id/myPickerView_my_date_time_pick_view3"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1" />

        <com.example.timeapplication.timeview.PickerView
            android:id="@+id/myPickerView_my_date_time_pick_view4"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1" />

        <com.example.timeapplication.timeview.PickerView
            android:id="@+id/myPickerView_my_date_time_pick_view5"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1" />

    </LinearLayout>

    <View
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:background="#000" />
</LinearLayout>

此处放六个PickerView,可以自己方便滑动,分别是年月日,时分秒控件

时间选择.png

MyDateTimePickDialog的介绍

1、构造方法

在构造方法里实现年月日的初始化,控件显示的位置与动画的初始化,和按钮的监听事件

public MyDateTimePickDialog(Context context) {
    super(context);
    mContext = context;

    View view = LayoutInflater.from(mContext).inflate(R.layout.layout_my_date_time_pick_view, null);
    setContentView(view);
    Window window = this.getWindow();
    window.setGravity(Gravity.BOTTOM);
    window.setBackgroundDrawableResource(R.drawable.shape_my_bottom_dialog);
    window.setWindowAnimations(R.style.dialog_anim);
    WindowManager.LayoutParams params = window.getAttributes();
    params.width = WindowManager.LayoutParams.MATCH_PARENT;
    params.height = WindowManager.LayoutParams.WRAP_CONTENT;
    window.setAttributes(params);

    TextView tvCancel = view.findViewById(R.id.cancel_my_date_time_pick_view);
    TextView tvOk = view.findViewById(R.id.ok_my_date_time_pick_view);
    mPickViewYear = view.findViewById(R.id.myPickerView_my_date_time_pick_view);
    mPickViewMonth = view.findViewById(R.id.myPickerView_my_date_time_pick_view1);
    mPickViewDay = view.findViewById(R.id.myPickerView_my_date_time_pick_view2);
    mPickViewHour = view.findViewById(R.id.myPickerView_my_date_time_pick_view3);
    mPickViewMinute = view.findViewById(R.id.myPickerView_my_date_time_pick_view4);
    mPickViewSecond = view.findViewById(R.id.myPickerView_my_date_time_pick_view5);
    mCalendar = Calendar.getInstance();
    mYear = mCalendar.get(Calendar.YEAR);
    mMonth = mCalendar.get(Calendar.MONTH) + 1;   // 获取的 月 从 0 开始
    mDay = mCalendar.get(Calendar.DAY_OF_MONTH);
    mHour = mCalendar.get(Calendar.HOUR_OF_DAY);
    mMinute = mCalendar.get(Calendar.MINUTE);
    mSecond = mCalendar.get(Calendar.SECOND);

    tvCancel.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            if (mCancelListener != null)
                mCancelListener.cancel();
        }
    });
    tvOk.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            mYearStr = mYear + "";
            mMonthStr = formatTime(mMonth);
            mDayStr = formatTime(mDay);
            mHourStr = formatTime(mHour);
            mMinuteStr = formatTime(mMinute);
            mSecondStr = formatTime(mSecond);

            if (mOkListener != null)
                mOkListener.ok(mYearStr, mMonthStr, mDayStr, mHourStr, mMinuteStr, mSecondStr);
        }
    });

}

2、对年月日时分秒逻辑封装

private void initPickView() {
    // ============= 年 数据 =============
    if (mType != 2) {
        List<String> listYear = new ArrayList<>();
        int index = 0;
        for (int i = 0; i < 20; i++) {
            String year = (mCalendar.get(Calendar.YEAR) - 10 + i) + "年";
            listYear.add(year);
            if (year.equals(mYear + "年"))
                index = i;

        }
        mPickViewYear.setData(listYear, false);
        mPickViewYear.setSelected(index);
        mPickViewYear.setOnSelectListener(new PickerView.onSelectListener() {
            @Override
            public void onSelect(String text) {
                String yearStr = text.replace("年", "");
                if (yearStr.matches("[0-9]+"))
                    mYear = Integer.parseInt(yearStr);
                setDayData();
            }
        });
    }

    // =============== 设置 日 数据 =================
    if (mType != 2)
        setDayData();

    // ==================== 设置 月 数据 ======================
    if (mType != 2) {
        List<String> listMonth = new ArrayList<>();
        for (int j = 1; j <= 12; j++) {
            if (j < 10)
                listMonth.add("0" + j + "月");
            else
                listMonth.add(j + "月");
        }
        mPickViewMonth.setData(listMonth);
        mPickViewMonth.setSelected(mMonth - 1);
        mPickViewMonth.setOnSelectListener(new PickerView.onSelectListener() {
            @Override
            public void onSelect(String text) {
                String monthStr = text.replace("月", "");
                if (monthStr.matches("[0-9]+"))
                    mMonth = Integer.parseInt(monthStr);
                setDayData();
            }
        });
    }

    // ================= 设置 时 数据 ====================
    if (mType != 1) {
        List<String> listHour = new ArrayList<>();
        for (int i = 0; i <= 23; i++) {
            if (i < 10) {
                listHour.add("0" + i + "时");
            } else
                listHour.add(i + "时");
        }
        mPickViewHour.setData(listHour);
        mPickViewHour.setSelected(mHour);
        mPickViewHour.setOnSelectListener(new PickerView.onSelectListener() {
            @Override
            public void onSelect(String text) {
                String hourStr = text.replace("时", "");
                if (hourStr.matches("[0-9]+"))
                    mHour = Integer.parseInt(hourStr);
            }
        });
    }


    // ================  设置 分 数据 =================
    if (mType != 1) {
        List<String> listMinute = new ArrayList<>();
        for (int i = 0; i <= 59; i++) {
            if (i < 10)
                listMinute.add("0" + i + "分");
            else
                listMinute.add(i + "分");
        }
        mPickViewMinute.setData(listMinute);
        mPickViewMinute.setSelected(mMinute);
        mPickViewMinute.setOnSelectListener(new PickerView.onSelectListener() {
            @Override
            public void onSelect(String text) {
                String minuteStr = text.replace("分", "");
                if (minuteStr.matches("[0-9]+"))
                    mMinute = Integer.parseInt(minuteStr);
            }
        });
    }

    // ================ 设置 秒 数据 ==================
    if (mType != 1 && mType != 2) {
        List<String> listSecond = new ArrayList<>();
        for (int i = 0; i <= 59; i++) {
            if (i < 10)
                listSecond.add("0" + i + "秒");
            else
                listSecond.add(i + "秒");
        }
        mPickViewSecond.setData(listSecond);
        mPickViewSecond.setSelected(mSecond);

        mPickViewSecond.setOnSelectListener(new PickerView.onSelectListener() {
            @Override
            public void onSelect(String text) {
                String secondStr = text.replace("秒", "");
                if (secondStr.matches("[0-9]+"))
                    mSecond = Integer.parseInt(secondStr);
            }
        });
    }

}

// 设置 日 数据
private void setDayData() {
    List<String> listDay = new ArrayList<>();
    int dayNum = 31;
    if (mMonth == 4 || mMonth == 6 || mMonth == 9 || mMonth == 11) {
        dayNum = 30;
    }
    if (mMonth == 2) {
        if (mYear % 400 == 0 || mYear % 4 == 0) {  // 闰年
            dayNum = 29;
        } else {
            dayNum = 28;
        }
    }

    for (int i = 1; i <= dayNum; i++) {
        if (i < 10)
            listDay.add("0" + i + "日");
        else
            listDay.add(i + "日");
    }
    mPickViewDay.setData(listDay);
    mPickViewDay.setSelected(mDay - 1);
    mPickViewDay.setOnSelectListener(new PickerView.onSelectListener() {
        @Override
        public void onSelect(String text) {
            String dayStr = text.replace("日", "");
            if (dayStr.matches("[0-9]+"))
                mDay = Integer.parseInt(dayStr);
        }
    });

}

3、只显示年月日,或者时分秒

public void setType(int type){
    mType = type;

    if (mType == 1) {  // 只筛选 年月日,把 时分秒 控件 隐掉
        mPickViewHour.setVisibility(View.GONE);
        mPickViewMinute.setVisibility(View.GONE);
        mPickViewSecond.setVisibility(View.GONE);
    }
    if (mType == 2) {  // 只筛选 时分,把 年月日 秒 控件隐掉
        mPickViewYear.setVisibility(View.GONE);
        mPickViewMonth.setVisibility(View.GONE);
        mPickViewDay.setVisibility(View.GONE);
        mPickViewSecond.setVisibility(View.GONE);
    }


}

控件的使用

使用上非常简单,直接按封装好的使用即可,看代码

public void setStartEndTime() {
            if (mPickDialog == null)
                mPickDialog = new MyDateTimePickDialog(MainActivity.this);
            // 此处设置一个默认值
            int[] startDateAndTime = TimeUtils.getDateAndTime("2022-7-19 13:23:44");
            if (startDateAndTime.length == 6) {
                mPickDialog.setDateAndTime(startDateAndTime[0], startDateAndTime[1], startDateAndTime[2], startDateAndTime[3], startDateAndTime[4], startDateAndTime[5]);
            }
            mPickDialog.showThisDialog(new MyDateTimePickDialog.OnPickViewOkListener() {
                @Override
                public void ok(String year, String month, String date, String hour, String minute, String second) {
                    Log.i("xjz111",year + "-" + month + "-" + date + " " + hour + ":" + minute + ":" + second);
                    mPickDialog.dismiss();
                }
            }, new MyDateTimePickDialog.OnPickViewCancelListener() {
                @Override
                public void cancel() {
                    mPickDialog.dismiss();
                }
            });
}

其中,ok的回调就是选择了之后,得到的时间数据,再进行逻辑封装处理即可。

git代码地址:

github.com/fatfatwolf/…