Parçaları kullanarak Android'deki her sekme için Ayrı Geri Yığını


158

Bir Android uygulamasında gezinme için sekmeler uygulamaya çalışıyorum. TabActivity ve ActivityGroup kullanımdan kaldırıldığından, bunun yerine Fragments kullanarak uygulamak istiyorum.

Her sekme için bir parça ayarlamak ve bir sekme tıklatıldığında parçaları değiştirmek nasıl biliyorum. Ancak her sekme için nasıl ayrı bir arka yığınım olabilir?

Bir örnek için A ve B Parçası Sekme 1'in altında ve C ve D Parçası 2'nin altındadır. Uygulama başlatıldığında A Parçası gösterilir ve Sekme 1 seçilir. Daha sonra A Parçası, F Parçası B ile değiştirilebilir. Sekme 2 seçildiğinde, Parçanın C görüntülenmesi gerekir. Sekme 1 seçilirse, B Parçası bir kez daha görüntülenmelidir. Bu noktada, A Parçasını göstermek için geri düğmesini kullanmak mümkün olmalıdır.

Ayrıca, cihaz döndürüldüğünde her sekmenin durumunun korunması önemlidir.

BR Martin

Yanıtlar:


23

Çerçeve şu anda bunu sizin için otomatik olarak yapmayacak. Her sekme için kendi arka yığınlarınızı oluşturmanız ve yönetmeniz gerekir.

Dürüst olmak gerekirse, bu gerçekten şüpheli bir şey gibi görünüyor. Bunun iyi bir kullanıcı arayüzü ile sonuçlanacağını düşünemiyorum - geri tuşu sekmeye bağlı olarak farklı şeyler yapacaksa, özellikle de arka tuşun üst kısmındaki tüm aktiviteyi kapatma normal davranışı varsa yığın ... kulağa kötü geliyor.

Bir web tarayıcısı kullanıcı arayüzü gibi bir şey oluşturmaya çalışıyorsanız, kullanıcıya özgü bir UX elde etmek için, içeriğe bağlı olarak çok sayıda ince davranış tweaks içerecektir, bu yüzden kesinlikle kendi arka yığınınızı yapmanız gerekir çerçevede bazı varsayılan uygulamalara güvenmek yerine yönetim. Bir örnek olarak, arka tuşun standart tarayıcıyla nasıl girip çıkabileceğiniz çeşitli yollarla nasıl etkileşim kurduğuna dikkat etmeye çalışın. (Tarayıcıdaki her bir "pencere" aslında bir sekmedir.)


7
Bunu yapma. Ve çerçeve pek işe yaramaz. Bu tür bir şey için size otomatik destek vermiyor, dediğim gibi, sırt davranışını dikkatlice kontrol etmeniz gereken çok özel durumlar dışında iyi bir kullanıcı deneyimi ile sonuçlanmayı hayal edemiyorum.
hackbod

9
Bu tür gezinme, daha sonra sekmeler var ve her sekmedeki sayfa hiyerarşisi iPhone uygulamaları için çok yaygındır (App Store ve iPod uygulamalarını kontrol edebilirsiniz). Kullanıcı deneyimlerini oldukça iyi buluyorum.
Dmitry Ryadnenko

13
Bu delilik. İPhone'un geri düğmesi bile yok. Sekmeleri parçalara uygulamak için çok basit bir kod gösteren API demoları vardır. Sorulan soru, her sekme için farklı arka yığınlara sahip olmakla ilgiliydi ve cevabım, çerçevenin bunu otomatik olarak sağlamadığı, çünkü semantik olarak geri düğmesinin yaptığı için büyük olasılıkla berbat bir kullanıcı deneyimi olacağı. Gerçi semantiği kolayca kendiniz uygulayabilirsiniz.
hackbod

4
Yine, iPhone'un bir geri düğmesi yok, bu yüzden semantik olarak Android gibi geri yığın davranışına sahip değil. Ayrıca, "etkinliklere sadık kalmak ve kendimi çok fazla zaman kazanmak daha iyi" burada bir anlam ifade etmiyor, çünkü etkinlikler kendi farklı sırt yığınlarıyla bir kullanıcı arayüzüne sekmeler koymanıza izin vermiyor; aslında faaliyetlerin geri yığın yönetimi Fragment çerçevesi tarafından sağlananlardan daha az esnektir.
hackbod

22
@hackbod Puanlarınızı takip etmeye çalışıyorum, ancak özel arka yığın davranışını uygulamada sorun yaşıyorum. Bunun tasarımına dahil olmanın, ne kadar kolay olabileceğine dair sağlam bir anlayışa sahip olacağınızın farkındayım. OP'nin kullanım durumu için bir demo uygulaması olması mümkün mü, gerçekten çok yaygın bir durum olduğundan, özellikle bu istekleri yapan müşteriler için iOS uygulamaları yazmak ve taşımak zorunda olanlar için .... ayrı yönetimi her FragmentActivity içindeki parça backstacks.
Richard Le Mesurier

138

Bu soruya çok geç kaldım. Ama bu konu benim için çok bilgilendirici ve yararlı olduğu için burada iki peni daha iyi postalamak düşündüm.

Böyle bir ekran akışına ihtiyacım vardı (Her sekmede 2 sekme ve 2 görünüm içeren minimalist bir tasarım),

tabA
    ->  ScreenA1, ScreenA2
tabB
    ->  ScreenB1, ScreenB2

Geçmişte aynı gereksinimleri yaşadım ve bunu kullanarak yaptım TabActivityGroup (ki o zaman da kullanımdan kaldırıldı) ve Etkinlikler kullanarak yaptı. Bu sefer Fragment kullanmak istedim.

İşte böyle yaptım.

1. Temel Parça Sınıfı oluşturun

public class BaseFragment extends Fragment {
    AppMainTabActivity mActivity;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mActivity = (AppMainTabActivity) this.getActivity();
    }

    public void onBackPressed(){
    }

    public void onActivityResult(int requestCode, int resultCode, Intent data){
    }
}

Uygulamanızdaki tüm parçalar bu Base sınıfını genişletebilir. Eğer özel parçalar kullanmak ListFragmentistiyorsanız, bunun için bir temel sınıf oluşturmanız gerekir. Sen kullanımı hakkında net olacak onBackPressed()veonActivityResult() sen tam olarak yazıyı okursanız ..

