2012-02-26 33 views
18

Tôi đã thực hiện một Hoạt động Android đơn giản với ActionBar để chuyển đổi giữa 2 đoạn. Mọi thứ đều ổn cho đến khi tôi xoay thiết bị. Trong thực tế, khi tôi xoay tôi đã có 2 mảnh một trong khác: một trong những hoạt động trước và một trong những đầu tiên. Tại sao? Nếu vòng quay phá hủy và tạo lại hoạt động của tôi, tại sao tôi có được 2 đoạn?Đoạn đôi xoay Android với ActionBar

đang

mẫu:

Hoạt động

package rb.rfrag.namespace; 

import android.app.ActionBar; 
import android.app.ActionBar.Tab; 
import android.app.Activity; 
import android.os.Bundle; 

    public class RFragActivity extends Activity { 
    /** Called when the activity is first created. */ 
    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     // Notice that setContentView() is not used, because we use the root 
     // android.R.id.content as the container for each fragment 

    // setup action bar for tabs 
     final ActionBar actionBar = getActionBar(); 
     actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); 
     //actionBar.setDisplayShowTitleEnabled(false); 

     Tab tab; 
     tab = actionBar.newTab() 
       .setText(R.string.VarsTab) 
       .setTabListener(new TabListener<VarValues>(
         this, "VarValues", VarValues.class)); 
     actionBar.addTab(tab); 

     tab = actionBar.newTab() 
       .setText(R.string.SecTab) 
       .setTabListener(new TabListener<SecFrag>(
         this, "SecFrag", SecFrag.class)); 
     actionBar.addTab(tab); 
    } 
} 

TabListener

package rb.rfrag.namespace; 

import android.app.ActionBar; 
import android.app.Activity; 
import android.app.Fragment; 
import android.app.FragmentManager; 
import android.app.FragmentTransaction; 
import android.app.ActionBar.Tab; 

public class TabListener<T extends Fragment> implements ActionBar.TabListener { 
    private Fragment mFragment; 
    private final Activity mActivity; 
    private final String mTag; 
    private final Class<T> mClass; 

    /** Constructor used each time a new tab is created. 
     * @param activity The host Activity, used to instantiate the fragment 
     * @param tag The identifier tag for the fragment 
     * @param clz The fragment's Class, used to instantiate the fragment 
     */ 
    public TabListener(Activity activity, String tag, Class<T> clz) { 
     mActivity = activity; 
     mTag = tag; 
     mClass = clz; 
    } 

    /* The following are each of the ActionBar.TabListener callbacks */ 

    public void onTabSelected(Tab tab, FragmentTransaction ft) {  
     // Check if the fragment is already initialized 
     if (mFragment == null) { 
      // If not, instantiate and add it to the activity 
      mFragment = Fragment.instantiate(mActivity, mClass.getName()); 
      ft.add(android.R.id.content, mFragment, mTag); 
     } else { 
      // If it exists, simply attach it in order to show it 
      ft.attach(mFragment); 
     } 
    } 

    public void onTabUnselected(Tab tab, FragmentTransaction ft) { 
     if (mFragment != null) { 
      // Detach the fragment, because another one is being attached 
      ft.detach(mFragment); 
     } 
    } 
} 

Trả lời

20

tôi đã giải quyết bằng onSaveInstanceStateonRestoreInstanceState trong Hoạt động để duy trì các tab được lựa chọn và thay đổi onTabSelected như sau.

Sửa đổi cuối cùng tránh việc Android xây dựng lại Phân đoạn mà nó không phá hủy. Tuy nhiên tôi không hiểu tại sao Hoạt động bị phá hủy bởi sự kiện xoay vòng trong khi Fragment hiện tại không có. (Có bạn một số ý tưởng về điều này?)

