最近在做Setting日期相关的工作,整理一下相关的API(相关代码源自Android 8.0高通代码)

注意:以下API来自Setting,使用的地方也在Setting中,如果其他非系统应用使用,可能需要加权限

设置日期API

void setDate(int year, int month, int day) {
    Calendar c = Calendar.getInstance();

    Log.d(TAG, "setDate: year " + year + " month " + month + " day " + day);
    c.set(Calendar.YEAR, year);
    c.set(Calendar.MONTH, month);
    c.set(Calendar.DAY_OF_MONTH, day);
    long when = Math.max(c.getTimeInMillis(), DatePreferenceController.DatePreferenceHost.MIN_DATE);

    if (when / 1000 < Integer.MAX_VALUE) {
        ((AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE)).setTime(when);
    }
}

DatePreferenceController.DatePreferenceHost.MIN_DATE:是一个长整型常量,表示日期可以设置的最小值。

DateFormat API

private void set24Hour(boolean is24Hour) {
    Log.d(TAG, "set24Hour: ");
    Settings.System.putString(mContext.getContentResolver(),
            Settings.System.TIME_12_24,
            is24Hour ? HOURS_24 : HOURS_12);
}

指定12小时 24小时制

获取当前DateFormat API

public boolean is24Hour() {
    Log.d(TAG, "is24Hour: ");
    return DateFormat.is24HourFormat(mContext);
}

设置时间API

void setTime(int hourOfDay, int minute) {
    Calendar c = Calendar.getInstance();

    c.set(Calendar.HOUR_OF_DAY, hourOfDay);
    c.set(Calendar.MINUTE, minute);
    c.set(Calendar.SECOND, 0);
    c.set(Calendar.MILLISECOND, 0);
    long when = Math.max(c.getTimeInMillis(), TimePreferenceHost.MIN_DATE);

    if (when / 1000 < Integer.MAX_VALUE) {
        ((AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE)).setTime(when);
    }
}

设置时区API

final AlarmManager alarm = (AlarmManager) activity.getSystemService(Context.ALARM_SERVICE);
alarm.setTimeZone(tzId);

这里tzId是一个字符串,形式为 大洲/City
比如Asia/Shanghai
在Android 8.0高通源码中一共有89个城市,设置的tzId必须在这89个数据中,否则底层会抛出异常,找不到指定的时区,应用crash

自定义DatePicker

由于客户给出的设计图和Android原生的DatePicker相差过大,只能自定义DatePicker了。
注意:这里使用的NumberPicker在framework层的源码做过少量修改。设计图给出的样式比较丑,还没有原生的漂亮。给出的设计大概是这样的
这里写图片描述

Java代码

package com.android.settings.datetime;

import android.app.AlarmManager;
import android.app.AlertDialog;
import android.content.Context;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.annotation.RequiresApi;
import android.util.Log;
import android.view.KeyEvent;
import android.view.ViewGroup.LayoutParams;
import android.view.WindowManager;
import android.widget.NumberPicker;

import com.android.settings.R;

import java.util.Calendar;

/**
 * Created by caihuijian on 18-5-18.
 */

public class CustomDatePickerDialog extends AlertDialog {
    private static final String TAG = "CustomDatePickerDialog";

    private NumberPicker mNumberYear;
    private NumberPicker mNumberMonth;
    private NumberPicker mNumberDay;

    private static final int MAX_YEAR = 37;//暂定 系统某处定义了
    private static final int MIN_YEAR = 7;//暂定 系统某处定义了

    private static final int BIG_MONTH_DAY = 31;//大月31天
    private static final int SMALL_MONTH_DAY = 30;//小月30天
    private static final int LEAP_FEB_DAY = 29;//闰年二月29天
    private static final int NOT_LEAP_FEB_DAY = 28;//平年二月28天
    private static final int MIN_DAY = 1;//最小日

    private static final int MAX_MONTH = 12;//最大月数
    private static final int MIN_MONTH = 1;//最小月数

    private Context mContext = null;
    final Calendar calendar = Calendar.getInstance();

    protected CustomDatePickerDialog(@NonNull Context context) {
        super(context);
        this.mContext = context;
    }

    @RequiresApi(api = Build.VERSION_CODES.HONEYCOMB)
    @Override
    public void show() {
        super.show();

        //全屏dialog
        WindowManager.LayoutParams layoutParams = getWindow().getAttributes();
        layoutParams.width = LayoutParams.MATCH_PARENT;
        layoutParams.height = LayoutParams.MATCH_PARENT;
        getWindow().getDecorView().setPadding(0, 0, 0, 0);
        getWindow().setAttributes(layoutParams);
        getWindow().getDecorView().setBackgroundResource(0x00000000);
        setCustomLayout();
    }

