簡體   English   中英

類實例中的方法在重新創建帶有片段的活動(根據設備方向更改)后仍保留舊的活動引用

[英]Method in class instance keeps old Activity references after Activity with Fragments is recreated (upon device orientation change)

今天,我想與您分享一個問題,並就如何最好地在我的應用程序中解決這個問題征詢您的意見。

上下文:我想要的是具有ActionBar和Fragments的布局。 對於片段,我使用Android Studio的默認代碼,將ViewPager與帶有SectionsPagerAdapter的ViewPager一起填充PlaceholderFragments。 不同的Fragment具有一種XML,並且與某些TextView有所不同,我根據制表符位置在PlaceholderFragments類的onViewCreated()重寫中更改了這些TextView。 另外,我希望“片段”在橫向模式下具有稍微不同的布局,因此我在“ res / layout-land /”目錄中添加了fragment_activity.xml。 在本活動中,我創建了一個類,在該類中,我有一個稱為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重新創建)初始化的新View,並且可以由仍在運行的SomeClass使用。

問題:當我不更新甚至沒有存儲在SomeClass中的某些東西的ID時,SomeClass仍使用Fragment中的舊(和已刪除)引用的ID! 例如,新片段(在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中? 難道不能以更簡單或更優雅的方式解決這個問題嗎?

我相信我通過更多地研究重新創建活動找到了答案。

另外,根據有關運行時更改文檔

您的應用程序應該能夠在任何時間重新啟動,而不會丟失用戶數據或狀態,以便處理諸如配置更改或用戶收到打來的電話等事件,然后在您的應用程序可能已完成之后很長時間返回到您的應用程序銷毀。 要了解如何恢復活動狀態,請閱讀活動生命周期。

因此,我現在將研究如何在重新創建Activity時使用捆綁包保存和還原信息。

此外,我可能需要暫停當前的SomeClass實例,並在Activity恢復后創建一個新實例。 但是,這非常棘手,因為我在SomeClass內運行了一個for循環,因此尚無線索解決該問題...不過,我認為SomeClass內的方法內部的getSupportActionBar()調用引用有點奇怪到最后一個(並銷毀的)活動,而不是新創建的活動,但這顯然是如何編譯的?

更新最后,我最終選擇通過將它們存儲在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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM