Android Setting 日期相关API,自定义DatePicker

2024-05-30 12:39

本文主要是介绍Android Setting 日期相关API,自定义DatePicker,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

最近在做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)@Overridepublic void show() {super.show();//全屏dialogWindowManager.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)@Overridepublic 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)@Overridepublic void onValueChange(NumberPicker numberPicker, int yearOld, int yearNew) {yearTextChange(yearNew);}};NumberPicker.OnValueChangeListener monthChangeListener = new NumberPicker.OnValueChangeListener() {@RequiresApi(api = Build.VERSION_CODES.HONEYCOMB)@Overridepublic void onValueChange(NumberPicker numberPicker, int monthOld, int monthNew) {monthTextChange(monthNew);}};NumberPicker.OnValueChangeListener dayChangeListener = new NumberPicker.OnValueChangeListener() {@Overridepublic 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();

这篇关于Android Setting 日期相关API,自定义DatePicker的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1016229

相关文章

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

sqlite3 相关知识

WAL 模式 VS 回滚模式 特性WAL 模式回滚模式(Rollback Journal)定义使用写前日志来记录变更。使用回滚日志来记录事务的所有修改。特点更高的并发性和性能;支持多读者和单写者。支持安全的事务回滚,但并发性较低。性能写入性能更好,尤其是读多写少的场景。写操作会造成较大的性能开销,尤其是在事务开始时。写入流程数据首先写入 WAL 文件,然后才从 WAL 刷新到主数据库。数据在开始

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

Android平台播放RTSP流的几种方案探究(VLC VS ExoPlayer VS SmartPlayer)

技术背景 好多开发者需要遴选Android平台RTSP直播播放器的时候,不知道如何选的好,本文针对常用的方案,做个大概的说明: 1. 使用VLC for Android VLC Media Player(VLC多媒体播放器),最初命名为VideoLAN客户端,是VideoLAN品牌产品,是VideoLAN计划的多媒体播放器。它支持众多音频与视频解码器及文件格式,并支持DVD影音光盘,VCD影

自定义类型:结构体(续)

目录 一. 结构体的内存对齐 1.1 为什么存在内存对齐? 1.2 修改默认对齐数 二. 结构体传参 三. 结构体实现位段 一. 结构体的内存对齐 在前面的文章里我们已经讲过一部分的内存对齐的知识,并举出了两个例子,我们再举出两个例子继续说明: struct S3{double a;int b;char c;};int mian(){printf("%zd\n",s

android-opencv-jni

//------------------start opencv--------------------@Override public void onResume(){ super.onResume(); //通过OpenCV引擎服务加载并初始化OpenCV类库,所谓OpenCV引擎服务即是 //OpenCV_2.4.3.2_Manager_2.4_*.apk程序包,存

从状态管理到性能优化:全面解析 Android Compose

文章目录 引言一、Android Compose基本概念1.1 什么是Android Compose?1.2 Compose的优势1.3 如何在项目中使用Compose 二、Compose中的状态管理2.1 状态管理的重要性2.2 Compose中的状态和数据流2.3 使用State和MutableState处理状态2.4 通过ViewModel进行状态管理 三、Compose中的列表和滚动

Spring 源码解读:自定义实现Bean定义的注册与解析

引言 在Spring框架中,Bean的注册与解析是整个依赖注入流程的核心步骤。通过Bean定义,Spring容器知道如何创建、配置和管理每个Bean实例。本篇文章将通过实现一个简化版的Bean定义注册与解析机制,帮助你理解Spring框架背后的设计逻辑。我们还将对比Spring中的BeanDefinition和BeanDefinitionRegistry,以全面掌握Bean注册和解析的核心原理。

两个月冲刺软考——访问位与修改位的题型(淘汰哪一页);内聚的类型;关于码制的知识点;地址映射的相关内容

1.访问位与修改位的题型(淘汰哪一页) 访问位:为1时表示在内存期间被访问过,为0时表示未被访问;修改位:为1时表示该页面自从被装入内存后被修改过,为0时表示未修改过。 置换页面时,最先置换访问位和修改位为00的,其次是01(没被访问但被修改过)的,之后是10(被访问了但没被修改过),最后是11。 2.内聚的类型 功能内聚:完成一个单一功能,各个部分协同工作,缺一不可。 顺序内聚:

log4j2相关配置说明以及${sys:catalina.home}应用

${sys:catalina.home} 等价于 System.getProperty("catalina.home") 就是Tomcat的根目录:  C:\apache-tomcat-7.0.77 <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} [%t] %-5p %c{1}:%L - %msg%n" /> 2017-08-10