6

如何在FragmentTabHost中显示片段的状态?如何在应用程序中维护片段的状态

感谢这个tutorial,我可以在我的应用程序中实现FragmentTabHost

我的意图是创建一个应用程序,其主要活动包含一些选项卡(它贯穿整个应用程序的顶部)。点击每个标签会在标签下方打开一个新的片段。

问题是当我点击一个选项卡做某事,然后去另一个选项卡打开一个新的片段,然后回到第一个选项卡 - 我在这里的更改不会保留。

流量:

enter image description here

我真的需要实现这个逻辑。如果我的方法错误,请提出替代方案。

感谢

代码:

主要活动

public class FagTabHostMain extends FragmentActivity { 
    FragmentTabHost mTabHost; 

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

     mTabHost = (FragmentTabHost) findViewById(android.R.id.tabhost); 
     mTabHost.setup(this, getSupportFragmentManager(), R.id.realtabcontent); 

     mTabHost.addTab(mTabHost.newTabSpec("audio").setIndicator("Audio"), 
       AudioContainerFragmentClass.class, null); 
     mTabHost.addTab(mTabHost.newTabSpec("video").setIndicator("Video"), 
       VideoContainerFragmentClass.class, null); 

    } 

    @Override 
    public void onBackPressed() { 
     boolean isPopFragment = false; 
     String currentTabTag = mTabHost.getCurrentTabTag(); 
     if (currentTabTag.equals("audio")) { 
      isPopFragment = ((AudioContainerFragmentClass) getSupportFragmentManager() 
        .findFragmentByTag("audio")).popFragment(); 
     } else if (currentTabTag.equals("video")) { 
      isPopFragment = ((VideoContainerFragmentClass) getSupportFragmentManager() 
        .findFragmentByTag("video")).popFragment(); 
     } 
     // Finish when no more fragments to show in back stack 
     finish(); 
    } 
} 

主要业务布局

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:orientation="vertical" > 

    <android.support.v4.app.FragmentTabHost 
     android:id="@android:id/tabhost" 
     android:layout_width="match_parent" 
     android:layout_height="match_parent" > 

     <!-- Not Using this one right now --> 
     <FrameLayout 
      android:id="@android:id/tabcontent" 
      android:layout_width="0dip" 
      android:layout_height="0dip" 
      android:layout_weight="0" /> 

    </android.support.v4.app.FragmentTabHost> 

    <FrameLayout 
     android:id="@+id/realtabcontent" 
     android:layout_width="match_parent" 
     android:layout_height="0dip" 
     android:layout_weight="1" /> 

</LinearLayout> 

AudioContainerFragmentClass

public class AudioContainerFragmentClass extends Fragment implements 
     OnClickListener { 

    final String TAG = "AudioContainerFragmentClass"; 
    private Boolean mIsViewInitiated = false; 
    private boolean addToBackStack = true; 
    private Button bNextFragment; 
    private LinearLayout linearLayout; 

    @Override 
    public View onCreateView(LayoutInflater inflater, ViewGroup container, 
      Bundle savedInstanceState) { 
     try { 
      Log.e("AudioContainerFragmentClass", "onCreateView called"); 
      linearLayout = (LinearLayout) inflater.inflate(
        R.layout.audio_fragment_container, container, false); 
     } catch (Exception e) { 
      printException(e.toString()); 
     } 
     return linearLayout; 
    } 

    @Override 
    public void onActivityCreated(Bundle savedInstanceState) { 
     try { 
      super.onActivityCreated(savedInstanceState); 
      Log.e("AudioContainerFragmentClass", "onActivityCreated called"); 
      if (!mIsViewInitiated) { 
       mIsViewInitiated = true; 
       initView(); 
      } 
     } catch (Exception e) { 
      printException(e.toString()); 
     } 
    } 

    private void initView() { 
     try { 
      Log.e("AudioContainerFragmentClass", "initView called"); 
      bNextFragment = (Button) linearLayout 
        .findViewById(R.id.bNextFragment); 
      bNextFragment.setOnClickListener(this); 
      replaceFragment(new AudioFragment(), false); 
     } catch (Exception e) { 
      printException(e.toString()); 
     } 
    } 

    private void replaceFragment(AudioFragment audioFragment, boolean b) { 
     try { 
      FragmentTransaction ft = getChildFragmentManager() 
        .beginTransaction(); 
      if (addToBackStack) { 
       ft.addToBackStack(null); 
      } 
      ft.replace(R.id.audio_sub_fragment, audioFragment); 
      ft.commit(); 
      getChildFragmentManager().executePendingTransactions(); 
     } catch (Exception e) { 
      printException(e.toString()); 
     } 
    } 

    // Called from FagTabHostMain Activity 
    public boolean popFragment() { 
     boolean isPop = false; 
     try { 
      Log.e("AudioContainerFragmentClass", "popFragment called"); 
      if (getChildFragmentManager().getBackStackEntryCount() > 0) { 
       isPop = true; 
       getChildFragmentManager().popBackStack(); 
      } 
     } catch (Exception e) { 
      printException(e.toString()); 
     } 
     return isPop; 
    } 

    @Override 
    public void onClick(View arg0) { 
     TextView tv = (TextView)getActivity().findViewById(R.id.tvaudioTitle); 
     tv.setText("Text changed"); 
    } 

    private void printException(String string) { 
     Log.e("__ERRORR__", string); 
    } 
} 