2. Projenin her yerine erişilebilen bazı Sekme tanımlayıcıları oluşturun

public class AppConstants{
    public static final String TAB_A  = "tab_a_identifier";
    public static final String TAB_B  = "tab_b_identifier";

    //Your other constants, if you have them..
}

burada açıklanacak bir şey yok ..

3. Tamam, Main Tab Activity- Lütfen koddaki yorumları gözden geçirin ..

public class AppMainFragmentActivity extends FragmentActivity{
    /* Your Tab host */
    private TabHost mTabHost;

    /* A HashMap of stacks, where we use tab identifier as keys..*/
    private HashMap<String, Stack<Fragment>> mStacks;

    /*Save current tabs identifier in this..*/
    private String mCurrentTab;

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.app_main_tab_fragment_layout);

        /*  
         *  Navigation stacks for each tab gets created.. 
         *  tab identifier is used as key to get respective stack for each tab
         */
        mStacks             =   new HashMap<String, Stack<Fragment>>();
        mStacks.put(AppConstants.TAB_A, new Stack<Fragment>());
        mStacks.put(AppConstants.TAB_B, new Stack<Fragment>());

        mTabHost                =   (TabHost)findViewById(android.R.id.tabhost);
        mTabHost.setOnTabChangedListener(listener);
        mTabHost.setup();

        initializeTabs();
    }


    private View createTabView(final int id) {
        View view = LayoutInflater.from(this).inflate(R.layout.tabs_icon, null);
        ImageView imageView =   (ImageView) view.findViewById(R.id.tab_icon);
        imageView.setImageDrawable(getResources().getDrawable(id));
        return view;
    }

    public void initializeTabs(){
        /* Setup your tab icons and content views.. Nothing special in this..*/
        TabHost.TabSpec spec    =   mTabHost.newTabSpec(AppConstants.TAB_A);
        mTabHost.setCurrentTab(-3);
        spec.setContent(new TabHost.TabContentFactory() {
            public View createTabContent(String tag) {
                return findViewById(R.id.realtabcontent);
            }
        });
        spec.setIndicator(createTabView(R.drawable.tab_home_state_btn));
        mTabHost.addTab(spec);


        spec                    =   mTabHost.newTabSpec(AppConstants.TAB_B);
        spec.setContent(new TabHost.TabContentFactory() {
            public View createTabContent(String tag) {
                return findViewById(R.id.realtabcontent);
            }
        });
        spec.setIndicator(createTabView(R.drawable.tab_status_state_btn));
        mTabHost.addTab(spec);
    }


    /*Comes here when user switch tab, or we do programmatically*/
    TabHost.OnTabChangeListener listener    =   new TabHost.OnTabChangeListener() {
      public void onTabChanged(String tabId) {
        /*Set current tab..*/
        mCurrentTab                     =   tabId;

        if(mStacks.get(tabId).size() == 0){
          /*
           *    First time this tab is selected. So add first fragment of that tab.
           *    Dont need animation, so that argument is false.
           *    We are adding a new fragment which is not present in stack. So add to stack is true.
           */
          if(tabId.equals(AppConstants.TAB_A)){
            pushFragments(tabId, new AppTabAFirstFragment(), false,true);
          }else if(tabId.equals(AppConstants.TAB_B)){
            pushFragments(tabId, new AppTabBFirstFragment(), false,true);
          }
        }else {
          /*
           *    We are switching tabs, and target tab is already has atleast one fragment. 
           *    No need of animation, no need of stack pushing. Just show the target fragment
           */
          pushFragments(tabId, mStacks.get(tabId).lastElement(), false,false);
        }
      }
    };


    /* Might be useful if we want to switch tab programmatically, from inside any of the fragment.*/
    public void setCurrentTab(int val){
          mTabHost.setCurrentTab(val);
    }


    /* 
     *      To add fragment to a tab. 
     *  tag             ->  Tab identifier
     *  fragment        ->  Fragment to show, in tab identified by tag
     *  shouldAnimate   ->  should animate transaction. false when we switch tabs, or adding first fragment to a tab
     *                      true when when we are pushing more fragment into navigation stack. 
     *  shouldAdd       ->  Should add to fragment navigation stack (mStacks.get(tag)). false when we are switching tabs (except for the first time)
     *                      true in all other cases.
     */
    public void pushFragments(String tag, Fragment fragment,boolean shouldAnimate, boolean shouldAdd){
      if(shouldAdd)
          mStacks.get(tag).push(fragment);
      FragmentManager   manager         =   getSupportFragmentManager();
      FragmentTransaction ft            =   manager.beginTransaction();
      if(shouldAnimate)
          ft.setCustomAnimations(R.anim.slide_in_right, R.anim.slide_out_left);
      ft.replace(R.id.realtabcontent, fragment);
      ft.commit();
    }


    public void popFragments(){
      /*    
       *    Select the second last fragment in current tab's stack.. 
       *    which will be shown after the fragment transaction given below 
       */
      Fragment fragment             =   mStacks.get(mCurrentTab).elementAt(mStacks.get(mCurrentTab).size() - 2);

      /*pop current fragment from stack.. */
      mStacks.get(mCurrentTab).pop();

      /* We have the target fragment in hand.. Just show it.. Show a standard navigation animation*/
      FragmentManager   manager         =   getSupportFragmentManager();
      FragmentTransaction ft            =   manager.beginTransaction();
      ft.setCustomAnimations(R.anim.slide_in_left, R.anim.slide_out_right);
      ft.replace(R.id.realtabcontent, fragment);
      ft.commit();
    }   


    @Override
    public void onBackPressed() {
        if(mStacks.get(mCurrentTab).size() == 1){
          // We are already showing first fragment of current tab, so when back pressed, we will finish this activity..
          finish();
          return;
        }

        /*  Each fragment represent a screen in application (at least in my requirement, just like an activity used to represent a screen). So if I want to do any particular action
         *  when back button is pressed, I can do that inside the fragment itself. For this I used AppBaseFragment, so that each fragment can override onBackPressed() or onActivityResult()
         *  kind of events, and activity can pass it to them. Make sure just do your non navigation (popping) logic in fragment, since popping of fragment is done here itself.
         */
        ((AppBaseFragment)mStacks.get(mCurrentTab).lastElement()).onBackPressed();

        /* Goto previous fragment in navigation stack of this tab */
            popFragments();
    }


    /*
     *   Imagine if you wanted to get an image selected using ImagePicker intent to the fragment. Ofcourse I could have created a public function
     *  in that fragment, and called it from the activity. But couldn't resist myself.
     */
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if(mStacks.get(mCurrentTab).size() == 0){
            return;
        }

        /*Now current fragment on screen gets onActivityResult callback..*/
        mStacks.get(mCurrentTab).lastElement().onActivityResult(requestCode, resultCode, data);
    }
}

