【问题标题】:Portrait layout doesnt work when activity starts in landscape当活动以横向开始时,纵向布局不起作用
【发布时间】:2015-07-23 12:32:01
【问题描述】:

这是实际问题的缩小版本。重现我面临的困难。

我以官网developer.android.com的例子来引用我的问题。 Building a Flexible UI

MainActivity 有 2 个布局。一种是layout 文件夹中的默认(小屏幕设备中的纵向)布局。大屏幕和横向模式的另一种布局,保存在layout-largelayout-land 文件夹中。

activity_main.xml 的默认布局仅包含一个 FrameLayout (R.id.fragment_container),我在其中动态添加和替换我创建的 2 个片段。

layout-landlayout-large 文件夹的其他布局相同。它有 2 个静态片段 [R.id.headlines_fragment - 显示标题列表] 和 [R.id.article_fragment - 选择标题时显示详细信息]。水平放置。左侧显示列表,右侧显示详细信息。

这是控制所有片段的MainActivity.java 的代码:

public class MainActivity extends Activity implements OnHeadLineSelectedListener {

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

        if(findViewById(R.id.fragment_container) != null) {
            if(savedInstanceState != null) {
                return;
            }

            HeadlinesFragment firstFragment = new HeadlinesFragment();
            firstFragment.setArguments(getIntent().getExtras());
            getFragmentManager().beginTransaction().add(R.id.fragment_container, firstFragment).commit();
        }
    }

    @Override
    public void onArticleSelected(int position) {
        ArticleFragment articleFrag = (ArticleFragment) getFragmentManager().findFragmentById(R.id.article_fragment);
        if(articleFrag != null && articleFrag.isVisible()) {
            articleFrag.updateArticleView(position);
        } else {

            articleFrag = new ArticleFragment();
            FragmentTransaction transaction = getFragmentManager().beginTransaction().replace(R.id.fragment_container, articleFrag);
            transaction.addToBackStack(null);
            transaction.commit();

        }
    }       
}

活动一开始,我就检查fragment_containerFrameLayout 是否存在。如果它不存在,则具有 2 个片段的布局已加载。因此无需动态添加片段,因为它们已经存在。

否则,我检查savedInstanceState 是否为空。如果为 null,则创建一个新的 HeadlinesFragment 并将其添加到框架中。如果它不为空,则表示之前已经创建了活动,因此必须已经添加了HeadlinesFragment。无需再次添加。所以,return

并且onArticleSelected() 方法用ArticleFragment 替换框架中的现有片段,或者如果在其他布局中,它只是更新已经存在的片段。 It is called from the HeadlinesFragment when an item is selected.

现在,如果我们以纵向模式进入活动,然后更改方向,这一切都可以正常工作。没问题。完美无瑕。

但是如果我们以landscape模式进入activity,一旦我将方向更改为纵向模式,就会显示一个空白屏幕。

原因是,onCreate() 被调用,而savedInstanceState 返回的不是null。因此,HeadlinesFragment 不会被创建并添加到框架中。

是的,如果我删除该检查,那么应用程序可以正常工作,但这意味着每次都会创建一个新的HeadlinesFragment 并将其添加到框架中,并堆叠在彼此之上。这一点也不可取。

我无法通过找出orientation 并应用适当的布局来实现这一点。因为,在大屏设备中,即使是纵向模式,也应该同时显示两个片段。

我尝试了许多复杂的逻辑。但似乎没有任何效果。任何帮助表示赞赏。

以纵向模式输入活动

1> 显示列表项。

2> 单击项目会将片段替换为 ArticleFragment(详细信息)。

3> 改变方向,并排显示。一切正常。

--->

以横向模式进入活动

1> 显示列表和详细信息。一切正常。

2> 但是一旦方向改变,你就会得到空白屏幕。由于没有创建和添加标题片段。

-->

如果有人能指导我如何解决这个问题,那将非常有帮助。而且由于实际项目很大,而且这个逻辑已经实现,逻辑上的剧烈变化不再是一种选择,因为这意味着要重新编写数千行代码。谢谢你。 :)

