9

So I am using Tabs and seeking for this kind of navigation:

tab1 -- > inside 1 -- > inside2

tab2 -- > inside 3 -- > inside 4

tab3 -- > inside 5

inside I mean that it should open a new layout and class.

My project main class is this:

public class TabsFragmentActivity extends SherlockFragmentActivity implements
        TabHost.OnTabChangeListener {

    private TabHost mTabHost;
    private HashMap<String, TabInfo> mapTabInfo = new HashMap<String, TabInfo>();
    private TabInfo mLastTab = null;
    private static Context mContext;

    private class TabInfo {
        private String tag;
        private Class clss;
        private Bundle args;
        private Fragment fragment;

        TabInfo(String tag, Class clazz, Bundle args) {
            this.tag = tag;
            this.clss = clazz;
            this.args = args;
        }
    }

    class TabFactory implements TabContentFactory {

        private final Context mContext;

        public TabFactory(Context context) {
            mContext = context;
        }

        public View createTabContent(String tag) {
            View v = new View(mContext);
            v.setMinimumWidth(0);
            v.setMinimumHeight(0);
            return v;
        }
    }

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

        // Step 1: Inflate layout
        setContentView(R.layout.tabs_fragment_activity);

        mContext = this;

        // Step 2: Setup TabHost
        initialiseTabHost(savedInstanceState);

        if (savedInstanceState != null) {
            mTabHost.setCurrentTabByTag(savedInstanceState.getString("tab")); 
        }

        addNavaigationBar();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        //Add Action item with title
            menu.add("some")
            .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM | MenuItem.SHOW_AS_ACTION_WITH_TEXT);

        return super.onCreateOptionsMenu(menu);
    }

    public void addNavaigationBar() {
        // Create Action Bar sherlock
        ActionBar navigation_bar = getSupportActionBar();

        // Setting standart navigation bar view
        navigation_bar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);

        navigation_bar.setDisplayShowTitleEnabled(true);
        navigation_bar.setTitle("Test");

        // Override Action items to navigation bar. Calls onCreateOptionsMenu
        // invalidateOptionsMenu();
    }

    protected void onSaveInstanceState(Bundle outState) {
        outState.putString("tab", mTabHost.getCurrentTabTag()); // save the tab
                                                                // selected
        super.onSaveInstanceState(outState);
    }

    /**
     * Step 2: Setup TabHost
     */
    private void initialiseTabHost(Bundle args) {
        mTabHost = (TabHost) findViewById(android.R.id.tabhost);

        mTabHost.setup();

        TabInfo tabInfo = null;

        TabsFragmentActivity.addTab(this, this.mTabHost, this.mTabHost
                .newTabSpec("Tab1").setIndicator("Tab 1"),
                (tabInfo = new TabInfo("Tab1", Tab1Fragment.class, args)));

        this.mapTabInfo.put(tabInfo.tag, tabInfo);


        TabsFragmentActivity.addTab(this, this.mTabHost, this.mTabHost
                .newTabSpec("Tab2").setIndicator("Tab 2"),
                (tabInfo = new TabInfo("Tab2", Tab2Fragment.class, args)));

        this.mapTabInfo.put(tabInfo.tag, tabInfo);

        TabsFragmentActivity.addTab(this, this.mTabHost, this.mTabHost
                .newTabSpec("Tab3").setIndicator("Tab 3"),
                (tabInfo = new TabInfo("Tab3", Tab3Fragment.class, args)));

        this.mapTabInfo.put(tabInfo.tag, tabInfo);
        // Default to first tab
        this.onTabChanged("Tab1");
        //
        mTabHost.setOnTabChangedListener(this);
    }

    private static void addTab(TabsFragmentActivity activity, TabHost tabHost,
            TabHost.TabSpec tabSpec, TabInfo tabInfo) {
        // Attach a Tab view factory to the spec
        tabSpec.setContent(activity.new TabFactory(activity));

        String tag = tabSpec.getTag();
        //getTabWidget()

        View view = prepareTabView(activity, R.id.tab_bar_icon);
        tabSpec.setIndicator(view);

        // Check to see if we already have a fragment for this tab, probably
        // from a previously saved state. If so, deactivate it, because our
        // initial state is that a tab isn't shown.
        tabInfo.fragment = activity.getSupportFragmentManager().findFragmentByTag(tag);

        if (tabInfo.fragment != null && !tabInfo.fragment.isDetached()) {

            FragmentTransaction ft = activity.getSupportFragmentManager().beginTransaction();

            ft.detach(tabInfo.fragment);

            ft.commit();

            activity.getSupportFragmentManager().executePendingTransactions();
        }
        tabHost.addTab(tabSpec);
    }

    private static View prepareTabView(Context context, int drawable){
        //inflate(R.layout.tab_indicator, android.R.id.tabs, false)
        View tabIndicator = LayoutInflater.from(context).inflate(R.layout.tab_indicator, null);
        ImageView icon = (ImageView) tabIndicator.findViewById(R.id.tab_bar_icon);
        icon.setImageResource(R.drawable.ic_launcher);
        return tabIndicator;

    }

    public void onTabChanged(String tag) {
        TabInfo newTab = this.mapTabInfo.get(tag);

        if (mLastTab != newTab) {
            FragmentTransaction ft = this.getSupportFragmentManager()
                    .beginTransaction();
            if (mLastTab != null) {
                if (mLastTab.fragment != null) {
                    ft.detach(mLastTab.fragment);
                }
            }
            if (newTab != null) {
                if (newTab.fragment == null) {
                    newTab.fragment = Fragment.instantiate(this,
                            newTab.clss.getName(), newTab.args);
                    ft.add(R.id.realtabcontent, newTab.fragment, newTab.tag);
                } else {
                    ft.attach(newTab.fragment);
                }
            }

            mLastTab = newTab;
            ft.commit();
            this.getSupportFragmentManager().executePendingTransactions();
        }
    }

}