4. app_main_tab_fragment_layout.xml (İlgilenen biri olması durumunda.)

<?xml version="1.0" encoding="utf-8"?>
<TabHost
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@android:id/tabhost"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">

    <LinearLayout
        android:orientation="vertical"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent">

        <FrameLayout
            android:id="@android:id/tabcontent"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:layout_weight="0"/>

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

        <TabWidget
            android:id="@android:id/tabs"
            android:orientation="horizontal"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_weight="0"/>

    </LinearLayout>
</TabHost>

5. AppTabAFirstFragment.java (Sekme A'daki ilk parça, tüm Sekmeler için simliar)

public class AppTabAFragment extends BaseFragment {
    private Button mGotoButton;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        View view       =   inflater.inflate(R.layout.fragment_one_layout, container, false);

        mGoToButton =   (Button) view.findViewById(R.id.goto_button);
        mGoToButton.setOnClickListener(listener);

        return view;
    }

    private OnClickListener listener        =   new View.OnClickListener(){
        @Override
        public void onClick(View v){
            /* Go to next fragment in navigation stack*/
            mActivity.pushFragments(AppConstants.TAB_A, new AppTabAFragment2(),true,true);
        }
    }
}

Bu en parlak ve doğru yol olmayabilir. Ama benim durumumda güzel çalıştı. Ayrıca sadece dikey modda bu gereksinim vardı. Bu kodu asla her iki yönlendirmeyi destekleyen bir projede kullanmak zorunda kalmadım. Orada ne tür zorluklarla karşılaştığımı söyleyemem ..

DÜZENLE :

Herkes tam bir proje isterse, github'a örnek bir proje gönderdim .


2
Her parça için veri depolamak, her birini yeniden oluşturmak, yığınları yeniden oluşturmak ... basit bir yönlendirme değişikliği için çok fazla iş.
Michael Eilers Smith

3
@omegatai sizinle tamamen aynı fikirde .. Android bizim için yığını yönetmediği için tüm sorun ortaya çıkıyor ( iOS yapıyor ve yönlendirme değişikliği veya çoklu parça içeren sekme bir esinti ) ve bu bizi bu Q / Bir iş parçacığı. Artık buna hiç iyi gitmiyor ..
Krishnabhadra

1
@Renjith Bunun nedeni, sekmeyi değiştirdiğinizde her seferinde parçanın yeniden oluşturulmasıdır. A sekmesinden B'ye geçiş yaptığımda, bir sekme bellekten kurtulur. Verilerinizi etkinliğe kaydedin ve her seferinde etkinliğin sunucudan almaya çalışmadan önce veri olup olmadığını kontrol edin.
Krishnabhadra

2
@Krishnabhadra Tamam, kulağa daha iyi geliyor. Yanılıyorsam düzeltmeme izin ver. Örneğinize göre, yalnızca bir etkinlik ve dolayısıyla bir paket vardır. BaseFragment'ta adaptör örnekleri oluşturun (projenizi referans alarak) ve verileri buraya kaydedin. Görünüm oluşturulduğunda bunları kullanın.
Renjith

1
Çalıştı. Çok teşekkürler. Tüm projeyi yüklemek iyi bir fikirdi! :-)
Vinay K

96

Son zamanlarda bir uygulama için tanımladığınız davranışın aynısını uygulamak zorunda kaldık. Uygulamanın ekranları ve genel akışı zaten tanımlanmıştı, bu yüzden buna bağlı kalmalıydık (bir iOS uygulama klonu ...). Neyse ki, ekran arka düğmelerinden kurtulmayı başardık :)

TabActivity, FragmentActivities (fragmanlar için destek kitaplığını kullanıyorduk) ve Fragment karışımını kullanarak çözümü hackledik. Geçmişe baktığımda, bunun en iyi mimari karar olmadığından eminim, ama işin işe yaramasını sağladık. Tekrar yapmak zorunda kalsaydım, muhtemelen daha fazla aktivite tabanlı bir çözüm yapmaya çalışabilirdim (fragman yok), ya da sekmeler için sadece bir Aktivitem var ve geri kalanların görünümler olmasına izin verdim (ki çok daha fazlasını buluyorum) genel faaliyetlerden daha fazla kullanılabilir).

Bu nedenle gereksinimler, her sekmede bazı sekmeler ve değiştirilebilir ekranlar olmasıydı:

tab 1
  screen 1 -> screen 2 -> screen 3
tab 2
  screen 4
tab 3
  screen 5 -> 6

vb...

Diyelim ki: kullanıcı sekme 1'de başlar, ekran 1'den ekran 2'ye ve sonra ekran 3'e gider, daha sonra sekme 3'e geçer ve ekran 4'ten 6'ya gider; sekme 1'e geri dönülürse, ekran 3'ü tekrar görmeli ve Geri düğmesine basarsa ekran 2'ye dönmelidir; Tekrar geri döndüğünde ekran 1'de; sekme 3'e geçin ve tekrar ekran 6'da.

Uygulamadaki ana faaliyet TabActivity'yi genişleten MainTabActivity'dir. Her sekme bir etkinlikle ilişkilidir, diyelim ActivityInTab1, 2 ve 3. Ve sonra her ekran bir parça olacak:

MainTabActivity
  ActivityInTab1
    Fragment1 -> Fragment2 -> Fragment3
  ActivityInTab2
    Fragment4
  ActivityInTab3
    Fragment5 -> Fragment6

Her ActivityInTab her seferinde sadece bir parça tutar ve bir parçayı diğeri için nasıl değiştireceğini bilir (hemen hemen bir ActvityGroup ile aynıdır). Harika olan şey, her sekme için ayrı sırt yığınlarını bu şekilde korumak oldukça kolaydır.

