【UE4】Android无法全屏(如何找到打包时GameActivity来源)-全屏代码如何实现
天空游荡的鱼
编辑于 2024年03月08日 18:17
收录于文集
共11篇

问题描述:

最近新买了RedMi K50和一加Ace Pro两台机器,一台是Android13,一台是Android12。以前UE4.26打包的程序,不论是刘海屏还是挖孔屏,都是可以全屏的。但安装到这两台机器上竟然不能全屏。正好是刘海和挖孔的位置被留了出来。

UE中如何全屏Android应用

在UE4/UE5中,Android应用的全屏需要设置两个位置,如下图中红框部分。

第一, 最大宽高比默认是2.1,随着市场上屏幕越来越多,该值可能要放大,例如我就设置为了2.3.

第二,勾选是否使用cutout。通俗点讲,就是你的APP是否要使用刘海或者挖孔的部分。

设置完成之后在Android的Manifest文件中会生成下面两项:

代码块
XML
自动换行
复制代码
<meta-data android:name="com.epicgames.ue4.GameActivity.bUseDisplayCutout" android:value="true" />
<meta-data android:name="android.max_aspect" android:value="2.30" />
复制成功

不出意外的话,你的App可以全屏了。

出现问题的原因

通过测试发现,相同的手机,用UE4.26和UE5打包结果完全不同。UE5不会有任何问题。但UE4.26在文中提到的两部手机就不会全屏显示了。

既然通过ProjectSetting修改的是上面提到的两项Manifest配置。那我们就打开Android Studio工程看看。差异是什么。

通过查看Android Studio工程代码,可以看到,是否使用刘海就在于下面这几行代码

代码块
clike
自动换行
复制代码
if (UseDisplayCutout)
{
	// will not be true if not Android Pie or later
	WindowManager.LayoutParams params = getWindow().getAttributes();
        params.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
	getWindow().setAttributes(params);
}
复制成功

所以,只要UseDisplayCutout为true,应该不会出问题吧?那就去找找该值在哪里设置的。

通过翻查代码,该值有两个地方设置

  1. 如果有闪屏界面,是通过闪屏界面传递给GameActivity

  2. 如果没有闪屏界面,该值是直接从Manifest获取的

代码块
clike
自动换行
复制代码
if (SplashScreenLaunch == false && android.os.Build.VERSION.SDK_INT >= 28)
{
	if(bundle.containsKey("com.epicgames.unreal.GameActivity.bUseDisplayCutout"))
	{
		UseDisplayCutout = bundle.getBoolean("com.epicgames.unreal.GameActivity.bUseDisplayCutout");
		Log.debug( "Display cutout set to " + UseDisplayCutout);
	}
	else
	{
		Log.debug( "Display cutout not found. Leaving as " + UseDisplayCutout);
	}
}
复制成功

代码块
clike
自动换行
复制代码
_extrasBundle = getIntent().getExtras();
if (_extrasBundle != null)
{
	ShouldHideUI = _extrasBundle.getString("ShouldHideUI") != null;
	UseDisplayCutout = _extrasBundle.getString("UseDisplayCutout") != null;
}
复制成功

那闪屏界面又是从哪里获取的呢?截取部分闪屏界面代码,可以看到同样是从Manifest获取的。并且还加了一些手机型号的判断,进而最终确认。

代码块
clike
自动换行
复制代码
try {
	ApplicationInfo ai = pm.getApplicationInfo(packageName, PackageManager.GET_META_DATA);
	Bundle bundle = ai.metaData;

	if(bundle.containsKey("com.epicgames.unreal.GameActivity.bUseDisplayCutout"))
	{
		UseDisplayCutout = bundle.getBoolean("com.epicgames.unreal.GameActivity.bUseDisplayCutout");
	}
}
catch (NameNotFoundException | NullPointerException e)
{
	Log.error("Error when accessing application metadata", e);
}