【问题讨论】:

  • 您可以使用 .replace mtehod 而不是 .add 以避免将片段堆叠在一起。这并不能解决每次都会创建一个新片段的事实
  • 当然我已经想到了,但是第一次创建活动呢?框架中没有任何东西可以替换。我确实试过了。它抛出一个异常。所以,不是一个选择。 :(
  • 真的会抛出异常吗?我很少使用 add 方法,因为它更方便,而且我没有遇到过这个问题。你得到什么例外?
  • 第一次你必须在框架布局中添加一些东西,然后只有你可以从下一次开始替换它。但这确实给了我一个想法。如果我在 xml 文件本身中静态放入一个随机片段会怎样。然后我可以每次都调用replace。我会试试看。

标签: java android android-layout android-fragments landscape-portrait


【解决方案1】:

好的。我有问题。问题是我们在大型设备的 xml 布局中使用 Fragments

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <fragment android:name="com.example.android.fragments.HeadlinesFragment"
              android:id="@+id/headlines_fragment"
              android:layout_weight="1"
              android:layout_width="0dp"
              android:layout_height="match_parent" />

    <fragment android:name="com.example.android.fragments.ArticleFragment"
              android:id="@+id/article_fragment"
              android:layout_weight="2"
              android:layout_width="0dp"
              android:layout_height="match_parent" />

</LinearLayout>

所以 android 试图在 MainActivity 中捕获 savedInstanceState 中的 Fragments。当屏幕旋转时,即使在 potrait 模式下加载了不同的布局,系统也会尝试恢复上述 Fragments。因此系统认为article_fragment 在右侧也可用,并尝试在点击标题时对其进行更新。

那么,解决方案是什么?

我在 MainActivity 中更改了一些代码,没有其他任何内容 :-)

/** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(null);
        setContentView(R.layout.news_articles);

        // Check whether the activity is using the layout version with
        // the fragment_container FrameLayout. If so, we must add the first fragment
        if (findViewById(R.id.fragment_container) != null) {

            // However, if we're being restored from a previous state,
            // then we don't need to do anything and should return or else
            // we could end up with overlapping fragments.
            /*if (savedInstanceState != null) {
                return;
            }*/

            // Create an instance of ExampleFragment
            HeadlinesFragment firstFragment = new HeadlinesFragment();

            // In case this activity was started with special instructions from an Intent,
            // pass the Intent's extras to the fragment as arguments
            firstFragment.setArguments(getIntent().getExtras());

            // Add the fragment to the 'fragment_container' FrameLayout
            getSupportFragmentManager().beginTransaction()
                    .add(R.id.fragment_container, firstFragment).commit();
        }
    }

所以我所做的只是告诉系统我不希望使用super.onCreate(null); 恢复任何东西,所以它现在什么都不恢复。

关于您遇到的空白屏幕

每当您以横向模式开始活动时。它默认加载大显示。无需进入 if 语句。因为它无法在横向模式下获得fragment_container。然后你旋转屏幕加载纵向布局,系统得到savedInstanceState != null,它返回而不加载HeadlinesFragment。所以你会得到银行屏幕。

所以我已经评论了你可以注意到的 If 语句。

/*if (savedInstanceState != null) {
                    return;
  }*/

所以现在它可以正确加载所有内容。 没问题

【讨论】:

  • 似乎是一个可能的解决方案。我今天将测试它。如果它有效,那么我会回来接受这个答案。无论如何,感谢您的时间和精力。 :) 非常感谢。
  • 别担心。我已经对此进行了测试。无论如何,我是开发人员而不是一个好的测试人员......
【解决方案2】:

从此开发者网站下载代码示例并根据您的需要进行修改。

Build Dynamic UI with fragments

超级可靠的代码。

【讨论】:

  • 如果您注意到,这正是我的代码。这是我在这里复制的确切问题。它适用于大屏幕和小屏幕设备。然而在方向改变方面失败了。
  • 哦!让我测试一下代码。我会尽快回复。
【解决方案3】:

我确定FragmentPortrait -&gt; Landscape 中正确呈现而不是相反的原因是有原因的,但有一点很清楚Activity 生命周期

在活动开始时调用。这是最 初始化应该去:调用setContentView(int)来膨胀 活动的 UI,使用findViewById(int) 以编程方式交互 使用 UI 中的小部件,调用 managedQuery(android.net.Uri, String[], String, String[], String) 来检索游标以获取数据 正在显示等。

注意上面写着这是大多数初始化应该去的地方的部分。通过检查 savedInstanceState 是否为空后退出,您将其留给 super 类来恢复您的 Fragment 考虑到先前的视图已被破坏,这不是一个好主意。

我的建议是检查savedInstanceState 的内容,而不是仅仅检查它是否为空。确保它包含足够的数据来恢复您之前的状态,如果没有初始化您的 Fragment。

片段的最佳实践涉及实施所有必要的方法来监控状态。

【讨论】:

  • 这不是问题,因为一旦我进入纵向模式,多次改变方向不会影响片段。 savedInstanceState 确实有适当的信息。从横向模式创建活动时会出现问题。捆绑包不为空,但没有关于片段的信息。因为它还没有被创建。我知道。我知道确切的问题。是的,在这种情况下,savedInstanceState 没有片段。我知道。但我找不到在不妨碍其余代码的情况下修复它的方法。我已经尝试了很多。 :(
  • @SamratDutta 如果可能的话,我可以访问完整的源代码以便运行和检查吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-15
相关资源
最近更新 更多