Her ActivityInTab için işlevsellik oldukça aynıydı: bir parçadan diğerine nasıl gidileceğini ve bir arka yığını nasıl koruyacağını biliyorsunuz, bu yüzden bunu bir temel sınıfa yerleştirdik. Basitçe ActivityInTab diyelim:

abstract class ActivityInTab extends FragmentActivity { // FragmentActivity is just Activity for the support library.

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

    /**
     * Navigates to a new fragment, which is added in the fragment container
     * view.
     * 
     * @param newFragment
     */
    protected void navigateTo(Fragment newFragment) {
        FragmentManager manager = getSupportFragmentManager();
        FragmentTransaction ft = manager.beginTransaction();

        ft.replace(R.id.content, newFragment);

        // Add this transaction to the back stack, so when the user presses back,
        // it rollbacks.
        ft.addToBackStack(null);
        ft.commit();
    }

}

Activity_in_tab.xml sadece şudur:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/content"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:isScrollContainer="true">
</RelativeLayout>

Gördüğünüz gibi, her sekmenin görünüm düzeni aynıydı. Çünkü her parçayı tutacak içerik adı verilen bir FrameLayout. Parçalar, her ekranın görüntüsüne sahip olanlardır.

Sadece bonus puanları için, kullanıcı Geri düğmesine bastığında ve geri dönecek daha fazla parça olmadığında onaylama iletişim kutusunu göstermek için küçük bir kod da ekledik:

// In ActivityInTab.java...
@Override
public void onBackPressed() {
    FragmentManager manager = getSupportFragmentManager();
    if (manager.getBackStackEntryCount() > 0) {
        // If there are back-stack entries, leave the FragmentActivity
        // implementation take care of them.
        super.onBackPressed();
    } else {
        // Otherwise, ask user if he wants to leave :)
        showExitDialog();
    }
}

Bu hemen hemen kurulum. Gördüğünüz gibi, her bir FragmentActivity (ya da sadece Android> 3'teki Etkinlik) kendi FragmentManager'ı ile tüm geri istifleme ile ilgileniyor.

ActivityInTab1 gibi bir etkinlik gerçekten basit olacak, sadece ilk parçasını (yani ekran) gösterecek:

public class ActivityInTab1 extends ActivityInTab {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        navigateTo(new Fragment1());
    }
}

Daha sonra, bir parçanın başka bir parçaya gitmesi gerekiyorsa, biraz kötü bir döküm yapması gerekir ... ama o kadar da kötü değil:

// In Fragment1.java for example...
// Need to navigate to Fragment2.
((ActivityIntab) getActivity()).navigateTo(new Fragment2());

Yani bu kadar. Bunun çok kanonik (ve çoğunlukla emin değilim çok iyi değil) bir çözüm olmadığından eminim, bu yüzden deneyimli Android geliştiricilerine bu işlevselliği elde etmek için daha iyi bir yaklaşımın ne olacağını sormak istiyorum. Android'de, beni açıklayan bir bağlantıya veya malzemeye yönlendirebilirseniz sevinirim . bu (sekmeler, sekmelerde iç içe ekranlar, vb) yaklaşmak için Android yolu. Yorumlarda bu cevabı koparmaktan çekinmeyin :)

Bu çözümün çok iyi olmadığının bir işareti olarak, son zamanlarda uygulamaya bazı navigasyon işlevleri eklemek zorunda kaldım. Kullanıcıyı bir sekmeden diğerine ve iç içe bir ekrana götürmesi gereken bazı tuhaf düğmeler. Bunu programlı olarak yapmak, kimin kimin problemleri olduğunu ve ne zaman uğraştığı parçaları ve aktiviteleri gerçekten somutlaştırıldıklarından ve başlattıklarından dolayı popoda bir acıydı. Bu ekranlar ve sekmeler gerçekten sadece Views gerçekten olsaydı çok daha kolay olurdu düşünüyorum.


Son olarak, yön değişikliklerinde hayatta kalmanız gerekiyorsa, parçalarınızın setArguments / getArguments kullanılarak oluşturulması önemlidir. Parçalarınızın yapıcılarında örnek değişkenleri ayarlarsanız vidalanırsınız. Ancak neyse ki düzeltmek gerçekten kolay: Yapımcıdaki setArguments içindeki her şeyi kaydedin ve sonra bunları kullanmak için onCreate'de getArguments ile bu şeyleri alın.


13
Harika cevap ama bence çok azı bunu görecek. Tam olarak aynı yolu seçtim (önceki cevaptaki konuşmadan görebileceğiniz gibi) ve tıpkı sizin gibi bundan memnun değilim. Bu API, büyük kullanım durumlarını kapsamadığından, Google'ın bu fragmanları gerçekten berbat ettiğini düşünüyorum. Karşılaşabileceğiniz başka bir sorun, parçayı başka bir parçanın içine yerleştirmek imkansızlığıdır.
Dmitry Ryadnenko

Yorum için teşekkürler boulder. Evet, fragmanlar API'sı hakkında daha fazla anlaşamadım. Zaten iç içe geçmiş parçalar sorununa girdim (bu yüzden "bir parçayı başka bir parçayla değiştir" yaklaşımı için gittik).
epidemya

1
Bunu TÜM aktiviteler aracılığıyla uyguladım. Elimde olanı beğenmedim ve Fragments'ı deneyeceğim. Bu, deneyiminizin tam tersidir! Her sekmedeki çocuk görünümlerinin yaşam döngüsünü idare etmek ve ayrıca kendi geri düğmenizi uygulamak için Etkinlikler ile birçok uygulama vardır. Ayrıca, tüm görünümlere bir referans tutamazsınız veya hafızayı patlatırsınız. Umarım Parçalar: 1) Parçaların yaşam döngüsünü hafızanın net bir şekilde ayrılmasıyla destekler ve 2) geri düğmesi işlevinin uygulanmasına yardımcı olur Artı, eğer bu işlem için parçaları kullanırsanız Tabletlerde çalıştırmak daha kolay olmaz mı?
gregm

Kullanıcı sekmeleri değiştirdiğinde ne olur? Parça backstack silinir mi? Backstack'ın nasıl kaldığından nasıl emin olunur?
gregm

