类实例中的方法在重新创建具有片段的Activity之后保留旧的Activity引用(在设备方向更改时)

时间:2015-01-19 21:19:58

标签: android android-activity android-fragments android-actionbar android-actionbaractivity

今天我一直在努力解决我想与您分享的问题,并就如何在我的应用中最好地解决这个问题征求您的意见。

上下文 我想要的是一个带有ActionBar和Fragments的布局。对于片段,我使用Android Studio的默认代码,使用带有SectionsPagerAdapter的ViewPager填充PlaceholderFragments。不同的片段有一个XML,并且由于我在PlaceholderFragments类的onViewCreated()重写中基于制表符更改的一些TextView而不同。另外,我希望片段在横向模式下具有略微不同的布局,因此我在res / layout-land /目录中添加了fragment_activity.xml。 在这个Activity中我创建了一个类,在这个类中我有一个名为updateControlButtons()的方法:

public class SomeActivity extends ActionBarActivity implements ActionBar.TabListener
(...)
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_hangboard);

    // Set up the action bar.
    final ActionBar actionBar = getSupportActionBar();
    Log.i("onCreate", actionBar.toString());
    (...)
  }

    private class SomeClass {
       public void updateControlButtons() {
       (...)

          runOnUiThread(new Runnable() {
                public void run() {
                    ActionBar actionBar = getSupportActionBar();
                    actionBar.hide();
                    Log.i("updateControlButtons", actionBar.toString());
                    getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); // KeepScreenOn off
                    (...)
                }
          }
       (...)
       }
    (.....)
  }

要处理设备的方向更改,我必须将几个TextView重新初始化为当前选项卡,还要重新初始化屏幕上发生的事件的当前状态(基于存储在SomeClass中的字段)。为此我按如下方式覆盖onViewCreated()方法:

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        //default code:
        super.onViewCreated(view, savedInstanceState); //this does not really do something

        int position = getArguments().getInt(ARG_SECTION_NUMBER); 
        Log.i("TEST onViewCreated position", Integer.toString(position));

        switch (position) {
            // no 0
            case 1: //BEGINNER
                someTextView.setText("0:05");
                break;
            (...)
        }

        // Reload the View IDs into the SomeClass if it is the current view created and SomeClass exists!
        if (SomeClass != null && SomeClass.timerTab == position) { 
             updateCurrentViewIDs(); 
        } // timerTab contains the Tab number on which the user started a timer, these values have to be restored

使用updateCurrentViewIDs的Activity方法:

private static void updateCurrentViewIDs() {
    // for device reorientation

    Log.i("updateCurrentViewIDs", "View re-initialised");
    View mView = mViewPager.findViewWithTag(SomeClass.timerTab); // the View that was playing

    SomeClass.mView = mView;

    // findIDs for current tab
    SomeClass.tvTimer = (TextView)mView.findViewById(R.id.tvTimer);
    SomeClass.tvTimerRemaining = (TextView)mView.findViewById(R.id.tvTimerRemaining);
    SomeClass.tvTimerTotal = (TextView)mView.findViewById(R.id.tvTimerTotal);
    (..)

    //update controls
    SomeClass.updateControlButtons();

正确: TextView ID可以正确更新到由Fragment娱乐初始化的新视图(由于Activity重新创建),并且可以由仍在运行的SomeClass使用。

问题: 当我没有更新甚至没有存储在SomeClass中的内容的ID时,片段中旧(和删除)引用的ID仍由SomeClass使用!例如,新Fragment(在onViewCreated()中)调用的SomeClass.updateControlButtons()仍然在SomeClass.updateControlButtons()中获取ActionBar的 ID!这可以在logcat中看到:

I/onCreate﹕ android.support.v7.internal.app.WindowDecorActionBar@420f7570
I/TEST onViewCreated position﹕ 1
(..)
I/SomeClass.start()﹕ called
I/updateControlButtons﹕ android.support.v7.internal.app.WindowDecorActionBar@420f7570
(..)
(NOW DEVICE IS ORIENTED TO LANDSCAPE)
I/onCreate﹕ android.support.v7.internal.app.WindowDecorActionBar@421592d0
I/TEST onViewCreated position﹕ 1
I/updateCurrentViewIDs﹕ View re-initialised
I/updateControlButtons﹕ android.support.v7.internal.app.WindowDecorActionBar@420f7570

当然,当关闭SomeClass实例并创建新实例时,ActionBar再次被引用正确。

解决方案:当然,我可以简单地将ActionBar全局存储在onCreate方法中,并将其作为updateCurrentViewIDs()方法内SomeClass中的字段。然后我可以用更新的字段替换getSupportActionBar。我还必须为Window做到这一点,正确地打电话给:

getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

顺便说一句,使用setRetainInstance(true)调用对我来说不起作用,因为我实际上想要在横向模式下更改布局。

问题:我不喜欢这种方式,感觉好像我应该在设备的简单方向更改时更新方式。在我看来,应该(并且)甚至没有存储在SomeClass中的东西?!难道这不能以更容易或更优雅的方式解决吗?

1 个答案:

答案 0 :(得分:0)

我相信我通过更多地了解Recreating an Activity来找到答案。

另外,根据documentation on Runtime Changes

您的应用程序应该能够在不丢失用户数据或状态的情况下随时重新启动,以便处理诸如配置更改或用户收到来电之类的事件,然后在很久之后返回应用程序您的申请流程可能已被销毁。要了解如何恢复活动状态,请阅读活动生命周期。

因此,我现在将研究如何使用Bundle在Activity娱乐中保存和恢复信息。

此外,我可能需要暂停当前的SomeClass实例,并在Activity恢复时创建一个新实例。然而这是相当棘手的,因为我在SomeClass中运行了一个for循环,所以没有线索如何解决...仍然,我认为在SomeClass内部的方法内调用getSupportActionBar()有点奇怪到最后(并销毁)的Activity而不是新创建的Activity,但这显然是如何编译的?

<强>更新 我最终通过将这些存储在Activity类范围的静态变量中来选择跟踪当前的ActionBar和Window。我通过在onCreate方法中添加这些行来重新创建这些变量:

@Override
protected void onCreate(Bundle savedInstanceState) {
    (..)
    storedActionBar = actionBar; //PETER: added to be able to access the new ActionBar upon Activiy Recreate (e.g. orientation change)
    storedWindow = getWindow(); // for the KEEP_SCREEN_ON flag