    @RequiresApi(api = Build.VERSION_CODES.HONEYCOMB)
    private void setCustomLayout() {
        setContentView(R.layout.cus_date_picker_dialog);

        //控件初始化
        mNumberMonth = (NumberPicker) findViewById(R.id.month);
        mNumberDay = (NumberPicker) findViewById(R.id.day);
        mNumberYear = (NumberPicker) findViewById(R.id.year);
        setViewMaxMin();
        initAllView();

        mNumberYear.setOnValueChangedListener(yearChangeListener);
        mNumberMonth.setOnValueChangedListener(monthChangeListener);
        mNumberDay.setOnValueChangedListener(dayChangeListener);
    }


    //取得当前时间 设置到view
    @RequiresApi(api = Build.VERSION_CODES.HONEYCOMB)
    private void initAllView() {
        int year, monthOfYear, dayOfMonth;
        if (calendar != null) {
            //因为控件只显示年的后两位 所以对100取余
            year = calendar.get(Calendar.YEAR) % 100;
            monthOfYear = calendar.get(Calendar.MONTH);
            dayOfMonth = calendar.get(Calendar.DAY_OF_MONTH);

            mNumberYear.setValue(year);
            mNumberMonth.setValue(monthOfYear);
            mNumberDay.setValue(dayOfMonth);

        }
    }

    @RequiresApi(api = Build.VERSION_CODES.HONEYCOMB)
    private int getMonthDay(int month) {
        switch (month) {
            case 1:
            case 3:
            case 5:
            case 7:
            case 8:
            case 10:
            case 12:
                return BIG_MONTH_DAY;
            case 4:
            case 6:
            case 9:
            case 11:
                return SMALL_MONTH_DAY;
            case 2:
                int year = mNumberYear.getValue();
                boolean isLeap = isLeapYear(year);
                if (isLeap) {
                    return LEAP_FEB_DAY;
                } else {
                    return NOT_LEAP_FEB_DAY;
                }
        }
        return 0;
    }