AudioFragment

public class AudioFragment extends Fragment { 

    @Override 
    public View onCreateView(LayoutInflater inflater, ViewGroup container, 
      Bundle savedInstanceState) { 
     View view = inflater.inflate(R.layout.audio_sub_fragment, container, 
       false); 
     return view; 
    } 
} 
+0

你支持什么min SDK? – SBerg413

+0

Android Api版本10 – reiley

回答

20

在我的应用程序中有同样的事情。 您需要将FragmentTabHost复制到你的项目,将您的代码以使用新的自定义FragmentTabHost,然后的doTabChanged更改代码以实现如下:

private FragmentTransaction doTabChanged(String tabId, FragmentTransaction ft) { 
    TabInfo newTab = null; 
    for (int i=0; i<mTabs.size(); i++) { 
     TabInfo tab = mTabs.get(i); 
     if (tab.tag.equals(tabId)) { 
      newTab = tab; 
     } 
    } 
    if (newTab == null) { 
     throw new IllegalStateException("No tab known for tag " + tabId); 
    } 
    if (mLastTab != newTab) { 
     if (ft == null) { 
      ft = mFragmentManager.beginTransaction(); 
     } 
     if (mLastTab != null) { 
      if (mLastTab.fragment != null) { 
       ft.hide(mLastTab.fragment); 
      } 
     } 
     if (newTab != null) { 
      if (newTab.fragment == null) { 
       newTab.fragment = Fragment.instantiate(mContext, 
         newTab.clss.getName(), newTab.args); 
       ft.add(mContainerId, newTab.fragment, newTab.tag); 
       findViewById(mContainerId).setContentDescription("DEBUG. add fragment to this container"); 
      } else { 
       if (newTab.fragment.isHidden()){ 
        ft.show(newTab.fragment); 
       } 
       else{ 
        ft.attach(newTab.fragment); 
       } 
      } 
     } 

     mPreviousTab = mLastTab; 
     mLastTab = newTab; 
    } 
    return ft; 
} 

的变化这是由是而不是deattach/attach的片段,我们正在做hide/show

+0

好逻辑,让我试试看。第三行中mTabs存储的值是多少?另外,您是否已将完整的代码片段发布到某处供参考。另外如何调用'doTabChanged' – reiley

+0

@ raul8,完整的代码片段实际上是原始的'FragmentTabHost',只有我上面发布的更改。为了开始使用此代码,只需1.将整个代码复制到项目中的新类(代码可在此处找到:http://grepcode.com/file/repository.grepcode.com/java/ext/com .google.android/android/4.2.2_r1/android/support/v4/app/FragmentTabHost.java) 2.将方法代码替换为我发布的方法代码。 3.在你的'FagTabHostMain'类中,为你的成员'FragmentTabHost mTab​​Host;' – Sean

+0

使用新类,谢谢@Sean的回答。但是我想你并没有把你所有的代码放在这里。因为在屏幕旋转时我得到了一些头痛的问题,因为你使用带有代码的mLastTab隐藏了前面的选项卡if(mLastTab!= null)if(mLastTab.fragment!= null){mLastTab.fragment ); } }'。你应该在onAttachedToWindow中用'if(tab.fragment!= null && tab.fragment.isHidden()){ft.show(tab.fragment); }'就在分离片段之前。 – hermannovich

2

我相信你的片段被每次打开的标签页时,这意味着你的字段变量复位重新实例化。

您可能可以使用saveInstance包来管理片段的状态,但我发现使用SharedPreferences更有用和更简单。即使您的应用程序重新启动,这也具有保存已保存状态的好处。

要读取和写入变量,我用这个小助手类SharedPreferences:现在