1
@gregm 1 sekme <-> 1 aktivite yaptığım gibi giderseniz, sekmeler değiştirildiğinde her sekme için backstack, etkinlikler gerçekten canlı tutulduğundan kalır; sadece duraklatılır ve devam ettirilir. Sekmeler TabActivity'de değiştirildiğinde etkinliklerin yok edilmesini ve yeniden oluşturulmasını sağlamanın bir yolu olup olmadığını bilmiyorum. Ancak, faaliyetlerin içindeki parçaları önerdiğim gibi değiştirirseniz , yok edilir (ve backstack patladığında yeniden oluşturulur). Böylece, sekme başına istediğiniz zaman en fazla bir parçanız canlı olacaktır.
epidemian


6

Parçalara güçlü referanslar depolamak doğru bir yol değildir.

FragmentManager sağlar putFragment(Bundle, String, Fragment)ve saveFragmentInstanceState(Fragment).

Her ikisi de bir backstack uygulamak için yeterlidir.


Kullanılması putFragment, yerine Fragment değiştirme, Eskisini ayırmak ve yeni bir tane ekleyin. Çerçevenin backstack'e eklenen bir değiştirme işlemine yaptığı şey budur. putFragmentgeçerli Fragmanlar listesine bir indeks saklar ve bu Fragmanlar oryantasyon değişiklikleri sırasında çerçeve tarafından kaydedilir.

İkinci yol, kullanıldığında saveFragmentInstanceState, tüm parça durumunu bir Paket'e kaydederek ayırmak yerine gerçekten kaldırmanıza izin verir. Bu yaklaşımı kullanmak, istediğiniz zaman bir Parçayı açabileceğiniz için arka yığını işlemeyi kolaylaştırır.


Bu usecase için ikinci yöntemi kullandım:

SignInFragment ----> SignUpFragment ---> ChooseBTDeviceFragment
               \                          /
                \------------------------/

Kullanıcının üçüncü ekrana geri düğmesine basarak Kayıt ekranına dönmesini istemiyorum. Ayrıca aralarında (kullanarak onCreateAnimation) flip animasyonlar yapıyorum , bu yüzden hacky çözümleri işe yaramaz, en azından kullanıcı açıkça bir şey doğru olmadığını fark etmeden.

Bu, kullanıcının beklediği şeyi yapan özel bir backstack için geçerli bir kullanım durumudur ...

private static final String STATE_BACKSTACK = "SetupActivity.STATE_BACKSTACK";

private MyBackStack mBackStack;

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

    if (state == null) {
        mBackStack = new MyBackStack();

        FragmentManager fm = getSupportFragmentManager();
        FragmentTransaction tr = fm.beginTransaction();
        tr.add(R.id.act_base_frg_container, new SignInFragment());
        tr.commit();
    } else {
        mBackStack = state.getParcelable(STATE_BACKSTACK);
    }
}

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putParcelable(STATE_BACKSTACK, mBackStack);
}

private void showFragment(Fragment frg, boolean addOldToBackStack) {
    final FragmentManager fm = getSupportFragmentManager();
    final Fragment oldFrg = fm.findFragmentById(R.id.act_base_frg_container);

    FragmentTransaction tr = fm.beginTransaction();
    tr.replace(R.id.act_base_frg_container, frg);
    // This is async, the fragment will only be removed after this returns
    tr.commit();

    if (addOldToBackStack) {
        mBackStack.push(fm, oldFrg);
    }
}

@Override
public void onBackPressed() {
    MyBackStackEntry entry;
    if ((entry = mBackStack.pop()) != null) {
        Fragment frg = entry.recreate(this);

        FragmentManager fm = getSupportFragmentManager();
        FragmentTransaction tr = fm.beginTransaction();
        tr.replace(R.id.act_base_frg_container, frg);
        tr.commit();

        // Pop it now, like the framework implementation.
        fm.executePendingTransactions();
    } else {
        super.onBackPressed();
    }
}

public class MyBackStack implements Parcelable {

    private final List<MyBackStackEntry> mList;

    public MyBackStack() {
        mList = new ArrayList<MyBackStackEntry>(4);
    }

    public void push(FragmentManager fm, Fragment frg) {
        push(MyBackStackEntry.newEntry(fm, frg);
    }

    public void push(MyBackStackEntry entry) {
        if (entry == null) {
            throw new NullPointerException();
        }
        mList.add(entry);
    }

    public MyBackStackEntry pop() {
        int idx = mList.size() - 1;
        return (idx != -1) ? mList.remove(idx) : null;
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        final int len = mList.size();
        dest.writeInt(len);
        for (int i = 0; i < len; i++) {
            // MyBackStackEntry's class is final, theres no
            // need to use writeParcelable
            mList.get(i).writeToParcel(dest, flags);
        }
    }

    protected MyBackStack(Parcel in) {
        int len = in.readInt();
        List<MyBackStackEntry> list = new ArrayList<MyBackStackEntry>(len);
        for (int i = 0; i < len; i++) {
            list.add(MyBackStackEntry.CREATOR.createFromParcel(in));
        }
        mList = list;
    }

    public static final Parcelable.Creator<MyBackStack> CREATOR =
        new Parcelable.Creator<MyBackStack>() {

            @Override
            public MyBackStack createFromParcel(Parcel in) {
                return new MyBackStack(in);
            }

            @Override
            public MyBackStack[] newArray(int size) {
                return new MyBackStack[size];
            }
    };
}

public final class MyBackStackEntry implements Parcelable {

    public final String fname;
    public final Fragment.SavedState state;
    public final Bundle arguments;

    public MyBackStackEntry(String clazz, 
            Fragment.SavedState state,
            Bundle args) {
        this.fname = clazz;
        this.state = state;
        this.arguments = args;
    }

    public static MyBackStackEntry newEntry(FragmentManager fm, Fragment frg) {
        final Fragment.SavedState state = fm.saveFragmentInstanceState(frg);
        final String name = frg.getClass().getName();
        final Bundle args = frg.getArguments();
        return new MyBackStackEntry(name, state, args);
    }

    public Fragment recreate(Context ctx) {
        Fragment frg = Fragment.instantiate(ctx, fname);
        frg.setInitialSavedState(state);
        frg.setArguments(arguments);
        return frg;
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(fname);
        dest.writeBundle(arguments);

        if (state == null) {
            dest.writeInt(-1);
        } else if (state.getClass() == Fragment.SavedState.class) {
            dest.writeInt(0);
            state.writeToParcel(dest, flags);
        } else {
            dest.writeInt(1);
            dest.writeParcelable(state, flags);
        }
    }

