处理屏幕上的片段重复旋转(带示例代码)

mid*_*ite 6 android android-fragments android-listfragment android-fragmentactivity android-framelayout

有一些类似的答案,但不是这种情况.


我的情况很简单.

我有一个具有两种不同布局的活动,一个在Portrait中,另一个在Landscape中.

Portrait中,我动态地使用<FrameLayout>并添加Fragment到它中.

Landscape中,我使用<fragment>Fragment静态的.(事实上​​这没关系)

它首先在Portrait中开始,然后我Fragment简单地添加了:

ListFrag listFrag = new ListFrag();
getSupportFragmentManager().beginTransaction()
        .replace(R.id.FragmentContainer, listFrag).commit();
Run Code Online (Sandbox Code Playgroud)

哪里ListFrag extends ListFragment.

然后我做一个屏幕旋转.我发现listFrag正在横向模式下重新创建. (其中我注意到onCreate()使用非null Bundle再次调用该方法)

我尝试使用setRetainInstance(false)像@NPike 这样的帖子.但这getRetainInstance()已经false是默认情况.它没有像我所说的那样按照文档说的那样做.有人可以解释一下吗?


我正在处理的片段是ListFragment,它确实setListAdapter()onCreate().所以这个if (container == null) return null;方法不能在这里使用.(或者我不知道如何申请).

我从这篇文章中得到了一些提示.我应该用if (bundle != null) setListAdapter(null); else setListAdapter(new ...);在我身上ListFragment吗?但是,当它被销毁/分离时,是否有更好的方法来删除/删除片段,而不是在创建时间内执行它?(所以if (container == null) return null;方法)


编辑:

唯一整洁的方式,我发现是做getSupportFragmentManager().beginTransaction().remove(getSupportFragmentManager().findFragmentById(R.id.FragmentContainer)).commit();onSaveInstanceState().但它会引发另一个问题.

  1. 当屏幕被部分遮挡时,如WhatsApp或TXT对话框弹出,片段也将消失.(这是相对较小的,只是视觉问题)

  2. 屏幕旋转时,活动将被完全销毁并重新创建.所以我可以重新添加片段onCreate(Bundle)onRestoreInstanceState(Bundle).但是在(1)的情况下,以及切换活动时,当用户返回我的活动时,既onCreate(Bundle)不会也onRestoreInstanceState(Bundle)不会被调用.我无处可重新创建Activity(并从Bundle中检索数据).

对不起,我没有说清楚,我已经做出了决定,该getSupportFragmentManager()...replace(...).commit();线只能在人像模式下运行.


示例代码

我已经提取了简单的代码,以便更好地说明情况:)

MainActivity.java

package com.example.fragremovetrial;

import android.os.Bundle;
import android.support.v4.app.FragmentActivity;

public class MainActivity extends FragmentActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        if (findViewById(R.id.FragmentContainer) != null) {
            System.out.println("-- Portrait --");       // Portrait
            ListFrag listFrag = new ListFrag();
            getSupportFragmentManager().beginTransaction()
                    .replace(R.id.FragmentContainer, listFrag).commit();
        } else {
            System.out.println("-- Landscape --");      // Landscape
        }
    }

    @Override
    protected void onResume() {
        super.onResume();
        if (findViewById(R.id.FragmentContainer) != null) {
            System.out.println("getRetainInstance = " +
                    getSupportFragmentManager().findFragmentById(R.id.FragmentContainer).getRetainInstance());
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

布局/ activity_main.xml中

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/FragmentContainer"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" />
Run Code Online (Sandbox Code Playgroud)

layout-land/activity_main.xml (无所谓)

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >
</LinearLayout>
Run Code Online (Sandbox Code Playgroud)

ListFrag.java

package com.example.fragremovetrial;

import android.os.Bundle;
import android.support.v4.app.ListFragment;
import android.widget.ArrayAdapter;

public class ListFrag extends ListFragment {
    private String[] MenuItems = { "Content A", "Contnet B" };

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        System.out.println("ListFrag.onCreate(): " + (savedInstanceState == null ? null : savedInstanceState));

        setListAdapter(new ArrayAdapter<String>(getActivity(), android.R.layout.simple_list_item_1, MenuItems));
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意我得到以下内容

调试消息

- Portrait -
ListFrag.onCreate():null
getRetainInstance = false

(rotate Port - > Land)

ListFrag.onCreate():Bundle [{android:view_state=android.util.SparseArray@4052dd28}]
- Landscape -
之前在保存期间,焦点视图报告了ID 16908298,但在恢复期间无法找到.

(旋转Land - > Port)

ListFrag.onCreate():Bundle [{android:view_state=android.util.SparseArray@405166c8}]
- Portrait -
ListFrag.onCreate():null
getRetainInstance = false

(旋转端口 - > Land )

ListFrag.onCreate():Bundle [{android:view_state=android.util.SparseArray@4050fb40}]
- 横向 -
以前聚焦的视图在保存期间报告了ID 16908298,但在恢复期间无法找到.

(旋转Land - > Port)

ListFrag.onCreate():Bundle [{android:view_state=android.util.SparseArray@40528c60}]
- Portrait -
ListFrag.onCreate():null
getRetainInstance = false

当屏幕保持旋转时,创建的片段数量不会无限增加,这是我无法解释的.(请帮忙)

mid*_*ite 2

我终于想出了一个解决方案来防止在活动重新创建时重新创建不需要的片段。就像我在问题中提到的:

我发现的唯一巧妙的方法是getSupportFragmentManager().beginTransaction().remove(getSupportFragmentManager().findFragmentById(R.id.FragmentContainer)).commit();onSaveInstanceState(). 但这会引发另一个问题......

...有一些改进。

onSaveInstanceState()

@Override
protected void onSaveInstanceState(Bundle outState) {
    if (isPortrait2Landscape()) {
        remove_fragments();
    }
    super.onSaveInstanceState(outState);
}

private boolean isPortrait2Landscape() {
    return isDevicePortrait() && (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE);
}
Run Code Online (Sandbox Code Playgroud)

isDevicePortrait()是这样的:

private boolean isDevicePortrait() {
    return (findViewById(R.id.A_View_Only_In_Portrait) != null);
}
Run Code Online (Sandbox Code Playgroud)

*请注意,我们无法确定getResources().getConfiguration().orientation设备当前是否为纵向。这是因为对象在屏幕旋转后立即Resources更改-甚至在调用之前! onSaveInstanceState()

如果我们不想用来findViewById()测试方向(出于任何原因,而且它毕竟不是那么整洁),请保留一个全局变量并通过inprivate int current_orientation;初始化它。这看起来更整洁。但我们应该注意不要在 Activity 生命周期的任何地方更改它。current_orientation = getResources().getConfiguration().orientation;onCreate()

*请确保我们remove_fragments() 之前 super.onSaveInstanceState()

(因为在我的例子中,我从布局和活动中删除了片段。如果是在之后super.onSaveInstanceState(),布局将已经被保存到捆绑包中。然后在活动重新创建后,片段将被重新创建。 ###)

###我已经证明了这个现象。但是为什么要在 Activity 重新创建时确定 Fragment 恢复呢?只是我的猜测。如果您对此有任何想法,请回答我的另一个问题