public class PreferencesData { 

public static void saveString(Context context, String key, String value) { 
    SharedPreferences sharedPrefs = PreferenceManager 
      .getDefaultSharedPreferences(context); 
    sharedPrefs.edit().putString(key, value).commit(); 
} 

public static void saveInt(Context context, String key, int value) { 
    SharedPreferences sharedPrefs = PreferenceManager 
      .getDefaultSharedPreferences(context); 
    sharedPrefs.edit().putInt(key, value).commit(); 
} 


public static void saveBoolean(Context context, String key, boolean value) { 
    SharedPreferences sharedPrefs = PreferenceManager 
      .getDefaultSharedPreferences(context); 
    sharedPrefs.edit().putBoolean(key, value).commit(); 
} 

public static int getInt(Context context, String key, int defaultValue) { 
    SharedPreferences sharedPrefs = PreferenceManager 
      .getDefaultSharedPreferences(context); 
    return sharedPrefs.getInt(key, defaultValue); 
} 

public static String getString(Context context, String key, String defaultValue) { 
    SharedPreferences sharedPrefs = PreferenceManager 
      .getDefaultSharedPreferences(context); 
    return sharedPrefs.getString(key, defaultValue); 
} 

public static boolean getBoolean(Context context, String key, boolean defaultValue) { 
    SharedPreferences sharedPrefs = PreferenceManager 
      .getDefaultSharedPreferences(context); 
    return sharedPrefs.getBoolean(key, defaultValue); 
} 
} 

,作为一个例子,以节省您的mIsViewInitiated变量,然后在onPause:

@Override 
protected void onPause() { 
    PreferencesData.saveBoolean(this, "isViewInitiated", mIsViewInitiated); 
    super.onPause(); 
} 

而对于再次检索它:

@Override 
public void onActivityCreated(Bundle savedInstanceState) { 
    try { 
     super.onActivityCreated(savedInstanceState); 
     Log.e("AudioContainerFragmentClass", "onActivityCreated called"); 
     // will now be true if onPause have been called 
     mIsViewInitiated = PreferencesData.getBoolean(this, "isViewInitiated", false); 
     if (!mIsViewInitiated) { 
      mIsViewInitiated = true; 
      initView(); 
     } 
    } catch (Exception e) { 
     printException(e.toString()); 
    } 
} 

由于此示例变量告诉是否某些用户界面已被加载,那么当活动被销毁时,您可能希望将其设置为false。

@Override 
protected void onDestroy() { 
    PreferencesData.saveBoolean(this, "isViewInitiated", false); 
    super.onDestroy(); 
} 

这个答案仅仅是一个单选项,并显示了我个人的喜好,而其他选项可能适合您的情况更好。我建议看看http://developer.android.com/guide/topics/data/data-storage.html

+0

您的回答非常好,但我试图避免共享首选项,因为要保留的数据的质量很高。即使肖恩提供的答案需要进一步处理,但它确实为我提供了一个开始。谢谢 – reiley

+1

没问题。由于在这个方向上我没有看到任何答案,所以我建议或许看看保留GoogleMap片段的API演示示例。我认为你应该能够结合setRetainInstance(true)并保持对片段的引用,以便重新实例化标签更改上正确的(保留的)片段。只要给你另一个选项来看看。 – cYrixmorten

0

修改您的Activity以覆盖onSaveInstanceState和您的onCreate方法以从“savedInstanceState”中恢复。

public static final String TAB_STATE = "TAB_STATE"; 

@Override 
protected void onSaveInstanceState(Bundle outState) { 
    outstate.putParcelable(TAB_STATE, mTabHost.onSaveInstanceState()); 
    super.onSaveInstanceState(outState); 
} 

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

    mTabHost = (FragmentTabHost) findViewById(android.R.id.tabhost); 
    mTabHost.setup(this, getSupportFragmentManager(), R.id.realtabcontent); 
    if(savedInstanceState==null || savedInstanceState.getParcelable(TAB_STATE)==null){ 
    mTabHost.addTab(mTabHost.newTabSpec("audio").setIndicator("Audio"), 
      AudioContainerFragmentClass.class, null); 
    mTabHost.addTab(mTabHost.newTabSpec("video").setIndicator("Video"), 
      VideoContainerFragmentClass.class, null); 
    } else{ 
     mTabHost.onRestoreInstanceState(savedInstanceState.getParcelable(TAB_STATE)); 
    } 

} 
+0

感谢您的回复,但无法在'onSaveInstanceState()'方法内调用'mTabHost.onSaveInstanceState()'方法。这是为你工作吗? – reiley

0

如上所述,您可以通过软件包,共享首选项或SQLite数据库保存并恢复数据。您可能还想在Fragment上致电setRetainInstance(true)。这将阻止你的片段被重复创建。

+0

我试过这种方法,但无法保留状态。可能是我做错了。你能用例子来解释一下吗? – reiley

相关问题