Android用户界面改进最明显的就是MD了。MD是Google于2014年推出的设计语言,它是一套完整的设计系统,包含了动画、样式、布局、组件等一系列与设计有关的元素。通过对这些行为的描述,让开发者设计出更符合目标的软件,同时对这些软件的功能也更易于用户的理解。除此之外,还有另外两个用户界面方面的改进,包括:
在Android7之前的版本上,所有activity都是全屏的,如果不设置透明效果,一次只能看到一个activity的界面。但是从Android7开始,系统支持了多窗口的功能。在有了多窗口支持之后,用户可以同时打开和看到多个应用的界面。这对于用户来说,是非常方便的。
Android上的多窗口功能有下面3种模式。
二分屏模式
这种模式主要在手机上使用。该模式将屏幕一分为二,同时显示两个应用的界面。屏幕中间是一条可以移动调整窗口大小的分隔线。
画中画模式
这种模式主要在TV上使用,在该模式下,某个应用的界面(通常是视频播放类应用)以一个小的浮动窗口形式在屏幕上显示。在Android8上,系统支持在tv之外的设备上使用这一功能。例如,手机上的视频聊天软件可以利用这一功能。
Freeform模式
这种模式类似于常见的桌面操作系统,窗口可以自由拖动和修改大小。但这一功能,在手机设备上,使用起来不是很方便,因此系统上没有提供直接打开这一功能的入口。Android7上,想要打开这一功能,需要借助命令行。
将设备连上pc之后,执行以下两条adb命令即可打开freeform模式:
(1)adb shell settings put global enable_freeform_support 1。
(2)然后重启手机:adb reboot。
重启之后,在近期任务界面会出现一个按钮,这个按钮可以将窗口切换到freeform模式。可能在有些手机上会报权限错误,这个大家可以Google下如何解决。
多窗口不影响和改变原先activity的生命周期。
在多窗口模式下,多个activity可以同时可见,但只有一个activity是resumed状态。所有其他activity都会处于paused状态(尽管它们是可见的)。
在以下三种场景下,系统会通知应用有状态变化,应用可以进行处理:
AndroidManifest.xml中新增了下面两个属性来进行多窗口的控制。
android:resizeableActivity=[“true”|“false”]
这个属性可以用在activity或者application中。如果该属性设置为true,activity将能以分屏和自由形状模式启动。如果此属性设置为false,activity将不支持多窗口模式。如果该值为false,且用户尝试在多窗口模式下启动activity,该activity将全屏显示。对于api目标level为24或更高级别的应用来说,这个值默认是true。
android:supportsPictureInPicture=[“true”|“false”]
这个属性用在activity上,表示该activity是否支持画中画模式。如果android:resizeableActivity=false,这个属性值将被忽略。
除了AndroidManifest.xml,在layout文件中,也新增了一些属性来进行相应的控制。
android:supportsPictureInPicture="true"
android:name=".MainActivity"
android:exported="true">
android:name="android.intent.action.MAIN" />
android:name="android.intent.category.LAUNCHER" />
android:defaultHeight="500dp"
android:gravity="top|end"
android:minHeight="450dp"
android:minWidth="300dp"
android:defaultWidth="600dp">
package com.mvp.myapplication;
import androidx.appcompat.app.AppCompatActivity;
import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;
import com.mvp.myapplication.databinding.ActivityMainBinding;
public class MainActivity extends AppCompatActivity {
// Used to load the 'myapplication' library on application startup.
static {
System.loadLibrary("myapplication");
}
private ActivityMainBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
// Example of a call to a native method
TextView tv = binding.sampleText;
tv.setText(stringFromJNI());
// Android7上还增加了下面这些api:
// this.isInMultiWindowMode()//查询是否处于多窗口模式
// this.isInPictureInPictureMode()//查询是否处于画中画模式
// this.onMultiWindowModeChanged();//多窗口模式变化时进行通知(进入或退出多窗口)
// this.onPictureInPictureModeChanged();//画中画模式变化时进行通知(进入或退出画中画模式)
this.enterPictureInPictureMode();//进入画中画模式,如果系统不支持,这个调用无效
}
/**
* A native method that is implemented by the 'myapplication' native library,
* which is packaged with this application.
*/
public native String stringFromJNI();
}
至于多窗口功能的实现主要依赖于ams和wms这两个系统服务,它们都位于system_server进程中。前者负责所有activity的管理,后者负责所有窗口的管理(不仅activity具有窗口,其他模块也会有窗口,例如,输入法)。ams和wms需要配合一起工作,因为无论是创建还是销毁activity都涉及activity对象和窗口对象的创建和销毁。
App Shortcuts是Android7.1上推出的新功能。借助于这项功能,应用程序可以在launcher中放置一些常用的应用入口以方便用户使用。
每个shortcut可以对应一个或者多个intent,它们各自会通过特定的intent来启动应用程序,例如:
当一个shortcut包括了多个intent时,用户的一次点击会触发所有这些intent,其中的最后一个intent决定了用户所看到的结果。
使用shortcuts有两种方式。
具体用户,读者可以参考下面这篇博客:https://www.jianshu.com/p/6b6f79096256
系统界面属于系统的一部分。系统上方的status bar,以及下方的navigation bar都属于系统界面。除此之外,近期任务界面、锁屏也都是属于系统界面。可见,系统界面是用户交互最多的ui元素。
AOSP源码中,包含了两类Android应用程序:
整个systemUI 由一个application的子类systemUIApplication进行初始化,application对应了整个应用程序的全局状态。系统会保证,application对象一定是应用程序中第一个实例化的对象。并且,application的oncreate方法一定早于应用中所有的activity、service、broadcastreceiver(但是不包含contentProvider)创建之前被调用。
systemUIApplication负责了所有systemUI组件的初始化,这其中就包含了最常见的system Bar(status Bar和navigation Bar合称为system Bar)。
system Bar虽然是系统的一部分,但是为了让应用能够提供更好的用户体验,系统提供了接口来进行控制。开发者可以根据需要来显示或隐藏status bar和navigation bar(它们中两者之一或者全部)。
三种模式
对于system bar的控制,Android系统定义了三种场景模式:
api与使用场景
https://www.jianshu.com/p/7de7bcf604b0
沉浸式全屏
Android4.4引用了一个新的flag:SYSTEM_UI_FLAG_IMMERSIVE。使用这个flag可以使你的应用获得真正的全屏。当这个flag与SYSTEM_UI_FLAG_HIDE_NAVIGATION和SYSTEM_UI_FLAG_FULLSCREEN组合起来使用时,会隐藏整个system bar使得应用获取整个屏幕的触摸事件。
由于应用接受了全部的触摸事件,只有当用户从屏幕边缘往内部滑动时,system bar才会显示出来。这样会清除SYSTEM_UI_FLAG_HIDE_NAVIGATION(如果设置了SYSTEM_UI_FLAG_FULLSCREEN也会被清除)。如果希望system bar在这之后再次自动隐藏起来,可同时设置SYSTEM_UI_FLAG_IMMERSIVE_STICKY。
提醒气泡:
当应用程序中首次进入沉浸式模式时,系统会显示提醒气泡。提醒气泡用于提醒用户如何显示系统栏。
非沉浸式模式:
这是应用程序在进入沉浸式模式之前出现的状态。除此之外,如果使用了SYSTEM_UI_FLAG_IMMERSIVE标志,并且当用户从屏幕边缘往内部滑动时,此时会清除SYSTEM_UI_FLAG_HIDE_NAVIGATION和SYSTEM_UI_FLAG_FULLSCREEN标志。清除这些标志后,system bar将重新出现并保持可见,此时也会是这样。请注意,最好的做法是将所有UI控件与系统栏保持同步,以最大限度地减少屏幕地状态数量,从而提供更加无缝地用户体验。所以这里所有的UI控件都与状态栏一起显示。一旦应用程序进入沉浸式模式,UI控件将于系统栏一起隐藏。为了确保UI可视性与系统栏可见性保持同步,可通过View.OnSystemUiVisibilityChangeListener来响应UI改变事件。
沉浸式模式:
系统栏和其他UI控件被隐藏。可以使用SYSTEM_UI_FLAG_IMMERSIVE_STICKY或SYSTEM_UI_FLAG_IMMERSIVE标志来实现此状态。
响应System UI的改变事件
在system bar隐藏或显示之后,应用自身的UI也可能需要做一些改变。并且,保持这两者的状态同步时一个很好的做法。
如果应用想要关心system ui的变更事件,只需要设置一个监听即可。例如,可以在activity的oncreate方法完成这个监听
View decorView = getWindow().getDecorView();
decorView.setOnSystemUiVisibilityChangeListener(new View.OnSystemUiVisibilityChangeListener() {
@Override
public void onSystemUiVisibilityChange(int visibility) {
//只有在LOW_PROFILE、HIDE_NAVIGATION和FULLSCREEN都没有设置的时候,system bar才是可见的
if ((visibility & View.SYSTEM_UI_FLAG_FULLSCREEN)==0){
//system bar可见时,例如显示action bar或者导航相关的控件
}else{
//system bar不可见时 例如隐藏action bar或者导航相关的控件
}
}
});