This create 3 Tabs with there content. Here is how look Tab1 fragment class (other look similar) :

public class Tab1Fragment extends SherlockFragment {
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        if (container == null) {
            // We have different layouts, and in one of them this
            // fragment's containing frame doesn't exist.  The fragment
            // may still be created from its saved state, but there is
            // no reason to try to create its view hierarchy because it
            // won't be displayed.  Note this is not needed -- we could
            // just run the code below, where we would create and return
            // the view hierarchy; it would just never be used.
            return null;
        }
        LinearLayout theLayout = (LinearLayout)inflater.inflate(R.layout.tab_frag1_layout, container, false);
        // Register for the Button.OnClick event
        Button b = (Button)theLayout.findViewById(R.id.frag1_button);
        b.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

**//Here i want open a new window but don't change pressed tab and when press back it should go back at this window**
            }
        });
        return theLayout;
    }
}

I write on button press what I want.

I can't open a new fragment or activity inside this fragment ?

How I should do navigation inside fragments ?

Simulant
  • 19,190
  • 8
  • 63
  • 98
Streetboy
  • 4,351
  • 12
  • 56
  • 101

1 Answers1

13

I like to let my host Activity handle all transitions, so what I do in my fragments is create an interface to handle navigation. For example, you could add this interface to your Tab1Fragment:

public interface Callback {
    public void onButtonBClicked();
}

Then in your TabsFragmentActivity, implement Tab1Fragment.Callback, which will require you to implement onButtonBClicked(). Here's an example of how you could implement this method:

@Override
public void onButtonBClicked() {

    Fragment anotherFragment = Fragment.instantiate(this, AnotherFragment.class.getName());
    FragmentTransaction ft = getFragmentManager().beginTransaction();
    ft.add(R.id.realtabcontent, anotherFragment);
    ft.addToBackStack(null);
    ft.commit();
}

Almost done. Next, you need to do is get a reference to this callback in your fragment. This is typically achieved in the onAttached() method. For example:

@Override
public void onAttach(Activity activity) {

    super.onAttach(activity);
    try {
        mCallback = (Callback) activity;
    }
    catch (ClassCastException e) {
        throw new ClassCastException(activity.toString() + " must implement " + Callback.class.getName());
    }
}

And, finally, in your OnClickListener, call mCallback.onButtonBClicked().

Jason Robinson
  • 31,005
  • 19
  • 77
  • 131
  • This is good example of using callbacks. But what is the difference if i call in my Fragment1 OnClickListiner this: ((TabsFragmentActivity) getActivity()).onButtonBClicked(); This also would call onButtonBClicked(). But what about handling stacks of every tabs ? – Streetboy Apr 18 '12 at 05:38
  • It makes no difference except it's cleaner they way I've done it since you would only have to cast it once (if you had a beefier interface with multiple calls). The implementation I have for `onButtonClicked()` adds the fragment to the back stack so pressing back will show the previous fragment. – Jason Robinson Apr 18 '12 at 15:27
  • Ok then how to switch tabs ? I mean i go tab1 into deep stack, then switch to tab2 and there go into deep and then switch again to tab1 and press back button and i want that it would go back just in tab1 – Streetboy Apr 19 '12 at 06:10
  • Ahh I see. So you want each tab to have its own independent back stack, and when switching tabs, you "switch" back stacks? – Jason Robinson Apr 19 '12 at 06:26
  • Yes i seeking solution for this – Streetboy Apr 19 '12 at 07:12
  • Uploaded my answer with the solution I use for your problem – Jason Robinson Apr 19 '12 at 15:39
  • @JasonRobinson this piece of code would actually destroy and playback the back stack on each tab change ? maybe I misunderstood something. do you know a way of actually maintaining more than one back stack ? – Rafael Nobre Jun 19 '12 at 17:24
  • @nobre that's what this does. when a tab is unselected, the backstack is saved. when it's reselected, its backstack is restored. – Jason Robinson Jun 19 '12 at 23:47
  • can i able to go from "Tab 1 -->inside 2" to "Tab 2 -->inside 4"?? i am also using the fragment for tab host but opening in another fragment in another tab is opening in the same tab..please help me – Akhil Dec 14 '12 at 05:33
  • @JasonRobinson Could you please be more specific about the backstack saving and restoring. I cannot see how this implementation achieves the goal defined by nobre. If you show Fragment1 on selecting Tab1, Fragment2 on selecting a list item showed by Fragment1, then selecting Tab2 and showing Fragment3, then how will this code show Fragment2 when selecting Tab1 again? I must be missing something. Could you please help? – Csaba Szugyiczki Apr 12 '14 at 22:39
  • @CsabaSzugyiczki Look at the history for my answer. I had to remove part of the answer due to the code being copyrighted. – Jason Robinson Apr 14 '14 at 14:25
  • @JasonRobinson Ohh, i have missed it, thanks for pointing it out. I have tried the solution, it does exactly what i needed, thanks. But the scroll position of lists is retained only until i have not switched to another tab. When i switch to another tab, then switch back, the list is scrolled to the top, instead of where it originally was. Do you know any way to make it work, other than saving and restoring the scroll position in savedInstanceState? Did you encounter problems like this when using this code? – Csaba Szugyiczki Apr 17 '14 at 14:55
  • @CsabaSzugyiczki I don't believe I encountered that. You can probably make use of `onSavedInstanceState` for saving scroll positions. If not, I'd recommend creating a new question. – Jason Robinson Apr 17 '14 at 15:58