    protected MyBackStackEntry(Parcel in) {
        final ClassLoader loader = getClass().getClassLoader();
        fname = in.readString();
        arguments = in.readBundle(loader);

        switch (in.readInt()) {
            case -1:
                state = null;
                break;
            case 0:
                state = Fragment.SavedState.CREATOR.createFromParcel(in);
                break;
            case 1:
                state = in.readParcelable(loader);
                break;
            default:
                throw new IllegalStateException();
        }
    }

    public static final Parcelable.Creator<MyBackStackEntry> CREATOR =
        new Parcelable.Creator<MyBackStackEntry>() {

            @Override
            public MyBackStackEntry createFromParcel(Parcel in) {
                return new MyBackStackEntry(in);
            }

            @Override
            public MyBackStackEntry[] newArray(int size) {
                return new MyBackStackEntry[size];
            }
    };
}

2

Yasal Uyarı:


Bu, oldukça standart Android şeyler gibi görünen benzer bir sorun türü için üzerinde çalıştığım ilgili bir çözümü yayınlamak için en iyi yer olduğunu hissediyorum. Sorunu herkes için çözmeyecek, ancak bazılarına yardımcı olabilir.


Parçalarınız arasındaki birincil fark yalnızca onları yedekleyen verilerse (yani, büyük düzen farklılıkları değil), parçayı gerçekten değiştirmeniz gerekmeyebilir, ancak yalnızca temel verileri değiştirip görünümü yenilemeniz gerekebilir.

İşte bu yaklaşım için olası bir örneğin açıklaması:

ListViews kullanan bir uygulama var. Listedeki her öğe, belirli sayıda çocuğu olan bir üst öğedir. Öğeye dokunduğunuzda, orijinal listeyle aynı ActionBar sekmesinde bu çocuklarla yeni bir listenin açılması gerekir. Bu iç içe listeler çok benzer bir düzene sahiptir (burada ve orada belki de bazı koşullu ayarlar), ancak veriler farklıdır.

Bu uygulama, ilk üst listenin altında birkaç yavru katmanı vardır ve bir kullanıcı birincisinin ötesinde belirli bir derinliğe erişmeye çalıştığında sunucudan veri alabiliriz veya olmayabilir. Liste bir veritabanı imlecinden oluşturulduğundan ve parçalar liste görünümünü liste öğeleriyle doldurmak için bir imleç yükleyici ve imleç bağdaştırıcısı kullandığından, bir tıklama kaydedildiğinde gerçekleşmesi gereken tek şey:

1) Listeye eklenen yeni öğe görünümleriyle ve yeni imleç tarafından döndürülen sütunlarla eşleşecek uygun 'to' ve 'from' alanlarıyla yeni bir bağdaştırıcı oluşturun.

2) Bu bağdaştırıcıyı ListView için yeni bağdaştırıcı olarak ayarlayın.

3) Tıklanan öğeye göre yeni bir URI oluşturun ve imleç yükleyiciyi yeni URI (ve projeksiyon) ile yeniden başlatın. Bu örnekte, URI, seçim arayüzünden UI'den geçirilen belirli sorgularla eşleştirilir.

4) Yeni veriler URI'den yüklendiğinde, bağdaştırıcı ile ilişkili imleci yeni imlece kaydırın, ardından liste yenilenir.

İşlemleri kullanmadığımız için bununla ilişkili bir backstack yoktur, bu nedenle hiyerarşiden geri çekilirken kendi hesabınızı oluşturmanız veya sorguları tersine oynamanız gerekecektir. Bunu denediğimde, sorguların hiyerarşinin en üstünde olana kadar onları tekrar oNBackPressed () 'de gerçekleştirebileceğim kadar hızlıydı, bu noktada çerçeve tekrar geri düğmesini devralır.

Kendinizi benzer bir durumda bulursanız, belgeleri okuduğunuzdan emin olun: http://developer.android.com/guide/topics/ui/layout/listview.html

http://developer.android.com/reference/android/support/v4/app/LoaderManager.LoaderCallbacks.html

Umarım bu birine yardımcı olur!


Herhangi birinin bunu yapması ve bir SectionIndexer (AlphabetIndexer gibi) kullanması durumunda, adaptörü değiştirdikten sonra hızlı kaydırma işleminizin çalışmadığını fark edebilirsiniz. Bir tür talihsiz bir hata, ancak yeni bir indeksleyiciyle bile adaptörün değiştirilmesi, FastScroll tarafından kullanılan bölümlerin listesini güncellemiyor. Bir geçici çözüm var, lütfen bakın: sorunun ve geçici çözümün
courtf

2

Tam olarak aynı sorunu yaşadım ve yığılmış sekme, geri ve yukarı navigasyonu kapsayan ve iyi test edilmiş ve belgelenmiş bir açık kaynak github projesi uyguladım:

https://github.com/SebastianBaltesObjectCode/PersistentFragmentTabs

Bu, gezinme sekmeleri ve yukarı ve geri gezinmenin parça değişimi ve işlenmesi için basit ve küçük bir çerçevedir. Her sekmenin kendi parça yığını vardır. ActionBarSherlock kullanır ve API seviye 8 ile uyumludur.


2

Android sadece 1 arka yığını işlediğinden bu karmaşık bir sorundur, ancak bu mümkündür. Tam olarak aradığınızı yapan Tab Stacker adlı bir kitaplık oluşturmak günlerimi aldı: her sekme için bir parça geçmişi. Açık kaynak kodludur ve tam olarak belgelenmiştir ve gradle ile kolayca dahil edilebilir. Kütüphaneyi github'da bulabilirsiniz: https://github.com/smart-fun/TabStacker

Davranışın ihtiyaçlarınıza uygun olduğunu görmek için örnek uygulamayı da indirebilirsiniz:

https://play.google.com/apps/testing/fr.arnaudguyon.tabstackerapp

Herhangi bir sorunuz varsa mail atmaktan çekinmeyin.


2

Birinin bakması ve ihtiyaçları için en iyisini seçmeye çalışması durumunda kendi çözümümü önermek istiyorum.

https://github.com/drusak/tabactivity

Kütüphaneyi yaratmanın amacı oldukça banal - iPhone gibi uygulayın.

