处理屏幕旋转上的片段重复(带有示例代码) [英] Handle Fragment duplication on Screen Rotate (with sample code)

本文介绍了处理屏幕旋转上的片段重复(带有示例代码)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

我的情况很简单.

我的 Activity 有两种不同的布局,一种是纵向,另一种是横向.

I have an Activity with two different layouts, one in Portrait, another in Landscape.

纵向中,我使用并将Fragment动态添加到其中.

In Portrait, i use <FrameLayout> and add Fragment into it dynamically.

横向中,我使用,所以Fragment静态.(其实这无所谓)

In Landscape, i use <fragment> so the Fragment is static. (in fact this doesn't matter)

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

It first starts in Portrait, then i added the Fragment by simply:

ListFrag listFrag = new ListFrag();
getSupportFragmentManager().beginTransaction()
        .replace(R.id.FragmentContainer, listFrag).commit();

其中 ListFrag 扩展了 ListFragment.

然后我进行屏幕旋转.我发现 listFrag 正在横向模式下重新创建.(其中我注意到 onCreate() 方法被再次调用,并使用非空 Bundle)

Then i do a screen rotate. I found the listFrag is re-creating in the Landscape mode. (In which i noticed the onCreate() method is called again with a non-null Bundle)

我尝试使用 setRetainInstance(false) 就像@NPike 在 this 中所说的那样发布.但是 getRetainInstance() 默认已经是 false.它不符合我的预期 文档说.谁能解释一下?

i tried to use setRetainInstance(false) like @NPike said in this post. But the getRetainInstance() is already false by default. It does not do what i expected as the docs said. Could anyone please explain?

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

The fragment i am dealing with, is a ListFragment, which does setListAdapter() in onCreate(). So the if (container == null) return null; method cannot be used here. (or i dont know how to apply).

我从 得到了一些提示这个帖子.我应该使用 if (bundle != null) setListAdapter(null);else setListAdapter(new ...); 在我的 ListFragment 中?但是,是否有更好的方法在片段被销毁/分离时确实删除/删除片段,而不是在其创建时进行?(如 if (container == null) return null; 方法)

I got some hints from this post. Should i use if (bundle != null) setListAdapter(null); else setListAdapter(new ...); in my ListFragment? But is there a nicer way to indeed removing/deleting the fragment when it is destroyed/detached, rather than doing it in its creation time? (so as the if (container == null) return null; method)

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

The only neat way i found is doing getSupportFragmentManager().beginTransaction().remove(getSupportFragmentManager().findFragmentById(R.id.FragmentContainer)).commit(); in onSaveInstanceState(). But it will raise another problems.

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

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

When screen rotate, the Activity is completely destroyed and re-created. So i can re-add the fragment in onCreate(Bundle) or onRestoreInstanceState(Bundle). But in the case of (1), as well as switching Activities, neither onCreate(Bundle) nor onRestoreInstanceState(Bundle) will be called when user get back to my Activity. I have nowhere to recreate the Activity (and retrieve data from Bundle).

抱歉,我没有说清楚,我已经有了一个决策,getSupportFragmentManager()...replace(...).commit(); 行只在其中运行人像模式.

Sorry I didn't say it clearly that, i already have a decision making, which the getSupportFragmentManager()...replace(...).commit(); line only run in Portrait mode.

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

I have extracted the simple code, for better illustration of the situration :)

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());
        }
    }
}

layout/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" />

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>

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));
    }
}

注意我得到了以下内容

-- 肖像--
ListFrag.onCreate(): null
getRetainInstance = 假

(旋转港口 -> 陆地)

ListFrag.onCreate(): Bundle[{android:view_state=android.util.SparseArray@4052dd28}]
-- 风景 --
以前聚焦的视图在保存期间报告 ID 为 16908298,但在恢复期间找不到.

(轮换土地 -> 港口)

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

(旋转港口 -> 陆地)

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

(轮换土地 -> 港口)

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

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

(rotate Port -> Land)

ListFrag.onCreate(): Bundle[{android:view_state=android.util.SparseArray@4052dd28}]
-- Landscape --
Previously focused view reported id 16908298 during save, but can't be found during restore.

(rotate Land -> Port)

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

(rotate Port -> Land)

ListFrag.onCreate(): Bundle[{android:view_state=android.util.SparseArray@4050fb40}]
-- Landscape --
Previously focused view reported id 16908298 during save, but can't be found during restore.

(rotate Land -> Port)

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

当屏幕不断旋转时,创建的 Fragment 数量不会无限增加,我无法解释.(请帮忙)

where the number of Fragment created will not increase infinitely as screen keep rotating, which i cannot explain. (please help)

推荐答案

我终于想出了一个解决方案,以防止在 Activity 重新创建时重新创建不需要的 Fragment.这就像我在问题中提到的:

I finally come up with a solution to prevent unwanted Fragment re-creating upon the Activity re-creates. It is like what i mentioned in the question:

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

The only neat way i found is doing getSupportFragmentManager().beginTransaction().remove(getSupportFragmentManager().findFragmentById(R.id.FragmentContainer)).commit(); in onSaveInstanceState(). But it will raise another problems...

...有一些改进.

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);
}

isDevicePortrait() 会像:

private boolean isDevicePortrait() {
    return (findViewById(R.id.A_View_Only_In_Portrait) != null);
}

*请注意,我们不能使用getResources().getConfiguration().orientation来确定设备是否当前是字面上的 肖像.这是因为 Resources 对象在RIGHT AFTER 屏幕旋转后发生了变化 - EVEN BEFORE> onSaveInstanceState() 被调用!!

*Notice that we cannot use getResources().getConfiguration().orientation to determine if the device is currently literally Portrait. It is because the Resources object is changed RIGHT AFTER the screen rotates - EVEN BEFORE onSaveInstanceState() is called!!

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

If we do not want to use findViewById() to test orientation (for any reasons, and it's not so neat afterall), keep a global variable private int current_orientation; and initialise it by current_orientation = getResources().getConfiguration().orientation; in onCreate(). This seems neater. But we should be aware not to change it anywhere during the Activity lifecycle.

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

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

(Because in my case, i remove the Fragments from the Layout, and from the Activity. If it is after super.onSaveInstanceState(), the Layout will already be saved into the Bundle. Then the Fragments will also be re-created after the Activity re-creates. ###)

### 我已经证明了这个现象.但是要做什么的原因在重新创建活动时确定片段恢复? 只是我的猜测.如果您对此有任何想法,请回答我的另一个问题.

### I have proved this phenomenon. But the reason of What to determine a Fragment restore upon Activity re-create? is just by my guess. If you have any ideas about it, please answer my another question.

这篇关于处理屏幕旋转上的片段重复(带有示例代码)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