// allow certain models for now to use full area around cutout
boolean BlockDisplayCutout = android.os.Build.VERSION.SDK_INT < 30;
if (android.os.Build.MANUFACTURER.equals("HUAWEI"))
{
	BlockDisplayCutout = false;
}
else if (android.os.Build.MANUFACTURER.equals("HMD Global"))
{
	String model = android.os.Build.MODEL;
	if (model.equals("Nokia 8.1"))
	{
		BlockDisplayCutout = false;
	}
}
else if (android.os.Build.MANUFACTURER.equals("samsung"))
{
	String model = android.os.Build.MODEL;
	if (model.startsWith("SM-G970") || model.startsWith("SM-G973") || model.startsWith("SM-G975") ||
		model.startsWith("SC-03L") || model.startsWith("SCV41") || model.startsWith("SC-04L") ||
		model.startsWith("SCV42") || model.startsWith("SM-N97") || model.startsWith("SM-F700") ||
		model.startsWith("SM-G98") || model.startsWith("SCV47") || model.startsWith("SCG01") ||
		model.startsWith("SCG02") || model.startsWith("SC-51A") || model.startsWith("SC-52A") ||
		android.os.Build.VERSION.SDK_INT >= 28)
	{
		BlockDisplayCutout = false;
	}
}
else if (android.os.Build.MANUFACTURER.equals("Xiaomi"))
{
	String model = android.os.Build.MODEL;
	if (model.startsWith("POCOPHONE F1"))
	{
		BlockDisplayCutout = false;
	}
}
else if (android.os.Build.MANUFACTURER.equals("OnePlus"))
{
	String model = android.os.Build.MODEL;
	if (model.startsWith("KB2000") || model.startsWith("KB2001") || model.startsWith("KB2003") ||
		model.startsWith("KB2005") || model.startsWith("KB2007") || model.startsWith("LE2110") ||
		model.startsWith("LE2111") || model.startsWith("LE2113") || model.startsWith("LE2115") ||
		model.startsWith("LE2117") || model.startsWith("LE2119") || model.startsWith("LE2100") ||
		model.startsWith("LE2101") || model.startsWith("LE2120") || model.startsWith("LE2121") ||
		model.startsWith("LE2123") || model.startsWith("LE2125") || model.startsWith("LE2127") ||
		model.startsWith("IN2020") || model.startsWith("IN2021") || model.startsWith("IN2023") ||
		model.startsWith("IN2025") || model.startsWith("IN2010") || model.startsWith("IN2011") ||
		model.startsWith("IN2013") || model.startsWith("IN2015") || model.startsWith("IN2017") ||
		model.startsWith("IN2019") || model.startsWith("AC2001") || model.startsWith("AC2003") ||
		model.startsWith("BE2025") || model.startsWith("BE2026") || model.startsWith("BE2028") ||
		model.startsWith("BE2029"))
	{
		BlockDisplayCutout = false;
	}
}
if (BlockDisplayCutout)
{
	UseDisplayCutout = false;
}

if (UseDisplayCutout)
{
	// only do this on Android Pie and above
	if (android.os.Build.VERSION.SDK_INT >= 28)
	{
		WindowManager.LayoutParams params = getWindow().getAttributes();
		params.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
		getWindow().setAttributes(params);
	}
	else
	{
		UseDisplayCutout = false;
	}
}
复制成功

这段代码UE4和UE5是有区别的。UE5默认值是通过boolean BlockDisplayCutout = android.os.Build.VERSION.SDK_INT < 30;判断,但UE4是默认为true,后面再通过手机型号判断是否需要忽略Cutout的配置。

从代码中可以看到,闪屏同样会设置是否使用刘海后者挖孔。

两种解决方法

如果你的项目就是UE4开发的,并且不想升级到UE5。毕竟升级是有代价和风险的。那么可以尝试这两种方法。

  1. 通过UPL修改为全屏

  2. 通过修改闪屏界面代码

1. 通过UPL修改

这种方式需要你对UPL了解,前面提到一个教程会告诉你如何在UE4、UE5中进行Android开发。

前面提到了全屏关键的几行代码,我们只需要在UPL里将那几行代码插入即可。但只能修改GameActivity,闪屏界面还是不能全屏,所以在项目设置里取消显示闪屏界面。自己开发一个闪屏界面。

然后在UPL文件中插入以下代码,将java代码插入到GameActivity的OnCreate中。这样游戏界面就可以全屏了。

代码块
clike
自动换行
复制代码
<!--添加代码到OnCreate函数的Super后面-->
    <gameActivityOnCreateAdditions> 
    <insert>
        
try {
	String packageName = getPackageName();
	PackageManager pm = getPackageManager();
	ApplicationInfo ai = pm.getApplicationInfo(packageName, PackageManager.GET_META_DATA);
	Bundle bundle = ai.metaData;
	if(bundle.containsKey("com.epicgames.ue4.GameActivity.bUseDisplayCutout"))
	{
		UseDisplayCutout = bundle.getBoolean("com.epicgames.ue4.GameActivity.bUseDisplayCutout");
	}
}
catch (NameNotFoundException | NullPointerException e)
{
	Log.error("Error when accessing application metadata", e);
}
if (UseDisplayCutout)
{
	// will not be true if not Android Pie or later
	WindowManager.LayoutParams paramsA = getWindow().getAttributes();
    paramsA.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
	getWindow().setAttributes(paramsA);
}
  </insert>
  </gameActivityOnCreateAdditions>
复制成功

这种方法有个缺陷,就是前面提到的闪屏界面需要特殊处理。(如果你会接入SDK,那这也不是个事)

2. 通过修改闪屏代码

这种方法比较直接和粗暴,但效果甚好。UE4、UE5打包Android生成了这么多代码,总有地方去修改这些代码吧。找到他们不就好了。

生成的Android工程代码

不论是GameApplication也好,GameActivity也好。还是SplashActivity。他们都是在UBT的时候生成。在UBT里有一份Android打包的代码UEBuildAndroid.cs、UEDeployAndroid.cs有兴趣可以去翻翻。

这里直接说结果,Engine\Build\Android\Java\src\com\epicgames\ue4到这里取找SplashActivity可以看到,就是完整的代码,并不是模板。那我们将该代码中关于是否忽略UseDisplayCutout的代码修改成与UE5一样就可以了。boolean BlockDisplayCutout = android.os.Build.VERSION.SDK_INT < 30;

这种方法修改了引擎原本的内容,一定要记好。不然更新或升级的时候出Bug就不好了。

重新打包试试,问题完美解决。