Ana avantajları:

  • TabLayout ile android.support.design kütüphanesini kullanın;
  • her sekmenin FragmentManager kullanarak kendi yığını vardır (parça referanslarını kaydetmeden);
  • derin bağlantı desteği (belirli bir sekmeyi ve içindeki belirli parçanın seviyesini açmanız gerektiğinde);
  • sekmelerin durumlarını kaydetme / geri yükleme;
  • sekmelerdeki fragmanların uyarlanabilir yaşam döngüsü yöntemleri;
  • ihtiyaçlarınız için uygulanması oldukça kolaydır.

Teşekkür ederim, bu oldukça yardımcı oldu. BaseTabFragment.java BaseTabListFragment.java çoğaltılmış ve ListFragment uzatmak s s ListFragmentek olarak kullanmanız gerekir Fragment. Sonra her zaman bir BaseTabFragment beklendiği varsayılan kod çeşitli bölümlerini değiştirmek zorunda kaldı. Daha iyi bir yol var mı?
primehalo

Ne yazık ki, ListFragment hakkında düşünmedim. Teknik olarak doğru çözümdür, ancak TabFragment ve BaseObTistFragment örneği için ek denetimler gerektirecektir. Fragment'ı ListView ile içeride kullanmak için başka bir yaklaşım (uygulanan ListFragment ile tamamen aynıdır). Bunu düşüneceğim. Beni bu konuda gösterdiğin için teşekkürler!
kasurd

1

Basit bir çözüm:

Sekme / kök görünüm çağrısını her değiştirdiğinizde:

fragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);

BackStack temizlenir. Kök parçasını değiştirmeden önce bunu aramayı unutmayın.

Ve bununla parçalar ekleyin:

FragmentTransaction transaction = getFragmentManager().beginTransaction();
NewsDetailsFragment newsDetailsFragment = NewsDetailsFragment.newInstance(newsId);
transaction.add(R.id.content_frame, newsDetailsFragment).addToBackStack(null).commit();

Not .addToBackStack(null)ve transaction.addörneğin ile değiştirilebilir transaction.replace.


-1

Bu konu çok ilginç ve kullanışlıdır.
Açıklama ve kodunuz için teşekkürler Krishnabhadra, kodunuzu kullanıyorum ve biraz geliştirdim, yığınları, currentTab vb.
Gerçek bir 4.0.4 ve 2.3.6 cihazda test edildi, emülatörde test edilmedi

Kod "AppMainTabActivity.java" bu kısmını değiştirmek, geri kalanı aynı kalır. Belki de Krishnabhadra bunu koduna ekleyecektir.

Oluşturulan verileri kurtarma

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.app_main_tab_fragment_layout);

    /*  
     *  Navigation stacks for each tab gets created..
     *  tab identifier is used as key to get respective stack for each tab
     */

  //if we are recreating this activity...
    if (savedInstanceState!=null) {
         mStacks = (HashMap<String, Stack<Fragment>>) savedInstanceState.get("stack");
         mCurrentTab = savedInstanceState.getString("currentTab");
    }
    else {
    mStacks = new HashMap<String, Stack<Fragment>>();
    mStacks.put(AppConstants.TAB_A, new Stack<Fragment>());
    mStacks.put(AppConstants.TAB_B, new Stack<Fragment>());

    }

    mTabHost = (TabHost)findViewById(android.R.id.tabhost);
    mTabHost.setup();

    initializeTabs();

  //set the listener the last, to avoid overwrite mCurrentTab everytime we add a new Tab
    mTabHost.setOnTabChangedListener(listener);
}

Değişkenleri kaydedin ve Paketle:

 //Save variables while recreating
@Override
public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putSerializable("stack", mStacks);
    outState.putString("currentTab", mCurrentTab);
    //outState.putInt("tabHost",mTabHost);
}

Önceki bir CurrentTab varsa, bunu ayarlayın, aksi takdirde yeni bir Tab_A oluşturun:

public void initializeTabs(){
    /* Setup your tab icons and content views.. Nothing special in this..*/
    TabHost.TabSpec spec    =   mTabHost.newTabSpec(AppConstants.TAB_A);

    spec.setContent(new TabHost.TabContentFactory() {
        public View createTabContent(String tag) {
            return findViewById(R.id.realtabcontent);
        }
    });
    spec.setIndicator(createTabView(R.drawable.tab_a_state_btn));
    mTabHost.addTab(spec);


    spec                    =   mTabHost.newTabSpec(AppConstants.TAB_B);
    spec.setContent(new TabHost.TabContentFactory() {
        public View createTabContent(String tag) {
            return findViewById(R.id.realtabcontent);
        }
    });
    spec.setIndicator(createTabView(R.drawable.tab_b_state_btn));
    mTabHost.addTab(spec);

//if we have non default Tab as current, change it
    if (mCurrentTab!=null) {
        mTabHost.setCurrentTabByTag(mCurrentTab);
    } else {
        mCurrentTab=AppConstants.TAB_A;
        pushFragments(AppConstants.TAB_A, new AppTabAFirstFragment(), false,true);
    }
}

Umarım bu diğer insanlara yardımcı olur.


Bu yanlış. OnCreate bir Paket ile çağrıldığında, bu Parçalar ekranda gösterilecek parçalarla aynı olmayacak ve setRetainInstance'ı kullanmadığınız sürece eskilerini sızdırıyorsunuz. ActivityManager, Etkinliğinizi "kaydederse", bir Parça Serileştirilebilir veya Parcelable olmadığından, kullanıcı Etkinliğinize geri döndüğünde çökecektir.
sergio91pt

-1

HashMap dayalı backstack kullanmamanızı tavsiye ederim> "faaliyetleri tutmayın" modunda hataların bir sürü vardır. Parça yığınında derinlemesine olmanız durumunda durumu doğru şekilde geri yüklemez. Ayrıca, iç içe harita parçasında da kırılacaktır (istisna ile: Kimlik için parça bulunamadı). Coz HashMap> arkaplandan sonra \ ön plan uygulaması boş olacak

Ben parçanın backstack ile çalışmak için yukarıdaki kodu optimize

Altta TabView

Ana faaliyet sınıfı

import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.content.Intent;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.Window;
import android.widget.ImageView;
import android.widget.TabHost;
import android.widget.TextView;

import com.strikersoft.nida.R;
import com.strikersoft.nida.abstractActivity.BaseActivity;
import com.strikersoft.nida.screens.tags.mapTab.MapContainerFragment;
import com.strikersoft.nida.screens.tags.searchTab.SearchFragment;
import com.strikersoft.nida.screens.tags.settingsTab.SettingsFragment;