public void onTabSelected(Tab tab, FragmentTransaction ft) { 
     // previous Fragment management 
     Fragment prevFragment; 
     FragmentManager fm = mActivity.getFragmentManager(); 
     prevFragment = fm.findFragmentByTag(mTag); 
     if (prevFragment != null) { 
      mFragment = prevFragment; 
     } // \previous Fragment management 

     // Check if the fragment is already initialized 
     if (mFragment == null) { 
      // If not, instantiate and add it to the activity 
      mFragment = Fragment.instantiate(mActivity, mClass.getName()); 
      ft.add(android.R.id.content, mFragment, mTag); 
     } else { 
      // If it exists, simply attach it in order to show it 
      ft.attach(mFragment); 
     } 
    } 
+1

này đã làm việc một cách hoàn hảo. Bất kỳ đầu mối nào nếu đây là cách làm đúng đắn? –

+3

Cho đến khi tôi phát hiện ra điều này, điều đó đã khiến tôi cảm thấy chán nản ... Cảm ơn rất nhiều vì câu trả lời này! (Vẫn không thể tin rằng Google sẽ không khắc phục điều đó trong ví dụ của họ) – Patrick

+0

Thú vị. Chỉ tôi mới sử dụng 'android.support.v4.view.ViewPager' ở chế độ dọc và hiển thị cả hai đoạn khi phong cảnh. Xoay hai lần và tôi nhận được hai mảnh trong cảnh quan. Một cái được gọi là một cái gì đó như * android: switcher: 2131099773: 1 * - làm tôi phát điên. Tôi phải tìm một hock để loại bỏ zombie. – Martin

11

Kể từ khi tôi sử dụng một android.support.v4.view.ViewPager trọng onTabSelected sẽ không giúp đỡ. Nhưng bạn vẫn gợi ý chỉ cho tôi đi đúng hướng.

android.support.v4.app.FragmentManager lưu tất cả các đoạn trong onSaveInstanceState của android.support.v4.app.FragmentActivity. Bỏ qua setRetainInstance - Tùy thuộc vào thiết kế của bạn, điều này có thể dẫn đến các đoạn trùng lặp.

Giải pháp đơn giản nhất là để xóa các đoạn lưu trong orCreate của hoạt động:

@Override 
    public void onCreate (final android.os.Bundle savedInstanceState) 
    { 
     if (savedInstanceState != null) 
     { 
     savedInstanceState.remove ("android:support:fragments"); 
     } // if 

     super.onCreate (savedInstanceState); 
… 
     return; 
    } // onCreate 
+0

+1000 Không bao giờ có thể tìm thấy điều này. – Patrick

+1

Điều này hoạt động tốt trừ khi Fragment2 đi vào Landscape, fragment1 được hiển thị. Không chắc chắn nếu tôi là người duy nhất có vấn đề này. Bất kì giải pháp nào? – Art

+0

Làm việc với giải pháp của bạn ở chân dung và phong cảnh. không chồng chéo nữa sau khi chuyển đổi giữa các tab. –

3

Các giải pháp không thực sự đòi hỏi rất nhiều toàn bộ công việc, các bước sau đảm bảo, rằng khi xoay màn hình, lựa chọn tab được duy trì. Tôi chạy vào các mảnh ghép chồng chéo, vì khi xoay màn hình, tab đầu tiên của tôi đã được chọn, không phải thẻ thứ hai được chọn trước khi xoay màn hình và do đó tab đầu tiên chồng chéo nội dung của tab thứ hai.

Đây là cách hoạt động của bạn sẽ giống (Tôi đang sử dụng ActionBarSherlock nhưng điều chỉnh nó nên rất dễ dàng):

public class TabHostActivity extends SherlockFragmentActivity { 

    private static final String SELETED_TAB_INDEX = "tabIndex"; 


    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 

    // Setup the action bar 
    ActionBar actionBar = getSupportActionBar(); 
    actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); 

    // Create the Tabs you need and add them to the actionBar... 

    if (savedInstanceState != null) { 
     // Select the tab that was selected before orientation change 
     int index = savedInstanceState.getInt(SELETED_TAB_INDEX); 
     actionBar.setSelectedNavigationItem(index); 
    } 
    } 

    @Override 
    protected void onSaveInstanceState(Bundle outState) { 
    super.onSaveInstanceState(outState); 
    // Save the index of the currently selected tab 
    outState.putInt(SELETED_TAB_INDEX, getSupportActionBar().getSelectedTab().getPosition()); 
    } 
} 

