【问题标题】:Android Fragment created twice orientation changeAndroid Fragment 创建了两次方向更改
【发布时间】:2015-05-24 12:53:29
【问题描述】:

我的片段被创建了两次,即使活动只是将片段添加到内容中一次。当我旋转屏幕时会发生这种情况。此外,每次调用片段的 onCreateView 时,它都会丢失所有的变量状态。

public class MainActivity extends ActionBarActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        if (savedInstanceState == null) { // Checking for recreation
            getSupportFragmentManager().beginTransaction()
                    .add(R.id.container, new AppPanelFragment())
                    .commit();
        }
    }

}

onCreate 在活动中检查 null savedInstanceState 并且只有 null 才会添加片段,所以我看不出为什么片段应该被创建两次?在 if 条件中放置一个断点告诉我它只会被调用一次,因此活动不应该多次添加片段。然而,每次方向改变时仍会调用片段的 onCreateView。

        public class AppPanelFragment extends Fragment {

            private TextView appNameText;

            @Override
            public View onCreateView(LayoutInflater inflater, ViewGroup container,
                                     Bundle savedInstanceState) {
// This method called several times
                View rootView = inflater.inflate(R.layout.fragment_app_panel, container, false);

// 2nd call on this method, appNameText is null, why?
appNameText = (TextView) rootView.findViewById(R.id.app_name);
appNameText.text = "My new App";

    return view;

    }

我设法使用 setRetainInstance(true) 保持变量状态,但这是真正的解决方案吗?我希望片段不会仅在方向更改时创建。

【问题讨论】:

    标签: android android-activity fragment


    【解决方案1】:

    在android中,当手机的方向改变时,activity会被销毁并重新创建。现在,我相信要解决您的问题,您可以使用片段管理器检查片段是否已存在于后台堆栈中,如果不存在则创建它。

    public void onCreated(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            if (savedInstanceState == null) {
                mFragmentManager = getSupportFragmentManager();
                AppPanelFragment fragment  = (AppPanelFragment)mFragmentManager.findFragmentById(R.id.fagment_id);
                if(fragment == null) {
                    //do your fragment creation
                } 
            }
        }
    

    附:我没有对此进行测试,但是一旦您在 findFragmentById 方法中提供了正确的片段 ID,它应该可以工作。

    【讨论】:

      【解决方案2】:

      Fragment 的生命周期与Activity 非常相似。默认情况下,是的,它们将在配置更改期间重新创建,就像 Activity 所做的那样。这是预期的行为。即使使用 setRetainInstance(true)(如果它包含 UI,我会说要非常小心地使用它)你的 View 将被销毁并重新创建,但在这种情况下你的 Fragment 实例不会被销毁——只是View.

      【讨论】:

      • 感谢您的回复。所以也许我的实际问题是我没有正确建模我的片段。我在这个片段中有一些数据,我想在方向改变时保留这些数据。听起来我将无法做到这一点。那么我应该将数据从片段类中抽象出来吗?这里会以哪种方式发生 - 我确实有可以附加模型的 PresentationModel
      • 如果您的数据可以放入捆绑包中,请使用 onSaveInstanceState() 将数据放入该捆绑包中,如果 savedInstanceState 捆绑包不是空。
      • 有道理,是的,数据可以很容易地保存在捆绑包中。这样就不需要 setRetainInstance 了吗?
      【解决方案3】:

      我知道现在回答有点晚了,但是使用 The Code Pimp 回答你可以做接下来的事情:

      如果片段存在于 backstack 中,我们会弹出并删除它以将其添加回来(如果在没有删除它的情况下将其添加回来,则抛出异常,说明它已经存在)。

      片段变量是类成员变量。

      这个方法会在Activity的onCreate方法中调用:

          if (savedInstanceState == null) {
              FragmentManager fragmentManager = getFragmentManager();
              FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
              if (fragmentManager.findFragmentById(getFragmentActivityLayoutContainerId()) == null) {
                  fragment = getNewFragmentInstance();
              } else {
                  fragment = fragmentManager.findFragmentById(getFragmentActivityLayoutContainerId());
                  fragmentTransaction.remove(fragment);
                  fragmentManager.popBackStack();
                  fragmentTransaction.commit();
                  fragmentTransaction = fragmentManager.beginTransaction();
              }
              fragmentTransaction.add(getFragmentActivityLayoutContainerId(), fragment);
              fragmentTransaction.commit();
          }
      

      下一个代码将在片段本身中被调用。

      这是您可以在片段中实现以了解其工作原理的代码的一个小示例。 dummyTV 是片段中心的一个简单文本视图,它根据方向接收文本(为此我们需要一个计数器)。

      private TextView dummyTV;
      private static int counter = 0;
      
      @Override
      protected int getFragmentLayoutId() {
          return R.layout.fragment_alerts_view;
      }
      
      @Override
      protected void saveReferences(View view) {
          dummyTV = (TextView) view.findViewById(R.id.fragment_alerts_view_dummy_tv);
      }
      
      @Override
      public void onViewCreated(View view, Bundle savedInstanceState) {
      
          if (savedInstanceState != null) {
              dummyTV.setText(savedInstanceState.getString("dummy_string"));
          } else {
              dummyTV.setText("flip me!");
          }
      
          dummyTV.append(" | " + String.valueOf(counter));
      }
      
      @Override
      public void onSaveInstanceState(Bundle outState) {
          outState.putString("dummy_string", counter++ % 2 == 0 ? "landscape" : "portrait");
      }
      

      【讨论】:

        【解决方案4】:

        如前所述,在方向更改时,活动将被销毁并重新创建。此外,系统会重新创建 Fragments(any)。

        为确保您的应用程序恢复到之前的状态,onSaveInstanceState() 在 Activity 被销毁之前被调用。

        因此,您可以在活动的 onSaveInstanceState() 方法中存储一些信息,然后在方向更改时将应用程序恢复到相同的状态。

        注意:您无需在方向更改时创建片段,因为片段会重新创建。

        来自http://www.mynewsfeed.x10.mx/articles/index.php?id=15的示例:

        public class MainActivity extends Activity {
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
        
            if ( savedInstanceState == null ){
                  //Initialize fragments
                  Fragment example_fragment = new ExampleFragment();
                  FragmentManager manager = getFragmentManager();
                  FragmentTransaction transaction = manager.beginTransaction();
                  transaction.add(R.id.container, example_fragment, "Example");
            } else{
               //control comes to this block only on orientation change.
        
               int postion = savedInstanceState.getInt("position"); //You can retrieve any piece of data you've saved through onSaveInstanceState()
        
              //finding fragments on orientation change 
              Fragment example_fragment = manager.findFragmentByTag("Example");
        
             //update the fragment so that the application retains its state
             example_fragment.setPosition(position); //This method is just an example
        
            }
        }
        
        @Override
        public void onSaveInstanceState(Bundle outState) {
            super.onSaveInstanceState(outState);
            outState.putInt("position", position); //add any information you'd like to save and restore on orientation change.
            }
        
        }
        

        【讨论】:

          猜你喜欢
          • 2013-04-22
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2023-04-08
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多