• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

Android 自由窗口实现小窗模式的(Android 10)

武飞扬头像
ian4u
帮助1

Android 基于自由窗口实现小窗模式分析(Android 10)

在小窗口模式定制过程中,难免会遇到一些奇怪的问题,需要系统层去做支持。

1 如何更新 TOP RESUMED 窗口UI

在多窗口模式下,如何在最顶部(TOP RESUMED状态)的应用更新时获取到通知,以便我们去更新窗口的相关UI。这里有两种思路:

  1. 获取当前resumed窗口列表,提供接口与窗口通信,并通知窗口更新。
  2. 当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

// 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:

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()
学新通

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhgfhkae
系列文章
更多 icon
同类精品
更多 icon
继续加载