屏幕旋转后无法使用setContentView(或加载片段)更改布局?

时间:2016-09-07 02:14:51

标签: c# android xamarin xamarin.android

我想用C#和Java标记这个问题;对于Java程序员,我相信你可以很容易地理解C#代码,它们非常相似。

自从知道如何编程以来,我遇到了许多奇怪的问题,这是最奇怪的问题之一,这肯定是Android引擎和API的问题。

首先,我想简要描述它有多奇怪。你能想到电话setContentView没有做任何事情的任何情况吗?没有抛出异常,Activity的内容视图根本没有改变为我想要的东西(即使传递null值也没有发生任何事情)。

事实上,我没有尝试过测试其他一些方案来重现问题。但是我将在这里描述的场景应该很容易重现这个问题。

您需要一个简单的自定义Fragment,您可以在其中加载一个简单的Button布局。现在在onCreateView回调中,您需要对布局进行充气,连接Click的{​​{1}}事件,以通过自定义片段触发另一个公开事件。

Button Fragment设置为RetainInstance。现在在true中,您声明了自定义MainActivity 静态 字段。在Fragment的{​​{1}}回调中,Fragment仅创建一次(通过核对null)。我们将自定义OnCreate的公开点击事件与处理程序联系起来,该处理程序只使用MainActivity显示新内容。

代码在没有屏幕旋转的情况下工作正常。但是在屏幕旋转之后,Fragment根本无效,并且没有例外。这听起来像点击事件没有被触发,但实际上它仍然被解雇了,而 SetContentView实际上被称为 (我很确定这一点,因为我通过在那里设置断点来调试它。

以下是代码:

主要布局 :(此名称为SetContentView

SetContentView

自定义片段

Main.axml

MainActivity

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:id="@+id/container"
   android:orientation="vertical"
   android:layout_width="match_parent"
   android:layout_height="match_parent">
</LinearLayout>

那就是重现奇怪问题的所有设置。 如上所述,代码运行良好,无需旋转屏幕。但是在旋转屏幕后,public class CustomFragment : Fragment { //the click event public event EventHandler ButtonClick; void onButtonClick(EventArgs e){ var handler = ButtonClick; if(handler != null) handler(this,e); } public override View OnCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState){ //we can even use a simple layout as a Button. var button = new Button(parent.Context); button.Click += (s,e) => { onButtonClick(EventArgs.Empty); }; return button; } } 什么都不做 ,虽然它肯定会被称为 (我确实知道通过在通话中设置断点)。

我认为另外一件事也很重要的是[Activity(Label = "Hello", MainLauncher = true] public class MainActivity : AppCompatActivity { //the static custom Fragment static CustomFragment frag; protected override void OnCreate(Bundle bundle){ base.OnCreate(bundle); SetContentView(Resource.Layout.Main); //this ensures that the code inside if block is run just once //between screen rotations if(frag == null){ frag = new CustomFragment(this); frag.RetainInstance = true; frag.ButtonClick += (s,e) => { //whatEverView here is any View, layout resource id //or even null value. SetContentView(whatEverView); }; //load the fragment into the container in the Main layout var ft = FragmentManager.BeginTransaction(); ft.Replace(Resource.Id.container, frag); ft.Commit(); } } } 似乎没有被调用之后的所有调用(因为在SetContentView运行之后没有达到设置的断点,就像那里一样是SetContentView打断那里的代码)。没有例外且应用程序没有崩溃,只是在此之后正常运行(再次单击按钮将再次调用SetContentView)。

这很奇怪,内心发生的事情肯定是我不知道的。阅读源代码不是我的优势,部分原因是因为我还不熟悉Android编程。

您可能需要尝试在自己的计算机上复制问题以了解此问题。

最后,代码在Kitkat和Marshmallow Android OS上运行(由Visual Studio Emulator运行)。

2 个答案:

答案 0 :(得分:1)

1)重新创建活动后,您将失去隐含this中原始活动(this.SetContentView(whatEverView);)的范围。

2)在您的片段OnCreateView中,您在内部使用Context ViewGroup MainActivity实例OnAttach,但每次活动都会更改由于配置更改而重新创建。 OnDetachCustomFragment应该用于分配当前的Context / Activity并清除/清空它。

3)不应使用FragmentManager.FindFragmentByTag的静态var,而应使用CustomFragment,并根据需要重新创建片段,因为操作系统可以回收片段,即使它将被保留,你将在野外追逐幻影崩溃......

这是一个快速修复示例,Button的内部仍然需要重置/清除旧的事件处理程序,当片段与Activity分离并更正{{1}使用旧/新上下文。

public class MainActivity : AppCompatActivity
{
    const string customFragmentTag = "custom_fragment";
    CustomFragment frag;
    protected override void OnCreate(Bundle bundle)
    {
        base.OnCreate(bundle);
        SetContentView(Resource.Layout.Main);

        frag = FragmentManager.FindFragmentByTag(customFragmentTag) as CustomFragment;
        if (frag == null)
        {
            frag = new CustomFragment(Resource.Layout.Frag);
            FragmentManager.BeginTransaction().Add(Resource.Id.container, frag, customFragmentTag).Commit();
        }
        frag.ButtonClick += (s, e) => SetContentView(Resource.Layout.Frag);
    }
}

如果您的Fragment需要访问当前的Context / Activity,您应该实现以下覆盖(currentActivity是类级别Activity var):

public override void OnAttach(Activity activity)
{
    base.OnAttach(activity);
    currentActivity = activity;
}

public override void OnAttach(Android.Content.Context context)
{
    base.OnAttach(context);
    currentActivity = context as Activity;
}

public override void OnDetach()
{
    base.OnDetach();
    currentActivity = null;
}

答案 1 :(得分:-2)

首先在更换时将TAG设置为您的片段。然后再按下您可以使用按标签查找片段并执行您想要的操作。