Và đây là những gì ActionBar.TabListener của tôi trông như thế (một lớp học riêng của nó ở trên Hoạt động):

private class MyTabsListener<T extends Fragment> implements ActionBar.TabListener { 
    private Fragment fragment; 
    private final SherlockFragmentActivity host; 
    private final Class<Fragment> type; 
    private String tag; 

    public MyTabsListener(SherlockFragmentActivity parent, String tag, Class type) { 
     this.host = parent; 
     this.tag = tag; 
     this.type = type; 
    } 

    @Override 
    public void onTabSelected(Tab tab, FragmentTransaction transaction) { 
     /* 
     * The fragment which has been added to this listener may have been 
     * replaced (can be the case for lists when drilling down), but if the 
     * tag has been retained, we should find the actual fragment that was 
     * showing in this tab before the user switched to another. 
     */ 
     Fragment currentlyShowing = host.getSupportFragmentManager().findFragmentByTag(tag); 

     // Check if the fragment is already initialised 
     if (currentlyShowing == null) { 
      // If not, instantiate and add it to the activity 
      fragment = SherlockFragment.instantiate(host, type.getName()); 
      transaction.add(android.R.id.content, fragment, tag); 
     } else { 
      // If it exists, simply attach it in order to show it 
      transaction.attach(currentlyShowing); 
     } 
    } 

    public void onTabUnselected(Tab tab, FragmentTransaction fragmentTransaction) { 
     /* 
     * The fragment which has been added to this listener may have been 
     * replaced (can be the case for lists when drilling down), but if the 
     * tag has been retained, we should find the actual fragment that's 
     * currently active. 
     */ 
     Fragment currentlyShowing = host.getSupportFragmentManager().findFragmentByTag(tag); 
     if (currentlyShowing != null) { 
      // Detach the fragment, another tab has been selected 
      fragmentTransaction.detach(currentlyShowing); 
     } else if (this.fragment != null) { 
      fragmentTransaction.detach(fragment); 
     } 
    } 

    public void onTabReselected(Tab tab, FragmentTransaction fragmentTransaction) { 
     // This tab is already selected 
    } 

Việc triển khai ở trên cũng cho phép thay thế Phân đoạn trong tab, dựa trên thẻ của chúng. Với mục đích này khi chuyển đổi các mảnh trong cùng một tab, tôi sử dụng cùng một tên Thẻ, được sử dụng cho khung ban đầu đã được thêm vào tab.

0

Cảm ơn Martin và asclepix về giải pháp của họ.Tôi có 3 tab và tab đầu tiên chứa 2 mảnh, như thế này:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    xmlns:tools="http://schemas.android.com/tools" 
    android:layout_width="fill_parent" 
    android:layout_height="fill_parent" 
    tools:context=".MainActivity" > 

    <FrameLayout 
     android:id="@+id/frActiveTask" 
     android:layout_width="fill_parent" 
     android:layout_height="50dp" 
     android:layout_alignParentBottom="true" 
     /> 

    <FrameLayout 
     android:id="@+id/frTaskList" 
     android:layout_width="fill_parent" 
     android:layout_height="match_parent" 
     android:layout_alignParentTop="true" 
     android:layout_above="@id/frActiveTask" 
     /> 

</RelativeLayout> 

Sử dụng onRestoreInstanceState, onSaveInstanceStatesavedInstanceState.remove("android:support:fragments"); phương pháp và tuyên bố làm việc gần như tốt, ngoại trừ nếu tab hoạt động của bạn không phải là người đầu tiên và xoay và bấm vào đầu tiên, một màn hình hiển thị rõ ràng xuất hiện và chỉ cho nhấp chuột thứ hai trên tab đầu tiên đến màn hình phân đoạn phù hợp. Sau khi gỡ lỗi mã tôi nhận ra rằng addTab đầu tiên luôn luôn kêu gọi một sự kiện onTabSelected trong người nghe tab, với một mảnh phương pháp add và sau đó khi setSelectedNavigationItem được gọi từ onRestoreInstanceState một detach được thực thi trên tab đầu tiên và một add cho người kia. Gọi add không cần thiết này được khắc phục trong giải pháp của tôi.

hoạt động của tôi

protected void onCreate(Bundle savedInstanceState) { 
    boolean firstTabIsNotAdded = false; 
    if (savedInstanceState != null) { 
     savedInstanceState.remove("android:support:fragments"); 
     firstTabIsNotAdded = savedInstanceState.getInt(SELETED_TAB_INDEX) != 0; 
    } 
    super.onCreate(savedInstanceState); 

    setContentView(R.layout.activity_main); 

// codes before adding tabs 

    actionBar = getSupportActionBar(); 
    actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); 


