Android 基于自由窗口实现小窗模式分析(Android 10)
在小窗口模式定制过程中,难免会遇到一些奇怪的问题,需要系统层去做支持。
1 如何更新 TOP RESUMED 窗口UI
在多窗口模式下,如何在最顶部(TOP RESUMED状态)的应用更新时获取到通知,以便我们去更新窗口的相关UI。这里有两种思路:
- 获取当前resumed窗口列表,提供接口与窗口通信,并通知窗口更新。
- 当top resumed 更新时,通知当前窗口做相关更新。
以下均使用伪代码 FreeformManager.isInSmallScreen() 代表处于小窗模式。
//frameworks/base/core/java/android/app/freeform/FreeformManager.java
public class FreeformManager {
// 启动自由窗口
public static void startFreeSplit(Context context, Intent intent, Rect rect, int position) {
if (intent == null || context == null) {
return;
}
intent.setFlags(Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT | Intent.FLAG_ACTIVITY_NEW_TASK);
ActivityOptions options = ActivityOptions.makeBasic();
options.setLaunchWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM);
if (rect != null) {
options.setLaunchBounds(rect);
}
context.startActivity(intent, options.toBundle());
}
// 是否处于小窗模式
public static void isInSmallScreen() {
if (intent == null || context == null) {
return;
}
intent.setFlags(Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT | Intent.FLAG_ACTIVITY_NEW_TASK);
ActivityOptions options = ActivityOptions.makeBasic();
options.setLaunchWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM);
if (rect != null) {
options.setLaunchBounds(rect);
}
context.startActivity(intent, options.toBundle());
}
这里提供思路2的相关实现:
Top resumed activity 更新时通知UI更新:
//frameworks/base/core/java/android/app/Activity.java
final void performTopResumedActivityChanged(boolean isTopResumedActivity, String reason) {
//当活动获得或失去系统中的顶部恢复位置时调用。从 AndroidQ 开始,可以在多窗口和多显示模式下同时恢复多个活动。
//应使用此回调而不是 onResume 来指示活动可以尝试打开独占访问设备(如相机)。它将始终在活动恢复之后和暂停之前传递。
onTopResumedActivityChanged(isTopResumedActivity);
// add by ian4u: top resumed activity 更新时通知UI更新
if (isInMultiWindowMode() && FreeformManager.isInSmallScreen()) {
updateDecorCaption();
}
}
private void updateDecorCaption() {
mHandler.postDelayed(() -> {
if (mDecor != null) {
DecorView decorView = (DecorView) this.mDecor;
// 更新 decorView UI, decorView包含了自由窗口的UI实现,有兴趣的自行分析。。。
decorView.updateDecorTitle();
}
}, 30);
}
以下是 DecorView 的修改:
// frameworks/base/core/java/com/android/internal/policy/DecorView.java
private View mForegroundView;
public void updateDecorTitle() {
boolean inCenterScale = FreeformManager.isInSmallScreen();
//设置小窗口顶部控制栏是否显示
if (mDecorCaptionView != null) {
mDecorCaptionView.updateCaption(inCenterScale ? View.VISIBLE : View.GONE);
}
Drawable foreDrawable = mContext.getDrawable(inCenterScale
? R.drawable.decor_caption_content_unfocused
: R.drawable.decor_caption_content);
// setForeground(foreDrawable);
// 设置小窗口的焦点样式
LayoutInflater layoutInflater = mWindow.getLayoutInflater();
if (mForegroundView == null) {
mForegroundView = layoutInflater.inflate(R.layout.decor_foreground, null);
addView(mForegroundView, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
}
mForegroundView.setForeground(foreDrawable);
// 设置小窗口圆角
ViewOutlineProvider viewOutlineProvider = new ViewOutlineProvider() {
@Override
public void getOutline(View view, Outline outline) {
if (view.getWidth() != 0 && view.getHeight() != 0) {
outline.setRoundRect(new Rect(0, 0, view.getMeasuredWidth(),
view.getMeasuredHeight()), 26);
}
}
};
setOutlineProvider(inCenterScale ? viewOutlineProvider : null);
setClipToOutline(inCenterScale);
}
以下是 DecorCaptionView 的修改,该类是顶部控制栏控件, 布局文件在
frameworks/base/core/res/res/layout/decor_caption.xml文章来源:https://www.toymoban.com/news/detail-501119.html
// frameworks/base/core/java/com/android/internal/widget/DecorCaptionView.java
public class DecorCaptionView extends ViewGroup implements View.OnTouchListener,
GestureDetector.OnGestureListener {
private View mSwitch;
public void updateCaption(int visible) {
if (mCaption != null) {
mCaption.setVisibility(visible);
}
}
}
ResizeTask 时,发送广播通知activity 更新UI:文章来源地址https://www.toymoban.com/news/detail-501119.html
final void performCreate(Bundle icicle, PersistableBundle persistentState) {
dispatchActivityPreCreated(icicle);
mCanEnterPictureInPicture = true;
restoreHasCurrentPermissionRequest(icicle);
if (persistentState != null) {
onCreate(icicle, persistentState);
} else {
onCreate(icicle);
}
//省略代码。。。
mFragments.dispatchActivityCreated();
mActivityTransitionState.setEnterActivityOptions(this, getActivityOptions());
dispatchActivityPostCreated(icicle);
// add by ian4u:
//当任务栈大小改变(ATMS.resizeTask)时,发送广播, 通知当前窗口做修改
if (isInMultiWindowMode() && FreeformManager.isInSmallScreen() ) {
IntentFilter filter = new IntentFilter(Intent.ACTION_CAPTION_UPDATE);
registerReceiver(mBroadcastReceiver, filter);
}
}
final void performDestroy() {
// 销毁广播注册
if (isInMultiWindowMode() && FreeformManager.isInSmallScreen()) {
unregisterReceiver(mBroadcastReceiver);
}
dispatchActivityPreDestroyed();
mDestroyed = true;
mWindow.destroy()
到了这里,关于Android 基于自由窗口实现小窗模式的分析(Android 10)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!