一、综述
已经有很多成熟的产品实现了夜间模式 ,总体来讲,此功能已经比较成熟,但是具体到单个产品,是否一定要上夜间模式还是需要慎重考虑的,因为对成熟产品的改造需要很大的工作量,并且夜间模式作为一种规范,会在后续的开发过程中不断影响工作量投入(每实现一个新的界面,都需要考虑对夜间模式的支持)。如果是一个对界面样式没有强烈要求的应用,不妨考虑通过降低屏幕亮度或者给整体界面增加半透明蒙层来支持夜间阅读效果。
1.1 降低屏幕亮度
操作屏幕亮度需要权限:
<uses-permission android:name="android.permission.WRITE_SETTINGS"/>
有两种降低屏幕亮度的方案:
1)降低单个Activity亮度
主要代码如下:
public static void setBrightness(Activity activity, float brightness) {
Window window = activity.getWindow();
WindowManager.LayoutParams layoutParams = window.getAttributes();
brightness = Math.max( 0, Math.min(brightness, 1));
layoutParams.screenBrightness = brightness;
window.setAttributes(layoutParams);
}
2)降低手机系统全局亮度
主要思路:
切换模式时,获取手机当前的亮度值并保存(用于退出应用时还原到此亮度) ---> 如果手机打开自动亮度调节则关闭自动调节, 再设置合适的较低亮度(夜间模式) --->退出应用时还原保存的初始亮度,并重设自动亮度调节模式。
主要代码如下:
获取当前亮度:
/**
* 获取当前系统亮度 ,失败返回defaultValue,获取成功返回正常非负数 (0~255)
*/
public static int getSystemBrightness(Context context, int defaultValue) {
try {
return Settings.System. getInt(
context.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS );
} catch (Exception e) {
e.printStackTrace();
}
return defaultValue;
}
获取当前的亮度调节模式:
/**
* 自动调节亮度的配置
* 返回Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC或SCREEN_BRIGHTNESS_MODE_MANUAL
*/
public static int getScreenBrightnessMode(Context context) {
try {
return Settings.System. getInt(context.getContentResolver(),
Settings.System.SCREEN_BRIGHTNESS_MODE );
} catch (Exception e) {
e.printStackTrace();
}
//0 手动 1自动,可以用 -1代表未知
return -1 ;
}
设置亮度:
/**亮度介于0~255之间 */
public static void saveBrightness(
ContentResolver contentResolver, int brightnessValue) {
String uriName = Settings.System.SCREEN_BRIGHTNESS ;
Uri uri = android.provider.Settings.System.getUriFor(uriName);
android.provider.Settings.System.putInt(
contentResolver, uriName, brightnessValue);
contentResolver.notifyChange(uri, null);
}
设置亮度调节模式:
/**
* 设置自动调节亮度模式
*/
public static void setScreenBrightnessMode(Context context, int mode) {
if (mode != Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL
&& mode != Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC ) {
return;
}
Settings.System.putInt(context.getContentResolver(),
Settings.System.SCREEN_BRIGHTNESS_MODE , mode);
}
注意:如果应用异常关闭,系统亮度就无法恢复。
1.2 半透明黑色蒙层
给界面增加一层半透明事件穿透(不可点击不能focus)的全屏View,减少屏幕亮度。
WindowManager manager = (WindowManager)getSystemService(Context.WINDOW_SERVICE);
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.MATCH_PARENT ,WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.TYPE_APPLICATION ,
WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE |WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT);
params.gravity = Gravity.TOP;
params.y = 10 ;
TextView tv = new TextView( this);
tv.setBackgroundColor(0xAA000000 );
manager.addView(tv,params);
二、常用的夜间模式实现
上面提了两种简单的支持用户夜间阅读的方案,不过很明显,这两种方案都并不让人满意,因为方案的最终效果是以降低色差对比度来在夜间保护眼睛的,这会影响用户的信息获取能力。对于一个复杂或者说用户量很大的应用,我们需要一套真正的夜间模式。
真正的夜间模式应该是什么样的呢?
手机屏幕上展现的数据,基本就是文字/图片/视频/音频四种基本形式,音频不用考虑。我们应该采取四个步骤来实现一个好的夜间模式:
1)对界面背景,正常模式下白色等浅色背景应该变成黑色/灰色之类的深色背景,来保证屏幕亮度的降低,避免夜间阅读对眼睛的伤害;
2)对文字,因为应用大部分区域的颜色都变为了深色,为了让用户能方便的看到深色背景上的内容,文字字体颜色应该变为浅色,来形成对比效果;
3)对图片,应用内可能加载深色图片和浅色图片,一个好的夜间模式,应该对图片上一层蒙板,来避免加载浅色图片带来的视觉刺激;
4)对视频,视频部分不建议使用蒙板来降低亮度,因为可能会影响用户的信息接收能力,通常是在播放界面增加亮度变化功能,由用户来决定屏幕亮度。
回到夜间模式实现的主题,我们的最大挑战在于前两条,因为对图片的蒙层,我们可以通过第一节中的全局蒙层或屏幕亮度来处理,不是急迫需要解决的部分,对视频的亮度调节,本身就可以作为视频一项功能实现,实现方法上一节也有描述了。
另外,Android自身对夜间模式的支持很弱,很多方案都要求切换了夜间模式后recreate界面来使切换生效,但这是非常影响体验