配置后从backstack更改片段现在共享FrameLayout?

时间:2013-05-24 17:41:18

标签: android android-fragments android-framelayout back-stack android-configchanges

app的问题:

当方向发生变化时,应用会遇到以下问题:

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

什么有效:在旋转屏幕之前,一切都按照我想要的方式运作。

活动描述简要说明:

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

以编程方式将此活动分段:

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

初始布局:FragmentA位于FragmentB的顶部,每个都位于自己的FrameLayouts中。

当用户单击FragmentB的listview项时:将FragmentA替换为FragmentC以允许用户编辑该项的字段。现在FragmentC位于FragmentB上面。

这似乎是一个非常简单的概念:活动的顶部用于编辑整个集合的属性或集合中的单个项目。我觉得我没有对布局做过任何奇妙的事情,所以我有点感到困惑的是,电话(模拟器)的简单旋转会导致这些问题,我正在尝试修复这样一个卑鄙的时间。

为什么Android Fragment Guide example对我不起作用:他们的例子就像我正在做的那样但他们的细节片段要么在新活动中打开,要么在当前活动中的自己的框架中打开,他们不要不进行任何片段交换,所以我无法收集他们如何使用onSaveIstanceState来保存可见的片段,然后在onCreate中使用该信息来重新创建在方向更改之前存在的UI。

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

1 个答案:

答案 0 :(得分:27)

解决。哦,我走过的兔子洞...无论如何,如果你遇到这样的问题需要考虑几件事情:

  • 最终我没有在onSaveInstanceState(Bundle outState)中编写任何代码。
  • 最终,我无需考虑在onSaveInstanceState处理后台堆栈或处理活动的onCreate
  • 当第一次以编程方式将片段“添加”到FrameLayout时,使用replace而不是“添加” - 这可能是我烦恼的根源之一。
  • 在onCreate上检查savedInstanceState的bundle是否为null,if(savedInstanceState == null),如果是,那么我知道之前的配置更改没有拆除活动,所以在这里我构建了应该正确显示的片段在活动开始。在其他地方以编程方式生成的其他片段(即,活动的onCreate()之后),它们不属于if,属于else
  • else onSaveInstanceState != null我知道只有一个原因是这个东西不是null,因为系统在onSaveInstanceState(Bundle outState)中创建了一个名为outState的包,并在活动的onCreate方法中将其绑定,我现在可以得到我的grubbies。所以我在这里知道了几件事:
    1. 我确保我在活动onCreate中创建的片段仍然是活动的一部分(我没有分离或销毁它们),但是,我无法做出相同的声明对于通过用户的操作带来生命的碎片,这些碎片可能会或可能不是当前(在方向,即配置更改时)附加到活动。
    2. 这是if-this-thing-is-attached子句的好地方。我最初搞砸了的一件事是我没有给我的所有程序化添加的片段添加标签;给所有以编程方式添加的片段标签。然后我可以找出savedInstanceState包中是否包含savedInstanceState.containsKey(MY_FRAG_TAG)getFragmentManager().findFragmentByTag(MY_FRAG_TAG)
    3. 的密钥

所以这里是活动的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);
}

活动的列表片段监听器,其中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. 然后在活动的onCreatesavedInstanceState条款中:

    @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);             
    
        }           
    }
    }
    
  4. ...但这是未经测试的,只是为了激发创意;在试图找出我的活动处理配置变化时出了什么问题时,我确实在这个方向上绊倒并摸索,并认为它可能会为合适的人带来成果;虽然最终,显然,我找到了一种更简单的方法来解决我的问题。