public class TagsActivity extends BaseActivity {
    public static final String M_CURRENT_TAB = "M_CURRENT_TAB";
    private TabHost mTabHost;
    private String mCurrentTab;

    public static final String TAB_TAGS = "TAB_TAGS";
    public static final String TAB_MAP = "TAB_MAP";
    public static final String TAB_SETTINGS = "TAB_SETTINGS";

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getWindow().requestFeature(Window.FEATURE_ACTION_BAR);
        getActionBar().hide();
        setContentView(R.layout.tags_activity);

        mTabHost = (TabHost) findViewById(android.R.id.tabhost);

        mTabHost.setup();

        if (savedInstanceState != null) {
            mCurrentTab = savedInstanceState.getString(M_CURRENT_TAB);
            initializeTabs();
            mTabHost.setCurrentTabByTag(mCurrentTab);
            /*
            when resume state it's important to set listener after initializeTabs
            */
            mTabHost.setOnTabChangedListener(listener);
        } else {
            mTabHost.setOnTabChangedListener(listener);
            initializeTabs();
        }
    }

    private View createTabView(final int id, final String text) {
        View view = LayoutInflater.from(this).inflate(R.layout.tabs_icon, null);
        ImageView imageView = (ImageView) view.findViewById(R.id.tab_icon);
        imageView.setImageDrawable(getResources().getDrawable(id));
        TextView textView = (TextView) view.findViewById(R.id.tab_text);
        textView.setText(text);
        return view;
    }

    /*
    create 3 tabs with name and image
    and add it to TabHost
     */
    public void initializeTabs() {

        TabHost.TabSpec spec;

        spec = mTabHost.newTabSpec(TAB_TAGS);
        spec.setContent(new TabHost.TabContentFactory() {
            public View createTabContent(String tag) {
                return findViewById(R.id.realtabcontent);
            }
        });
        spec.setIndicator(createTabView(R.drawable.tab_tag_drawable, getString(R.string.tab_tags)));
        mTabHost.addTab(spec);

        spec = mTabHost.newTabSpec(TAB_MAP);
        spec.setContent(new TabHost.TabContentFactory() {
            public View createTabContent(String tag) {
                return findViewById(R.id.realtabcontent);
            }
        });
        spec.setIndicator(createTabView(R.drawable.tab_map_drawable, getString(R.string.tab_map)));
        mTabHost.addTab(spec);


        spec = mTabHost.newTabSpec(TAB_SETTINGS);
        spec.setContent(new TabHost.TabContentFactory() {
            public View createTabContent(String tag) {
                return findViewById(R.id.realtabcontent);
            }
        });
        spec.setIndicator(createTabView(R.drawable.tab_settings_drawable, getString(R.string.tab_settings)));
        mTabHost.addTab(spec);

    }

    /*
    first time listener will be trigered immediatelly after first: mTabHost.addTab(spec);
    for set correct Tab in setmTabHost.setCurrentTabByTag ignore first call of listener
    */
    TabHost.OnTabChangeListener listener = new TabHost.OnTabChangeListener() {
        public void onTabChanged(String tabId) {

            mCurrentTab = tabId;

            if (tabId.equals(TAB_TAGS)) {
                pushFragments(SearchFragment.getInstance(), false,
                        false, null);
            } else if (tabId.equals(TAB_MAP)) {
                pushFragments(MapContainerFragment.getInstance(), false,
                        false, null);
            } else if (tabId.equals(TAB_SETTINGS)) {
                pushFragments(SettingsFragment.getInstance(), false,
                        false, null);
            }

        }
    };

/*
Example of starting nested fragment from another fragment:

Fragment newFragment = ManagerTagFragment.newInstance(tag.getMac());
                TagsActivity tAct = (TagsActivity)getActivity();
                tAct.pushFragments(newFragment, true, true, null);
 */
    public void pushFragments(Fragment fragment,
                              boolean shouldAnimate, boolean shouldAdd, String tag) {
        FragmentManager manager = getFragmentManager();
        FragmentTransaction ft = manager.beginTransaction();
        if (shouldAnimate) {
            ft.setCustomAnimations(R.animator.fragment_slide_left_enter,
                    R.animator.fragment_slide_left_exit,
                    R.animator.fragment_slide_right_enter,
                    R.animator.fragment_slide_right_exit);
        }
        ft.replace(R.id.realtabcontent, fragment, tag);

        if (shouldAdd) {
            /*
            here you can create named backstack for realize another logic.
            ft.addToBackStack("name of your backstack");
             */
            ft.addToBackStack(null);
        } else {
            /*
            and remove named backstack:
            manager.popBackStack("name of your backstack", FragmentManager.POP_BACK_STACK_INCLUSIVE);
            or remove whole:
            manager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
             */
            manager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
        }
        ft.commit();
    }

    /*
    If you want to start this activity from another
     */
    public static void startUrself(Activity context) {
        Intent newActivity = new Intent(context, TagsActivity.class);
        newActivity.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        context.startActivity(newActivity);
        context.finish();
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        outState.putString(M_CURRENT_TAB, mCurrentTab);
        super.onSaveInstanceState(outState);
    }

    @Override
    public void onBackPressed(){
        super.onBackPressed();
    }
}

tags_activity.xml

<

?xml version="1.0" encoding="utf-8"?>
<TabHost
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@android:id/tabhost"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <FrameLayout
            android:id="@android:id/tabcontent"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:layout_weight="0"/>
        <FrameLayout
            android:id="@+android:id/realtabcontent"
            android:background="@drawable/bg_main_app_gradient"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"/>
        <TabWidget
            android:id="@android:id/tabs"
            android:background="#EAE7E1"
            android:orientation="horizontal"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="0"/>
    </LinearLayout>
</TabHost>

tags_icon.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/tabsLayout"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="@drawable/bg_tab_gradient"
    android:gravity="center"
    android:orientation="vertical"
    tools:ignore="contentDescription" >

    <ImageView
        android:id="@+id/tab_icon"
        android:layout_marginTop="4dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
    <TextView 
        android:id="@+id/tab_text"
        android:layout_marginBottom="3dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="@color/tab_text_color"/>

</LinearLayout>

resim açıklamasını buraya girin

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.