调试 UI 的问题有时很棘手,Android Studio 40 内置了全新的布局检查器 (Layout Inspector),它的使用效果类似 Chrome 开发者工具,可以帮助开发者调试 Android 应用的 UI (用户界面)。布局检查器可用于设备和 Android 模拟器,它可以展示视图的层次结构。该工具有助于定位由根节点引起的问题。和上一个版本不同的是,新版本的布局检查器可以以三维的视角来展现视图层次结构,您可以直观地看到视图的布局方式。通过该工具您可以逐层来检查视图层次结构,同时它还会展示所有视图的属性,包括继承自视图父类的属性。
接下来我们一起了解一下最新版本的布局检查器是如何发挥作用的。首先点击窗口的 View 菜单,找到 Tool Window 子菜单,然后选择 Layout Inspector ,这样就打开了布局检查器窗口。
布局检查器仅显示正在运行的进程的 UI 层次结构。也就是说您需要连接到设备或者模拟器上的一个正在运行的可调试应用,有两种方式可以满足该条件:
选择所需的应用进程后,布局检查器会基于当前 UI 层次结构创建一个快照。如果您启用了 Live Updates 选项,那么当您在设备上 *** 作界面时,快照会动态更新。
该版本的布局检查器延续了之前版本的功能并且更加多样化。首先,布局检查器可以用两种方式显示 UI 层次结构: 以二维的轮廓格式,或者以一种称为旋转模式 (rotation mode) 的三维视图形式。
点击 rotation 按钮会在二维和三维视图之间进行切换。当处于旋转模式时,您可以旋转 UI 层次结构。旋转 *** 作可以帮助您更直观地了解视图的组织结构。请注意,旋转仅在 Android 10 或以上的设备上才可以使用。
右侧的窗格会显示所选视图的所有已声明的属性和继承的属性。您可以通过点击任何已声明的属性来打开布局相关的 xml 文件。和旋转特性一样,这个功能也仅适用于 Android 10 以上的设备。
通过布局检查器您还可以将新设计的界面和现有 UI 进行比较。要加载布局设计,点击 Load Overlay,然后选择一个布局设计。成功加载后,您可以改变它的半透明值 (alpha) 来比较现有布局与所选的设计布局之间的区别。
现在大家已经了解了布局检查器的使用方式。那么接下来我们通过实例来看一下如何使用它来解决应用的问题。这里我们有一个简单的示例应用,它包含一个 fragment,其中有一些静态文本和一个。如果您在阅读文章时想同步进行 *** 作,可以先按照下面步骤 *** 作创建工程。
当您运行应用的时候,您会看到一个可爱的 android,但是里面少了一些东西: 底部的导航标签。看一下布局文件,我们可以看到底部的导航视图是存在的,但是屏幕却没有显示它。
看来布局检查器大显身手的时候到了: 我们运行一下程序并检查一下这个问题,成功连接应用进程后,切换到旋转视图会看到应用的 UI 出现了问题。
首先我们可以看到 LinearLayout 里布局了一个工具栏 (toolbar),然后是 navigation host。在它下面,您可以看到导航栏位于最下方——看来底部的导航栏被挤出了屏幕。
有可能是 navigation host 的尺寸设置错了,我们尝试把它的高度设置为 'wrap_content':
回到布局检查器,您可以看到 LinearLayout 的尺寸正常了,但是底部的导航栏的位置不对:
有很多方法可以解决这个问题: 我们可以设置 navigation host 和底部导航栏的 layout_weight 参数,或者我们可以将 LinearLayout 换成 ConstraintLayout,但是切换布局不是本文的重点,所以我们设置一下 layout_weights 参数:
然后得到如下结果:
再运行应用的时候,布局就正常了。
快快尝试一下布局检查器的新特性,然后和我们分享您的使用体验吧。欢迎大家向我们反馈问题,或者告诉我们新的特性需求。
点击这里 查看 Android 官方中文文档 —— 使用布局检查器调试布局
1Android端代码可以在Eclipse中开发(AndroidStudio没有试,应该也可以)
2Unity3D端代码要在Unity中开发
3Android和Unity3D端,两边都需要加入一些代码从而可以使之关联交互。
4将Android端代码编译成jar文件以插件形式放入到Unity端中
5在Unity中将整个项目Build成apk文件,然后安装到手机或模拟器里运行
本文主要讲解1,2,3。对于4,5建议大家去看雨松MOMO的Unity博客的第17篇和第18篇。
UnityPlay:
在编写Android端和Unity3d端代码前,有必要先了解一下可以使两部分交互的类UnityPlay。
个人理解UnityPlay是个Unity提供给外部交互的一个接口类。
为什么是“个人理解”?这我不得不爆粗口了,TMD官网根本就没有相关的API和文档(如果大家有谁找到一定给我来一份,就当我骂自己了)。
在关联Android时,想拿到UnityPlay以及相关类的jar包可以从下面的地址找到:Unity安装路径\Editor\Data\PlaybackEngines\androidplayer\bin在bin文件夹下有一个classesjar的jar文件,它就是我们想要的。
而在bin同目录下有一个src文件,点击到最后有3个类,分别是UnityPlayerActivityjava,UnityPlayerProxyActivityjava,UnityPlayerNativeActivityjava。前两个打开个后只有一行代码,说的是UnityPlayerActivity和UnityPlayerProxyActivity都继承自UnityPlayerNativeActivity。而打开UnityPlayerNativeActivity中居然有代码,而且我估计这应该是UnityPlayerNativeActivity的源码。
由于关于UnityPlay的资料我只找到这么一个,所以我把UnityPlayerNativeActivity中的代码都贴出来,如果我注解有不对的地方希望大家指正。
/
UnityPlayerActivity,UnityPlayerProxyActivity都继承自UnityPlayerNativeActivity
而UnityPlayerNativeActivity继承自NativeActivity
在该类里定义了一些和ANDROID生命周期相同的回调方法,留给自定义的Activity子类重写。
/
public class UnityPlayerNativeActivity extends NativeActivity
{
//UnityPlayer的引用,并且我们不能改变这个引用变量的名字,它被native code所引用
protected UnityPlayer mUnityPlayer;
protected void onCreate (Bundle savedInstanceState)
{
requestWindowFeature(WindowFEATURE_NO_TITLE);
superonCreate(savedInstanceState);
// 设置显示窗口参数
getWindow()takeSurface(null);
setTheme(androidRstyleTheme_NoTitleBar_Fullscreen);
getWindow()setFormat(PixelFormatRGB_565);
// 创建一个UnityPlayer对象,并赋值给全局的引用变量
mUnityPlayer = new UnityPlayer(this);
//为UnityPlayer设置一些参数
if (mUnityPlayergetSettings ()getBoolean ("hide_status_bar", true))
getWindow ()setFlags (WindowManagerLayoutParamsFLAG_FULLSCREEN,
WindowManagerLayoutParamsFLAG_FULLSCREEN);
int glesMode = mUnityPlayergetSettings()getInt("gles_mode", 1);
boolean trueColor8888 = false;
// UnityPlayerinit()方法需要在将view附加到layout之前调用。它将会调用native code
mUnityPlayerinit(glesMode, trueColor8888);
// 从UnityPlayer中获取到Unity的View视图
View playerView = mUnityPlayergetView();
// 将Unity视图加载到根视图上
setContentView(playerView);
// 使Unity视图获取焦点
playerViewrequestFocus();
}
protected void onDestroy ()
{
// 当Activity结束的时候调用UnityPlayerquit()方法,它会卸载之前调用的native code
mUnityPlayerquit();
superonDestroy();
}
// 下面几个方法都是ANDROID相关回调方法,确保在ANDROID执行相应方法时UnityPlayer也需调用相应方法
protected void onPause()
{
superonPause();
mUnityPlayerpause();
}
protected void onResume()
{
superonResume();
mUnityPlayerresume();
}
public void onConfigurationChanged(Configuration newConfig)
{
superonConfigurationChanged(newConfig);
mUnityPlayerconfigurationChanged(newConfig);
}
public void onWindowFocusChanged(boolean hasFocus)
{
superonWindowFocusChanged(hasFocus);
mUnityPlayerwindowFocusChanged(hasFocus);
}
public boolean dispatchKeyEvent(KeyEvent event)
{
if (eventgetAction() == KeyEventACTION_MULTIPLE)
return mUnityPlayeronKeyMultiple(eventgetKeyCode(), eventgetRepeatCount(), event);
return superdispatchKeyEvent(event);
}
}
看完这个类后就知道了为什么在自定义的Activity中继承了UnityPlayerActivity等类以后,只要重写了onCreate并调用superonCreate()方法后不需要任何其他的代码就会自动的显示出Unity3D的视图。因为初始化Unity视图的代码都在UnityPlayerNativeActivity父类中实现了。
ANDROID端代码:
在写ANDROID代码的时候,一定要导入Unity3D提供给我们的jar包,jar包的位置我在上面说了。引入jar包加入到buildpath中这些最基本的我就不多说了。
要想和Unity交互,我们就不能继承ANDROID提供给我们的Activity,我们需要继承刚才jar包中引入的Unity提供的Activity类,一共有这么3个:
UnityPlayerActivity,UnityPlayerProxyActivity,UnityPlayerNativeActivity。具体区别不知道,因为没有文档,没有API,没有源码(这里再次鄙视一下)。刚才我们看过UnityPlayerNativeActivity的代码(虽然很短,但我觉得这个就是源码),知道UnityPlayerActivity,UnityPlayerProxyActivity都是它的子类,而且最终父类为NativeActivity。所以我们继承Unity提供的最外层的子类是最好的选择,我这里选择的是UnityPlayerActivity,因为名字最简单,觉得该封装的都应该封装好了。
public class MainActivity extends UnityPlayerActivity {
private Button topButton;
private Button bottomButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
superonCreate(savedInstanceState);
// 设置test为我们的根布局
setContentView(Rlayouttest);
// 通过刚才的源码分析,知道mUnityPlayer为一个全局的引用变量,而且已经在父类中设置好了,所以直接拿来用就可以了
View playerView = mUnityPlayergetView();
// 将Unity的视图添加到我们为其准备的父容器中
LinearLayout ll = (LinearLayout) findViewById(RidunityViewLyaout);
lladdView(playerView);
// 上面的button设置监听器
topButton = (Button) findViewById(RidtopButton);
topButtonsetOnClickListener(new ViewOnClickListener() {
@Override
public void onClick(View v) {
//发送消息给Unity端,该函数第一个参数为接受消息的类对象,第二个该类对象用接受消息的方法,第三个参数为传递的消息
//所以下面的意思就为:调用Main Camera下面的Previous方法,传送的消息为空
UnityPlayerUnitySendMessage("Main Camera","Previous","");
}
});
// 为下面的button设置监听器
bottomButton = (Button) findViewById(RidbottomBtn);
bottomButtonsetOnClickListener(new ViewOnClickListener() {
@Override
public void onClick(View v) {
//调用Main Camera下面的Next方法,传送的消息为空
UnityPlayerUnitySendMessage("Main Camera","Next","");
}
});
}
}
最后看一下Android端的布局文件,布局很简单,上下各有一个button按钮,两个按钮中间是Unity的视图。
<xml version="10" encoding="utf-8">
<RelativeLayout xmlns:android=">
1Android端代码可以在Eclipse中开发(AndroidStudio没有试,应该也可以)
2Unity3D端代码要在Unity中开发
3Android和Unity3D端,两边都需要加入一些代码从而可以使之关联交互。
4将Android端代码编译成jar文件以插件形式放入到Unity端中
5在Unity中将整个项目Build成apk文件,然后安装到手机或模拟器里运行
本文主要讲解1,2,3。对于4,5建议大家去看雨松MOMO的Unity博客的第17篇和第18篇。
UnityPlay:
在编写Android端和Unity3d端代码前,有必要先了解一下可以使两部分交互的类UnityPlay。
个人理解UnityPlay是个Unity提供给外部交互的一个接口类。
为什么是“个人理解”?这我不得不爆粗口了,TMD官网根本就没有相关的API和文档(如果大家有谁找到一定给我来一份,就当我骂自己了)。
在关联Android时,想拿到UnityPlay以及相关类的jar包可以从下面的地址找到:Unity安装路径\Editor\Data\PlaybackEngines\androidplayer\bin在bin文件夹下有一个classesjar的jar文件,它就是我们想要的。
而在bin同目录下有一个src文件,点击到最后有3个类,分别是UnityPlayerActivityjava,UnityPlayerProxyActivityjava,UnityPlayerNativeActivityjava。前两个打开个后只有一行代码,说的是UnityPlayerActivity和UnityPlayerProxyActivity都继承自UnityPlayerNativeActivity。而打开UnityPlayerNativeActivity中居然有代码,而且我估计这应该是UnityPlayerNativeActivity的源码。
由于关于UnityPlay的资料我只找到这么一个,所以我把UnityPlayerNativeActivity中的代码都贴出来,如果我注解有不对的地方希望大家指正。
/
UnityPlayerActivity,UnityPlayerProxyActivity都继承自UnityPlayerNativeActivity
而UnityPlayerNativeActivity继承自NativeActivity
在该类里定义了一些和ANDROID生命周期相同的回调方法,留给自定义的Activity子类重写。
/
public class UnityPlayerNativeActivity extends NativeActivity
{
//UnityPlayer的引用,并且我们不能改变这个引用变量的名字,它被native code所引用
protected UnityPlayer mUnityPlayer;
protected void onCreate (Bundle savedInstanceState)
{
requestWindowFeature(WindowFEATURE_NO_TITLE);
superonCreate(savedInstanceState);
// 设置显示窗口参数
getWindow()takeSurface(null);
setTheme(androidRstyleTheme_NoTitleBar_Fullscreen);
getWindow()setFormat(PixelFormatRGB_565);
// 创建一个UnityPlayer对象,并赋值给全局的引用变量
mUnityPlayer = new UnityPlayer(this);
//为UnityPlayer设置一些参数
if (mUnityPlayergetSettings ()getBoolean ("hide_status_bar", true))
getWindow ()setFlags (WindowManagerLayoutParamsFLAG_FULLSCREEN,
WindowManagerLayoutParamsFLAG_FULLSCREEN);
int glesMode = mUnityPlayergetSettings()getInt("gles_mode", 1);
boolean trueColor8888 = false;
// UnityPlayerinit()方法需要在将view附加到layout之前调用。它将会调用native code
mUnityPlayerinit(glesMode, trueColor8888);
// 从UnityPlayer中获取到Unity的View视图
View playerView = mUnityPlayergetView();
// 将Unity视图加载到根视图上
setContentView(playerView);
// 使Unity视图获取焦点
playerViewrequestFocus();
}
protected void onDestroy ()
{
// 当Activity结束的时候调用UnityPlayerquit()方法,它会卸载之前调用的native code
mUnityPlayerquit();
superonDestroy();
}
// 下面几个方法都是ANDROID相关回调方法,确保在ANDROID执行相应方法时UnityPlayer也需调用相应方法
protected void onPause()
{
superonPause();
mUnityPlayerpause();
}
protected void onResume()
{
superonResume();
mUnityPlayerresume();
}
public void onConfigurationChanged(Configuration newConfig)
{
superonConfigurationChanged(newConfig);
mUnityPlayerconfigurationChanged(newConfig);
}
public void onWindowFocusChanged(boolean hasFocus)
{
superonWindowFocusChanged(hasFocus);
mUnityPlayerwindowFocusChanged(hasFocus);
}
public boolean dispatchKeyEvent(KeyEvent event)
{
if (eventgetAction() == KeyEventACTION_MULTIPLE)
return mUnityPlayeronKeyMultiple(eventgetKeyCode(), eventgetRepeatCount(), event);
return superdispatchKeyEvent(event);
}
}
以上就是关于Android 将App的内容延伸到状态栏/导航栏全部的内容,包括:Android 将App的内容延伸到状态栏/导航栏、Android 布局优化之 ViewStub、 include、merge、Android Studio 4.0+ 中新的 UI 层次结构调试工具等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)