9

应用程序问题:

当方向改变时,应用程序会遇到以下问题:

  • FragmentA 和 FragmentC 现在都占用 FrameLayout 容器。

什么工作:一切都按我的意愿工作......在旋转屏幕之前。

活动简介:

EditActivity 目的:编辑集合和项目字段。

此活动以编程方式创建的片段:

  • FragmentA - 用于编辑集合字段的片段
  • FragmentB - 集合中项目的 ListFragment
  • FragmentC - 用于编辑项目字段的片段。

初始布局:FragmentA 位于 FragmentB 之上,每个都有自己的 FrameLayouts。

当用户单击 FragmentB 的列表视图项目时:将 FragmentA 替换为 FragmentC 以允许用户编辑该项目的字段。现在 FragmentC 位于 FragmentB 之上。

这似乎是一个非常简单的概念:活动的顶部用于编辑整个集合的属性或集合中的单个项目。我觉得我在布局上没有做任何奇妙的事情,所以我有点困惑,手机(模拟器)的简单旋转会导致这些问题,我花了这么多时间试图解决。

为什么Android Fragment Guide 示例对我不起作用:他们的示例很像我正在做的事情,但是他们的详细片段要么在新活动中打开,要么在当前活动中的自己的框架中打开,他们没有做任何事情交换片段,因此我无法收集他们将如何使用 onSaveIstanceState 来保留可见的片段,然后在 onCreate 中使用该信息来重新创建在方向更改之前存在的 UI。

编辑:通过将 listfragment 放入 XML 中解决了一个问题,这解决了永久旋转的“加载...”问题。

4

1 回答 1

29

解决了。哦,我走过的兔子洞……无论如何,如果你遇到这样的问题,需要考虑以下几点:

  • 最终我不必在onSaveInstanceState(Bundle outState).
  • 最终,我不必考虑在onSaveInstanceState活动中处理 backstack 或处理它onCreate
  • 当第一次以编程方式将片段“添加”到 FrameLayout 时,请使用replace而不是“添加”——这可能是我麻烦的根源之一。
  • 在 onCreate 检查 savedInstanceState 的 bundle 是否为 null,if(savedInstanceState == null)如果是,那么我知道该活动之前没有因配置更改而被拆除,所以在这里我构建了应该在活动启动时显示的片段。以编程方式在其他地方(即,晚于活动onCreate())的其他片段,它们不属于if,它们属于else
  • else onSaveInstanceState != null而且我知道这件事不为空只有一个原因,因为系统在其中创建了一个名为 outState 的包并将其放入onSaveInstanceState(Bundle outState)活动的onCreate方法中,我现在可以在其中获取我的垃圾。所以在这里我知道了几件事:
    1. 当然,我在活动中创建的片段onCreate仍然是活动的一部分(我没有分离或销毁它们),但是,我不能对通过用户的行为赋予生命的片段提出同样的要求,这些片段可能或当前(在定向又名配置更改时)可能未附加到活动。
    2. 这是一个if-this-thing-is-attached条款的好地方。我最初搞砸的一件事是我没有给我所有以编程方式添加的片段一个标签;给所有以编程方式添加的片段标签。然后我可以找出 savedInstanceState 包是否包含该键 withsavedInstanceState.containsKey(MY_FRAG_TAG)和 withgetFragmentManager().findFragmentByTag(MY_FRAG_TAG)

所以这里是活动的 onCreate(简化):

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

    // ...omitted code...

    if(savedInstanceState == null){                     
        // create fragment for collection edit buttons
        editCollection = FragmentA.newInstance(someVariable);               

        // programmatically add fragment to ViewGroup
        getFragmentManager().beginTransaction().replace(R.id.edit_topFrame, editCollection, EDIT_COLLECTIONS_TAG).commit();


    }
    // else there be stuff inside the savedInstanceState bundle
    else{
        // fragments that will always be in the savedInstanceState bundle
        editCollectionFragment = (FragmentA)getFragmentManager().findFragmentByTag(EDIT_COLLECTIONS_TAG);

        // fragments that may not be in the bundle
        if(savedInstanceState.containsKey(EDIT_ITEM_TAG)){
            editItemFragment = (FragmentC)getFragmentManager().getFragment(savedInstanceState, EDIT_ITEM_TAG);              
        }

    }

    // This fragment is NOT programmatically added, ie, it is statically found in an XML file.
    // Hence, the system will take care of preserving this fragment on configuration changes.
    listFrag = (ListViewFragment)getFragmentManager().findFragmentById(R.id.ListFragment);


    // create adapter
    adapter = new EditCursorAdapter(this, null);

    // set list fragment adapter
    listFrag.setListAdapter(adapter);

    // prepare the loader
    getLoaderManager().initLoader(LOADER_ID, null, this);
}