    tabStartAndStop = actionBar.newTab().setText(getString(R.string.tab_title_start_and_stop)) 
      .setTabListener(
        new FragmentTabListener<StartStopFragment>(this, 
          getString(R.string.tab_title_start_and_stop_id), 
          StartStopFragment.class, 
          firstTabIsNotAdded)); 
    tabHistory = actionBar.newTab().setText(getString(R.string.tab_title_history)) 
      .setTabListener(
        new FragmentTabListener<HistoryFragment>(this, 
          getString(R.string.tab_title_history_id), 
          HistoryFragment.class, 
          false)); 
    tabRiporting = actionBar.newTab().setText(getString(R.string.tab_title_reporting)) 
      .setTabListener(
        new FragmentTabListener<ReportingFragment>(this, 
          getString(R.string.tab_title_reporting_id), 
          ReportingFragment.class, 
          false)); 

    actionBar.addTab(tabStartAndStop); 

     actionBar.addTab(tabHistory); 
     actionBar.addTab(tabRiporting); 

    } 

    @Override 
    protected void onRestoreInstanceState(Bundle savedInstanceState) { 
     if (savedInstanceState != null) { 
      int index = savedInstanceState.getInt(SELETED_TAB_INDEX); 
      actionBar.setSelectedNavigationItem(index); 
     } 
     super.onRestoreInstanceState(savedInstanceState); 
    } 

    @Override 
    protected void onSaveInstanceState(Bundle outState) { 
     super.onSaveInstanceState(outState); 
     // Save the index of the currently selected tab 
     outState.putInt(SELETED_TAB_INDEX, getSupportActionBar().getSelectedTab().getPosition()); 
    } 

Và người nghe tab sửa đổi

public class FragmentTabListener<T extends SherlockFragment> implements com.actionbarsherlock.app.ActionBar.TabListener { 
    private Fragment mFragment; 
    private final Activity mFragmentActivity; 
    private final String mTag; 
    private final Class<T> mClass; 
    private boolean doNotAdd; 

    /** Constructor used each time a new tab is created. 
     * @param activity The host Activity, used to instantiate the fragment 
     * @param tag The identifier tag for the fragment 
     * @param clz The fragment's Class, used to instantiate the fragment 
     */ 
    public FragmentTabListener(Activity activity, String tag, Class<T> clz, boolean doNotAdd) { 
     mFragmentActivity = activity; 
     mTag = tag; 
     mClass = clz; 
     this.doNotAdd = doNotAdd; 
    } 

    /* The following are each of the ActionBar.TabListener callbacks */ 
    public void onTabSelected(Tab tab, FragmentTransaction ft) { 

     // Check if the fragment is already initialized 
     if (mFragment == null) { 
      // If not, instantiate and add it to the activity 
      if(doNotAdd){ 
       doNotAdd = false; 
      }else{ 
       mFragment = Fragment.instantiate(mFragmentActivity, mClass.getName()); 
       ft.add(android.R.id.content, mFragment, mTag); 
      } 
     } else { 
      // If it exists, simply attach it in order to show it 
      ft.attach(mFragment); 
     } 
    } 

    public void onTabUnselected(Tab tab, FragmentTransaction ft) { 
     if (mFragment != null) { 
      // Detach the fragment, because another one is being attached 
      ft.detach(mFragment); 
     } 
    } 

    public void onTabReselected(Tab tab, FragmentTransaction ft) { 
     // User selected the already selected tab. Usually do nothing. 
    } 
} 
Các vấn đề liên quan