6

Làm thế nào để duy trì trạng thái của phân đoạn khi nó được hiển thị trong FragmentTabHost?Làm thế nào để duy trì trạng thái của phân đoạn trong ứng dụng

Nhờ điều này tutorial, tôi có thể triển khai FragmentTabHost trong đơn đăng ký của mình.

Mục đích của tôi là tạo ứng dụng có hoạt động chính chứa một số tab (nằm trên đầu trong suốt ứng dụng). Nhấp vào mỗi tab sẽ mở một đoạn mới bên dưới các tab.

Vấn đề là khi tôi nhấp vào tab làm điều gì đó, sau đó chuyển đến tab chống mở sẽ mở một đoạn mới, sau đó quay lại tab đầu tiên - những thay đổi của tôi ở đây không được duy trì.

Lưu lượng:

enter image description here

tôi thực sự cần phải thực hiện logic này. Nếu phương pháp của tôi sai, vui lòng đề xuất phương án thay thế.

Cảm ơn

Code:

Hoạt động chính

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(); 
    } 
} 

hoạt động chính bố trí

<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

SDK nào bạn đang hỗ trợ? – SBerg413

+0

Android Api phiên bản 10 – reiley

Trả lời

20

Có điều tương tự trong ứng dụng của tôi. Bạn sẽ cần phải sao chép FragmentTabHost để dự án của bạn, chỉ mã của bạn để sử dụng tùy chỉnh mới FragmentTabHost và sau đó thay đổi mã của doTabChanged để thực hiện như sau:

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; 
} 

Sự thay đổi đã được thực hiện là thay vì deattach/attach sự fragment, chúng tôi đang thực hiện hide/show

+0

Logic tốt, hãy để tôi thử nó. Giá trị 'mTabs' được lưu trữ trong dòng ba là gì. Ngoài ra, bạn đã đăng đoạn đầy đủ ở đâu đó để tham khảo. Ngoài ra làm thế nào để gọi 'doTabChanged' – reiley

+0

@ raul8, đoạn mã đầy đủ thực tế là' FragmentTabHost' ban đầu chỉ với những thay đổi mà tôi đã đăng ở trên. Để bắt đầu sử dụng mã này, chỉ cần 1. sao chép toàn bộ mã vào một lớp mới trong dự án của bạn (mã có thể được tìm thấy tại đây: http://grepcode.com/file/repository.grepcode.com/java/ext/com .google.android/android/4.2.2_r1/android/support/v4/app/FragmentTabHost.java) 2. Thay thế mã phương thức bằng mã tôi đã đăng. 3. Trong lớp 'FagTabHostMain' của bạn, hãy sử dụng lớp mới cho thành viên của bạn' FragmentTabHost mTabHost; ' – Sean

+0

Cảm ơn @Sean vì câu trả lời của bạn. Nhưng tôi đoán bạn đã không đặt tất cả mã của bạn ở đây. Bởi vì trên màn hình xoay tôi đã nhận được một số nhức đầu, bởi vì bạn ẩn tab trước bằng cách sử dụng mLastTab với mã đó 'if (mLastTab! = Null) { nếu (mLastTab.fragment! = Null) { ft.hide (mLastTab.fragment)); } } '. Bạn nên hiển thị tab ẩn trước đó trong onAttachedToWindow với một mã như 'if (tab.fragment! = Null && tab.fragment.isHidden()) { ft.show (tab.fragment); } 'ngay trước khi tách mảnh vỡ ra. – hermannovich

2

Tôi tin rằng mảnh của bạn đang được tái khởi tạo mỗi lần bạn chuyển tab, có nghĩa là biến lĩnh vực của bạn được đặt lại.

Bạn có thể sử dụng gói saveInstance để quản lý trạng thái phân đoạn của mình nhưng tôi thấy việc sử dụng SharedPreferences hữu ích và đơn giản hơn rất hữu ích và đơn giản hơn. Điều này cũng có lợi ích của việc giữ trạng thái đã lưu ngay cả khi ứng dụng của bạn được khởi động lại.

Để đọc và ghi các biến để SharedPreferences tôi sử dụng lớp này helper nhỏ:

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); 
} 
} 

Bây giờ, làm ví dụ, để lưu biến mIsViewInitiated của bạn, sau đó trong onPause:

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

Và để truy xuất lại:

@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()); 
    } 
} 

Vì biến mẫu này cho biết một số giao diện người dùng đã được tải, sau đó bạn có thể muốn đặt thành false khi hoạt động bị hủy.

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

Câu trả lời này chỉ là một tùy chọn và thể hiện sở thích cá nhân của tôi, trong khi các tùy chọn khác có thể phù hợp với tình huống của bạn tốt hơn.Tôi khuyên bạn nên xem qua số http://developer.android.com/guide/topics/data/data-storage.html

+0

Câu trả lời của bạn rất tốt, nhưng tôi đã cố gắng tránh các prefs được chia sẻ vì dữ liệu để giữ lại ở mức độ lớn. Ngay cả câu trả lời được cung cấp bởi Sean là cần xử lý thêm, nhưng nó cung cấp cho tôi một sự khởi đầu. Cảm ơn bạn – reiley

+1

Không sao cả. Vì tôi không thấy bất kỳ câu trả lời nào theo hướng này, tôi khuyên bạn nên xem xét các ví dụ demo API về việc giữ lại một đoạn GoogleMap. Tôi sẽ nghĩ rằng bạn sẽ có thể kết hợp setRetainInstance (true) và giữ một tham chiếu đến các mảnh vỡ để khôi phục lại đoạn (đúng) được giữ lại trên các thay đổi của tab. Chỉ cần cung cấp cho bạn một tùy chọn khác để xem xét. – cYrixmorten

0

Sửa đổi Hoạt động của bạn để ghi đè onSaveInstanceState và phương thức onCreate của bạn để khôi phục từ "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

Cảm ơn bạn đã trả lời nhưng không thể gọi phương thức 'mTabHost.onSaveInstanceState()' bên trong 'onSaveInstanceState()'. Điều này có hiệu quả với bạn không? – reiley

0

Như đã trình bày ở trên, bạn có thể lưu và sau đó khôi phục dữ liệu của bạn thông qua Bundle, Preferences được chia sẻ hoặc một db SQLite. Bạn cũng có thể gọi số setRetainInstance(true) trên số Fragment của mình. Điều này sẽ ngăn chặn các mảnh vỡ của bạn bị tái tạo liên tục.

+0

Tôi đã thử phương pháp này nhưng không thể giữ lại trạng thái. Có thể tôi đã làm điều đó sai. Bạn có thể giải thích bằng ví dụ không? – reiley

Các vấn đề liên quan