以及 Activity 的列表片段侦听器,其中 FragmentC 被交换为 FragmentA:

// listfragment listener
@Override
public void listFragListener(Cursor cursor) {

    // checking backstack size
    Log.d(TAG, SCOPE +"backstack size: "+getFragmentManager().getBackStackEntryCount());

    // With each listview click there should be only one item in the backstack.
    getFragmentManager().popBackStack();

    // create new fragment
    editItemFragment = FragmentC.newInstance(cursor);

    // programmatically add new fragment
    FragmentTransaction ft = getFragmentManager().beginTransaction();
    ft.replace(R.id.edit_topFrame, editItemFragment, EDIT_ITEM_TAG);
    ft.addToBackStack("pop all of these");  // was testing different ways of popping
    ft.commit();

    // interesting: this reports the same value as the first log in this method.
    // ...clearly addToBackStack(null).commit() doesn't populate the backstack immediately?
    Log.d(TAG, SCOPE +"backstack size: "+getFragmentManager().getBackStackEntryCount());        
}

而 onSaveInstanceState 就像一只松鸦一样赤裸裸:

    @Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);        

}

摘要:我的活动完全按照我的意愿运作。

现在,如果我有一堆添加的片段,那么我可能会以更加程序化的方式处理它们,而不是硬编码if(savedInstanceState.contains(*hard coded key*). 我对此进行了一些测试,但无法证明其功效,但是对于那里的人来说,这可能会激发您对可以做什么的想法:

  1. 制作一组私有的添加片段:

    // Collection of Frag Tags
    private Set<String> AddedFragmentTagsSet = new HashSet<String>();
    
  2. onAttachFragment做类似的事情:

    @Override
    public void onAttachFragment(Fragment fragment) {
    super.onAttachFragment(fragment);   
    
    // logging which fragments get attached and when
    Log.d(TAG, SCOPE +"attached fragment: " +fragment.toString());
    
    // NOTE: XML frags have not frigg'n tags
    
    // add attached fragment's tag to set of tags for attached fragments
    AddedFragmentTagsSet.add(fragment.getTag());
    
    // if a fragment has become detached remove its tag from the set
    for(String tag : AddedFragmentTagsSet){
        if(getFragmentManager().findFragmentByTag(tag).isDetached()){
            AddedFragmentTagsSet.remove(tag);
        }
        Log.d(TAG, SCOPE +"contents of AddedFragmentTagsSet: " +tag);
    }
    }
    
  3. 然后在活动的onCreate和内的savedInstanceState子句中:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_edit);
    
    // ...omitted code...
    
    if(savedInstanceState == null){                     
        // create fragment for collection edit buttons
        editCollection = FragmentA.newInstance(someVariable);               
    
        // programmatically add fragment to ViewGroup
        getFragmentManager().beginTransaction().replace(R.id.edit_topFrame, editCollection, EDIT_COLLECTIONS_TAG).commit();
    
    
    }
    // else there be stuff inside the savedInstanceState bundle
    else{
        // fragments that will always be in the savedInstanceState bundle
        editCollectionFragment = (FragmentA)getFragmentManager().findFragmentByTag(EDIT_COLLECTIONS_TAG);
    
        //////////// find entries that are common to AddedFragmentTagsSet & savedInstanceState's set of keys ///////////
    
        Set<String> commonKeys = savedInstanceState.keySet();           
        commonKeys.retainAll(AddedFragmentTagsSet);
    
        for(String key : commonKeys){
            editItemFragment = FragmentC)getFragmentManager().getFragment(savedInstanceState, key);             
    
        }           
    }
    }
    

...但这未经测试,只是为了激发想法而提出;在试图找出我的活动处理配置更改的问题时,我确实在这个方向上磕磕绊绊,并认为它可能会为合适的人带来成果;尽管最终,显然,这次我找到了一种更简单的方法来解决我的问题。

于 2013-05-25T16:18:41.013 回答