为什么从纵向切换到横向时,FindViewById return 为空?
Why does FindViewById return null when switching from portrait to landscape?
我的一项活动的 InitView 方法调用其 属性
protected ListView MainMenu
{
get
{
return FindViewById<ListView>(Resource.Id.mainmenu);
}
}
...它指的是定义为 RelativeLayout "MainMenuLayout.axml" 一部分的 ListView,位于我的 Android 项目的 "layout" 文件夹中:
<ListView
android:id="@+id/mainmenu"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:choiceMode="singleChoice"
android:layout_below="@id/mainmenuHeader"
android:divider="#e5e5e5"
android:dividerHeight="1dp"
android:background="#fff" />
在纵向模式下,这会按预期工作。但是,当切换到横向模式时,对上面 returns 的 FindViewById 的调用为空。据我了解,Android 应该将布局文件夹用于纵向和横向模式。 Resource.designer.cs还包含
public const int mainmenu = 2131492931;
符合预期。值得注意的是,通过设置 ConfigurationChanges = ConfigChanges.Orientation 和 ConfigChanges.ScreenSize 手动处理方向更改会导致横向模式正常工作。这是 Xamarin 的问题,还是我误解了什么?
1) 创建一个名为 drawable-land 的文件夹
2) 将您的 xml 文件复制并粘贴到此文件夹中,然后再次 运行 应用并尝试更改方向。
ID 为 "mainmenu" 的元素在为此 activity 纵向模式生成的布局中不存在。因此 FindViewById return 为空。您需要确保纵向模式布局包含 "mainmenu" 元素,否则进行空检查,例如:
if (MainMenu != null) {
MainMenu.Visibility = ViewStates.Visible;
}
...在您访问主菜单之前 属性。 (注意: 在访问并非所有布局中都存在的元素之前简单地检查设备方向可能会导致细微错误,因此不应这样做)。
我自己的问题的长答案是这个 activity 实际上并没有在其 InitView 中调用 SetContentView(Resource.Layout.MainMenuLayout),而是使用 SetContentView(Resource.Layout.MasterLayout)。所以我们需要查看 MasterLayout.axml,而不是 MainMenuLayout.axml。更重要的是 MasterLayout.axml 有两个版本,一个在 "layout-port" 文件夹中,另一个在 "layout" 中。 "layout" 中的那个有 include 指令
<include layout="@layout/MainMenuLayout" />
...但是 "layout-port" 中的版本没有这个包含。这就是为什么 "mainmenu" 元素只存在于横向布局中而不存在于纵向布局中的原因。
故事的寓意:在布局中使用 include 指令时,请保持房屋井然有序。
[编辑: 详细说明此响应中的 "subtle bugs" 注释:如果您在访问仅存在的元素之前使用 WindowManager.DefaultDisplay.Rotation 检查设备方向例如纵向模式下,FindViewById 可以仍然return null。在 Xamarin 上,Activity 可能处于设备方向为纵向但尚未创建纵向视图元素的情况。如果您在切换活动后立即快速翻转设备,就会发生这种情况。 Activity 的不一致版本将很快被停止并使用正确的配置恢复,但它可以持续足够长的时间以至于您的代码将执行并且您会收到 NullReferenceException。这就是为什么你应该检查 FindViewById returns 是否为空,而不仅仅是查看设备配置。
我不确定做空检查是否对其他人有帮助,毫无意义的说默认情况下应该断言空引用。此外,不应在每次访问时都加载视图,只有当它不存在时才应该分配它...但这只是 my2cents
在我的例子中,findViewByID 返回了一个空值,因为我在 Activity 中调用了它,但视图在层次上属于片段。
因此您只需将 MainMenu 属性 移动到片段,然后通过 Activity:
中的片段访问它
myFragment.MainMenu
我的一项活动的 InitView 方法调用其 属性
protected ListView MainMenu
{
get
{
return FindViewById<ListView>(Resource.Id.mainmenu);
}
}
...它指的是定义为 RelativeLayout "MainMenuLayout.axml" 一部分的 ListView,位于我的 Android 项目的 "layout" 文件夹中:
<ListView
android:id="@+id/mainmenu"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:choiceMode="singleChoice"
android:layout_below="@id/mainmenuHeader"
android:divider="#e5e5e5"
android:dividerHeight="1dp"
android:background="#fff" />
在纵向模式下,这会按预期工作。但是,当切换到横向模式时,对上面 returns 的 FindViewById 的调用为空。据我了解,Android 应该将布局文件夹用于纵向和横向模式。 Resource.designer.cs还包含
public const int mainmenu = 2131492931;
符合预期。值得注意的是,通过设置 ConfigurationChanges = ConfigChanges.Orientation 和 ConfigChanges.ScreenSize 手动处理方向更改会导致横向模式正常工作。这是 Xamarin 的问题,还是我误解了什么?
1) 创建一个名为 drawable-land 的文件夹
2) 将您的 xml 文件复制并粘贴到此文件夹中,然后再次 运行 应用并尝试更改方向。
ID 为 "mainmenu" 的元素在为此 activity 纵向模式生成的布局中不存在。因此 FindViewById return 为空。您需要确保纵向模式布局包含 "mainmenu" 元素,否则进行空检查,例如:
if (MainMenu != null) {
MainMenu.Visibility = ViewStates.Visible;
}
...在您访问主菜单之前 属性。 (注意: 在访问并非所有布局中都存在的元素之前简单地检查设备方向可能会导致细微错误,因此不应这样做)。
我自己的问题的长答案是这个 activity 实际上并没有在其 InitView 中调用 SetContentView(Resource.Layout.MainMenuLayout),而是使用 SetContentView(Resource.Layout.MasterLayout)。所以我们需要查看 MasterLayout.axml,而不是 MainMenuLayout.axml。更重要的是 MasterLayout.axml 有两个版本,一个在 "layout-port" 文件夹中,另一个在 "layout" 中。 "layout" 中的那个有 include 指令
<include layout="@layout/MainMenuLayout" />
...但是 "layout-port" 中的版本没有这个包含。这就是为什么 "mainmenu" 元素只存在于横向布局中而不存在于纵向布局中的原因。
故事的寓意:在布局中使用 include 指令时,请保持房屋井然有序。
[编辑: 详细说明此响应中的 "subtle bugs" 注释:如果您在访问仅存在的元素之前使用 WindowManager.DefaultDisplay.Rotation 检查设备方向例如纵向模式下,FindViewById 可以仍然return null。在 Xamarin 上,Activity 可能处于设备方向为纵向但尚未创建纵向视图元素的情况。如果您在切换活动后立即快速翻转设备,就会发生这种情况。 Activity 的不一致版本将很快被停止并使用正确的配置恢复,但它可以持续足够长的时间以至于您的代码将执行并且您会收到 NullReferenceException。这就是为什么你应该检查 FindViewById returns 是否为空,而不仅仅是查看设备配置。
我不确定做空检查是否对其他人有帮助,毫无意义的说默认情况下应该断言空引用。此外,不应在每次访问时都加载视图,只有当它不存在时才应该分配它...但这只是 my2cents
在我的例子中,findViewByID 返回了一个空值,因为我在 Activity 中调用了它,但视图在层次上属于片段。
因此您只需将 MainMenu 属性 移动到片段,然后通过 Activity:
中的片段访问它myFragment.MainMenu