    //UI中,年只有2位,可以使用这两位进行闰年判断
    boolean isLeapYear(int year) {
        if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0) {
            Log.d(TAG, "isLeapYear: " + year + "年是闰年");
            return true;
        } else {
            Log.d(TAG, "isLeapYear: " + year + "年不是闰年");
            return false;
        }
    }

    @RequiresApi(api = Build.VERSION_CODES.HONEYCOMB)
    private void setViewMaxMin() {
        mNumberMonth.setMaxValue(MAX_MONTH);
        mNumberMonth.setMinValue(MIN_MONTH);
        mNumberDay.setMaxValue(BIG_MONTH_DAY);
        mNumberDay.setMinValue(MIN_DAY);
        mNumberYear.setMaxValue(MAX_YEAR);
        mNumberYear.setMinValue(MIN_YEAR);
    }


    @RequiresApi(api = Build.VERSION_CODES.HONEYCOMB)
    @Override
    public boolean onKeyUp(int keyCode, KeyEvent event) {

        switch (keyCode) {
            case KeyEvent.KEYCODE_DPAD_CENTER:
                if (mNumberYear.isFocused()) {
                    mNumberMonth.requestFocus();
                } else if (mNumberMonth.isFocused()) {
                    mNumberDay.requestFocus();
                } else if (mNumberDay.isFocused()) {
                    setDate(2000 + mNumberYear.getValue(), mNumberMonth.getValue(), mNumberDay.getValue());
                    this.dismiss();

                }
                return true;
            case KeyEvent.KEYCODE_BACK:
                if (mNumberDay.isFocused()) {
                    mNumberMonth.requestFocus();
                    return true;
                } else if (mNumberMonth.isFocused()) {
                    mNumberYear.requestFocus();
                    return true;
                }
        }
        //手动输入数字 填充年月日
        if (keyCode >= KeyEvent.KEYCODE_0 && keyCode <= KeyEvent.KEYCODE_9) {
            int singleDigit = keyCode - KeyEvent.KEYCODE_0;
            Log.d(TAG, "onKeyUp: keyCode" + keyCode);
            if (mNumberYear.hasFocus()) {
                Log.d(TAG, "onKeyUp: year");
                //手动输入不会触发ValueChangeListener,需要自己处理
                int yearChangeTo = manualInput(mNumberYear, singleDigit);
                yearTextChange(yearChangeTo);
            } else if (mNumberMonth.hasFocus()) {
                Log.d(TAG, "onKeyUp: month");
                //手动输入不会触发ValueChangeListener,需要自己处理
                int monthChange = manualInput(mNumberMonth, singleDigit);
                monthTextChange(monthChange);
            } else if (mNumberDay.hasFocus()) {
                manualInput(mNumberDay, singleDigit);
            }
            return true;
        }

        return super.onKeyUp(keyCode, event);
    }

    @RequiresApi(api = Build.VERSION_CODES.HONEYCOMB)
    private int manualInput(NumberPicker numberPicker, int userInput) {
        //手动输入逻辑:
        //取个位数放到十位上
        //输入值作为个位数
        //十位 个位相加
        //判断该值是否在最大与最小值之间 小于最小值取最小值 大于最大值取最大值 十位取最大值的十位数,个位为用户输入

        int temp = numberPicker.getValue();
        int value = temp % 10 * 10 + userInput;
        int maxValue = numberPicker.getMaxValue();
        int minValue = numberPicker.getMinValue();
        if (value < minValue) {
            value = minValue;
        } else if (value > maxValue) {
            value = maxValue - maxValue % 10 + userInput;
            if(value > maxValue){
                value = maxValue;
            }
        }
        Log.d(TAG, "manualInput: value" + value);
        numberPicker.setValue(value);
        return value;
    }

    //设置日期API
    @RequiresApi(api = Build.VERSION_CODES.FROYO)
    void setDate(int year, int month, int day) {
        Calendar c = Calendar.getInstance();
        Log.d(TAG, "setDate: year " + year + " month " + month + " day " + day);
        c.set(Calendar.YEAR, year);
        c.set(Calendar.MONTH, month);
        c.set(Calendar.DAY_OF_MONTH, day);
        long when = Math.max(c.getTimeInMillis(), DatePreferenceController.DatePreferenceHost.MIN_DATE);

        if (when / 1000 < Integer.MAX_VALUE) {
            ((AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE)).setTime(when);
        }
    }

    NumberPicker.OnValueChangeListener yearChangeListener = new NumberPicker.OnValueChangeListener() {
        @RequiresApi(api = Build.VERSION_CODES.HONEYCOMB)
        @Override
        public void onValueChange(NumberPicker numberPicker, int yearOld, int yearNew) {
            yearTextChange(yearNew);
        }
    };
    NumberPicker.OnValueChangeListener monthChangeListener = new NumberPicker.OnValueChangeListener() {
        @RequiresApi(api = Build.VERSION_CODES.HONEYCOMB)
        @Override
        public void onValueChange(NumberPicker numberPicker, int monthOld, int monthNew) {
            monthTextChange(monthNew);
        }
    };
    NumberPicker.OnValueChangeListener dayChangeListener = new NumberPicker.OnValueChangeListener() {
        @Override
        public void onValueChange(NumberPicker numberPicker, int dayOld, int dayNew) {
        }
    };

    //年的控件变化需要做的处理
    @RequiresApi(api = Build.VERSION_CODES.HONEYCOMB)
    public void yearTextChange(int yearNew) {
        //年的变化会导致2月是否有29日变化
        boolean isLeapYear = isLeapYear(yearNew);
        int month = mNumberMonth.getValue();
        if (isLeapYear && month == 2) {
            //闰年二月
            mNumberDay.setMaxValue(29);
        } else if (!isLeapYear && month == 2) {
            //非闰年二月
            mNumberDay.setMaxValue(28);
        }
    }

    //月的控件变化需要做的处理
    @RequiresApi(api = Build.VERSION_CODES.HONEYCOMB)
    private void monthTextChange(int monthNew) {
        int monthDay = getMonthDay(monthNew);
        mNumberDay.setMaxValue(monthDay);
    }

}

xml

<?xml version="1.0" encoding="utf-8"?><!-- Layout of time picker-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#1E1E1E"
    android:gravity="center"
    android:orientation="horizontal">

    <!-- year -->
    <NumberPicker
        android:id="@+id/year"
        android:layout_width="80dip"
        android:layout_height="wrap_content"
        android:custtomSytle="true"
        android:focusable="true"
        android:focusableInTouchMode="true" />

    <!-- divider1 -->
    <TextView
        android:id="@+id/divider1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical"
        android:layout_marginEnd="2dip"
        android:layout_marginStart="2dip"
        android:importantForAccessibility="no"
        android:text="/"
        android:textColor="#808080"
        android:textSize="55dp" />

    <!-- month -->
    <NumberPicker
        android:id="@+id/month"
        android:layout_width="80dip"
        android:layout_height="wrap_content"
        android:custtomSytle="true"
        android:focusable="true"
        android:focusableInTouchMode="true" />
    <!-- divider2 -->
    <TextView
        android:id="@+id/divider2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical"
        android:layout_marginEnd="2dip"
        android:layout_marginStart="2dip"
        android:importantForAccessibility="no"
        android:text="/"
        android:textColor="#808080"
        android:textSize="55dp" />

    <!-- day -->
    <NumberPicker
        android:id="@+id/day"
        android:layout_width="80dip"
        android:layout_height="wrap_content"
        android:custtomSytle="true"
        android:focusable="true"
        android:focusableInTouchMode="true" />
</LinearLayout>

使用

                CustomDatePickerDialog datePickerDialog = new CustomDatePickerDialog(getActivity());
                datePickerDialog.show();
Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