How to refresh the content of a tab in an actionBar (Android)? - android

Here the summary of my 'project' on Android 4.x and the problem I can't solve:
i have an actionBar with 2 tabs (i know it is deprecated):
...
Fragment fragmentTab1 = new FragmentTab1();
Fragment fragmentTab2 = new FragmentTab2();
Fragment fragmentTab3 = new FragmentTab3();
ActionBar.Tab tab1, tab2;
ActionBar actionBar = getActionBar();
tab1 = actionBar.newTab().setText("tab1");
tab2 = actionBar.newTab().setText("tab2");
tab1.setTabListener(new MyTabListener(fragmentTab1));
tab2.setTabListener(new MyTabListener(fragmentTab2));
actionBar.addTab(tab1);
actionBar.addTab(tab2);
...
I would like to change the fragment of a tab and refresh what the user sees, doing that in a method (not only when the tab is (re)selected).
It sounds simple, but impossible to find a easy way to do that.
After many fails, this is what I try (i remove the tab and add it with a new fragment):
void myMethod () {
getActionBar().removeTabAt(0);
tab1 = actionBar.newTab().setText("tab3");
tab1.setTabListener(new MyTabListener(fragmentTab3));
getActionBar().addTab(tab1, 0);
...
And then this awful thing to desesperatly try to update what is seen...
...
getActionBar().setSelectedNavigationItem(tab1.getPosition()+1);
getActionBar().setSelectedNavigationItem(tab1.getPosition());
}
Everything goes as wanted, except that the reselected tab1 is empty... :(
If I select with my finger tab2 then tab1 (so manually in place of programmaticaly), then tab1 appears as wanted...
So, how can I change programmaticaly the content of a tab and refresh what is seen in an actionBar?
Already a big thanks for your time!

You should depend on TabListener methods, don't do getActionBar().setSelectedNavigationItem(). There are good code samples on the Internet.
1) Google webpage # Creating Swipe Views with Tabs. Code snippet:
ActionBar.TabListener tabListener = new ActionBar.TabListener() {
public void onTabSelected(ActionBar.Tab tab, FragmentTransaction ft) {
// show the given tab
}
public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction ft) {
// hide the given tab
}
public void onTabReselected(ActionBar.Tab tab, FragmentTransaction ft) {
// probably ignore this event
}
};
Notes: Refresh your data in onTabSelected(). You may check for tags or text in tab parameter.
2) Another good link for you # Android Fragment Tabs Example. Code snippet from the link:
public class TabListener implements ActionBar.TabListener {
private Fragment fragment;
public TabListener(Fragment fragment) {
this.fragment = fragment;
}
// When a tab is tapped, the FragmentTransaction replaces
// the content of our main layout with the specified fragment;
// that's why we declared an id for the main layout.
#Override
public void onTabSelected(Tab tab, FragmentTransaction ft) {
ft.replace(R.id.activity_main, fragment);
}
Notes: Again, refresh your data in onTabSelected(). I have to warn you that TabListener is deprecated in the new Lollipop API.
EDIT:
Sample code to set Tabs to the correct Fragments.
// Setting tab listeners.
bmwTab.setTabListener(new TabListener(bmwFragmentTab));
toyotaTab.setTabListener(new TabListener(toyotaFragmentTab));
fordTab.setTabListener(new TabListener(fordFragmentTab));
Good luck and have fun...

Related

Reset the fragment backstack when switching tabs

Now im into my first question, after not finding an answer in the internet. Usually this should be a really easy thing, but it took me hours and hours so please excuse me if its a beginner question. Also i want to make sure im going the right way before spending more days just to see that in the end it just not works as i want, the way i implement it atm.
The problem:
My App has 3 Tabs with 3 fragments as content, one for every tab. I use Tabs because all users i asked want Tabs and not a Navigation Drawer and i listen to users, not to google (sorry). Also the 3 tabs have very different "roles" (Language Guide, Currency convertor, and own Notebook for own phrases etc) and it makes sense to split it this way (imo). If someone has other ideas, go ahead, im open :-)
The tab-navigation works as intended so far. But now i want to build the further navigation for the single Tabs an im not sure anymore wich way to go... Theres just so many way for a simple thing like this...
The first Tab has some imageButtons to further navigate into this section (See Screenshot 1), to choose a category in the language section. Of course this means the tabs should be stay in place to be able to easy switch to the Converter i.E, just the fragment from tab 1 should get switched to the new fragment (see Screenshot 2), and so on. The Buttons that you see in the below screenshot have clicklisteners that change current fragment to the new one (like "Daily")
For this i open another fragment and add it to the backstack. Maybe you already can guess what happens now. If i press the backbutton, i come back to the first fragment of course. Problems start when i switch tabs when im inside a fragment opened on tab 1. The added fragment is still in backstack what means if i press back button on Tab 2 or 3, the displayed content gets mixes up somehow with the other fragment ones. See Screenshot 3
Do i make an error somehere or can i simply tell the backstack to "reset" when switching tabs so this problem doesnt occur?
Thank you for info and sorry for language and spelling errors, english isnt my main language.
Tablistener Class to switch the tab fragments:
public class MyTabListener implements ActionBar.TabListener {
Fragment fragment;
public MyTabListener(Fragment fragment) {
this.fragment = fragment;
}
public void onTabSelected(ActionBar.Tab tab, FragmentTransaction ft) {
ft.replace(R.id.fragment_container, fragment);
}
public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction ft) {
ft.remove(fragment);
}
public void onTabReselected(ActionBar.Tab tab, FragmentTransaction ft) {
// nothing done here
}
}
The Fragments class from first fragment look like this (not full code):
public static class FragmentTab1 extends Fragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
final View view = inflater.inflate(R.layout.tab, container, false);
ImageButton catBtn_Daily = (ImageButton) view.findViewById(R.id.catBtn_Daily);
ImageButton catBtn_OnTheRoad = (ImageButton) view.findViewById(R.id.catBtn_OnTheRoad);
ImageButton catBtn_Shopping = (ImageButton) view.findViewById(R.id.catBtn_Shopping);
ImageButton catBtn_Restaurant = (ImageButton) view.findViewById(R.id.catBtn_Restaurant);
ImageButton catBtn_Romance = (ImageButton) view.findViewById(R.id.catBtn_Romance);
ImageButton catBtn_Emergency = (ImageButton) view.findViewById(R.id.catBtn_Emergency);
// attach an OnClickListener
catBtn_Daily.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// Create new fragment and transaction
Fragment newFragment = new FragmentTab4();
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.replace(R.id.fragment_container, newFragment);
transaction.addToBackStack(null);
transaction.setTransition(4097);
transaction.commit();
}
});
Mainactivity:
public class MainActivity extends Activity {
ActionBar.Tab tab1, tab2, tab3;
Fragment fragmentTab1 = new FragmentTab1();
Fragment fragmentTab2 = new FragmentTab2();
Fragment fragmentTab3 = new FragmentTab3();
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_tab_test);
ActionBar actionBar = getActionBar();
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
tab1 = actionBar.newTab().setText("Home");
tab2 = actionBar.newTab().setText("Calculator");
tab3 = actionBar.newTab().setText("Notebook");
tab1.setTabListener(new MyTabListener(fragmentTab1));
tab2.setTabListener(new MyTabListener(fragmentTab2));
tab3.setTabListener(new MyTabListener(fragmentTab3));
actionBar.addTab(tab1);
actionBar.addTab(tab2);
actionBar.addTab(tab3);
Cannot post pics yet, rep too low....
Please see here (third pic is called Untitled-3.png)
Screen 1: https://www.dropbox.com/s/3av3pf6a17p2w42/Untitled-1.png
Screen 2: https://www.dropbox.com/s/3av3pf6a17p2w42/Untitled-2.png
Screen 3: https://www.dropbox.com/s/3av3pf6a17p2w42/Untitled-3.png
you might be better off not using the fragment back stack at all. you can maintain your own stack and override onBackPressed() to provide the appropriate behavior.
#Override protected void onBackPressed()
{
if(myStack.isEmpty())
super.onBackPressed(); // default handling finishes the activity
else
{
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.replace(R.id.fragment_container, myStack.pop());
transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_CLOSE);
transaction.commit();
}
}
Actually it sounds like you might want to consider a completely different approach. If I am understanding correctly you might want to consider using a ViewPager with a FragmentPagerAdapter (see the google documentation code sample here: FragmentPagerAdapter).
Essentially the viewpager presents the fragments under your tabs, the adapter provides the list of fragments to show and you select the current "page" by calling setCurrentItem on the viewpager (when the user clicks a tab). Additionally users will be able to swipe between pages (you would update the selected tab by listening to the pager events see google documentation code sample here: ViewPager).
This approach avoids the whole backstack issue and sounds more like what you are after.
Hope it helps.
You can try following:
public void onTabSelected(YourTab tab) {
Fragment fragment = new YourTabFragment();
replaceRootFragment(fragment);
}
public void replaceRootFragment(Fragment fragment) {
if (getSupportFragmentManager().getBackStackEntryCount() != 0) {
int id = getSupportFragmentManager().getBackStackEntryAt(0).getId();
try {
getSupportFragmentManager().popBackStackImmediate(id, FragmentManager.POP_BACK_STACK_INCLUSIVE);
} catch (IllegalStateException e) {
return;
}
}
getSupportFragmentManager().beginTransaction()
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
.replace(R.id.container, fragment)
.commit();
}
Call onTabSelected when you click on the tab.

How to make actionbarsherlock tabs open listviews instead of fragments

I want to create a feed like the one in the facebook app, and I want to have actionbarsherlock navigation tabs that will be used to filter the feed - a tab for people, a tab for places and a tab for items.
Right now I've set it up so that each tab opens up a fragment. How can I make them open up a list view instead?
ActionBar.Tab itemsFeedTab = actionBar.newTab();
ActionBar.Tab peopleFeedTab = actionBar.newTab();
ActionBar.Tab placesFeedTab = actionBar.newTab();
Fragment itemsFeedFragment = new FeedItems();
Fragment peopleFeedFragment = new FeedPeople();
Fragment placesFeedFragment = new FeedPlaces();
itemsFeedTab.setTabListener(new MyTabsListener(itemsFeedFragment));
peopleFeedTab.setTabListener(new MyTabsListener(peopleFeedFragment));
placesFeedTab.setTabListener(new MyTabsListener(placesFeedFragment));
actionBar.addTab(itemsFeedTab, 0, true);
actionBar.addTab(peopleFeedTab, 1, false);
actionBar.addTab(placesFeedTab, 2, false);
class MyTabsListener implements ActionBar.TabListener {
public Fragment fragment;
public MyTabsListener(Fragment fragment){
this.fragment = fragment;
}
#Override
public void onTabSelected(Tab tab, FragmentTransaction ft) {
// TODO Auto-generated method stub
ft.replace(R.id.home, fragment);
}
If your fragments contain only ListViews then its better to use ListFragment
Well the answer is hidden in your question , you are trying to open ListView instead of Fragment.
Why don't you open a ListFragment and you are done.

Action bar tabs - how to retrieve value from fragments in Activity?

I've settings activity, which has 3 tabs (each tab contains fragment) + in the main activity there is also buttons row - with buttons OK, and Cancel.When I press OK, I would like to do something like this:
1) get all my custom variables from all fragments
2) save them to shared prefs
But how to get access to fragment variables? I tried this:
Adding tabs in main activity:
ActionBar.Tab tab1 = actionbar.newTab().setText(res.getString(R.string.actSettingsTab1));
tab1.setTabListener(new MyTabsListener(new Tab1Fragment(), "tab1"));
actionbar.addTab(tab1);
//...similar for all tabs
This is my tab listener:
class MyTabsListener implements ActionBar.TabListener {
private Fragment fragment;
private String tag;
public MyTabsListener(Fragment fragment, String tag) {
this.fragment = fragment;
this.tag = tag;
}
#Override
public void onTabReselected(Tab tab, FragmentTransaction ft) {
// do nothing
}
#Override
public void onTabSelected(Tab tab, FragmentTransaction ft) {
ft.replace(R.id.fragment_container, fragment, tag);
}
#Override
public void onTabUnselected(Tab tab, FragmentTransaction ft) {
ft.remove(fragment);
}
}
And this is how I would like to get variables from fragments:
Tab1Fragment tab1 = (Tab1Fragment) fm.findFragmentByTag("tab1");
Tab2Fragment tab2 = (Tab1Fragment) fm.findFragmentByTag("tab2");
Tab3Fragment tab3 = (Tab1Fragment) fm.findFragmentByTag("tab3");
but it's strange - findFragmentByTag returns fragment only for selected tab, otherwise returns null. So when I have selected tab1 and press ok, findFragmentByTag return fragment for tab1, but null for others.
Maybe I'm doing something wrong, or whole my tring is goind wrong way. How to retrieve values from all fragments in parent activity and save them to shared preferences?
Separate the process into two steps:
1) Gather all settings from all the fragments in real time (i.e. as they are changed by the user). Use Listener pattern, so that fragments will expose an interface by which they will notify attached listener when user modifies any of the settings for which given fragment is responsible for. Let the activity attach itself as a listener to all fragments, and capture changes (store them in a structure that suits you).
2) Let the activity save settings using SharedPreferences when OK button is clicked. It does not have to access fragments as it always about any settings user has changed.
This way you do not have to access all the fragments at once, which is impossible if they are removed from the memory (as they are not visible anyway at a given time). Instead, you can re-assign the activity as a listener to selected fragment every time it gets selected/displayed.
I wouldn't recommend actually accessing the variables from your fragments. You could make a public method in those fragments that does the shared preferences saving that you want. Ex:
Tab1Fragment tab1 = (Tab1Fragment) fm.findFragmentByTag("tab1");
Tab2Fragment tab2 = (Tab1Fragment) fm.findFragmentByTag("tab2");
Tab3Fragment tab3 = (Tab1Fragment) fm.findFragmentByTag("tab3");
tab1.savePreferences();
tab2.savePreferences();
tab3.savePreferences();
ActionBar.Tab tab1 = actionbar.newTab().setText(res.getString(R.string.actSettingsTab1));
tab1.setTabListener(new MyTabsListener(new Tab1Fragment(), "tab1"));
actionbar.addTab(tab1);
//...similar for all tabs
this does not add your Fragment to the backstack therefore you get only the selected tab and the others are null.
I think the best you can do is iterate through the tabs of the ActionBar using getTabCount() and then you should be able to get your content with getTabAt(int index). getCustomView()

Transaction between fragments only inside one ActionBar Tab

I have an app with three tabs (ActionBar Tabs), each one with one fragment at a time.
TabListener
TabsActivity
Tab1 -> ListFragment1 -> ListFragment2 -> Fragment3
Tab2 -> Tab2Fragment
Tab3 -> Tab3Fragment
The problem is when I create the FragmentTransaction (inside OnListItemClicked) from ListFragment1 to ListFragment2, the fragments inside the other tabs also change to ListFragment2.
In the end, I want to change fragments only inside on tab and preserve the state of the other tabs.
I'm already saving the state (OnSavedInstance()).
Do you know what I'm missing here?
Some of the code:
public class TabsActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.tabs);
// setup Action Bar for tabs
ActionBar actionBar = getActionBar();
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
// instantiate fragment for the tab
Fragment networksFragment = new NetworksFragment();
// add a new tab and set its title text and tab listener
actionBar.addTab(actionBar.newTab().setText("Tab1")
.setTabListener(new TabsListener(ListFragment1)));
// instantiate fragment for the tab
Fragment historyFragment = new HistoryFragment();
// add a new tab and set its title text and tab listener
actionBar.addTab(actionBar.newTab().setText("Tab2")
.setTabListener(new TabsListener(Tab2Fragment)));
// instantiate fragment for the tab
Fragment settingsFragment = new SettingsFragment();
// add a new tab and set its title text and tab listener
actionBar.addTab(actionBar.newTab().setText("Tab3")
.setTabListener(new TabsListener(Tab3Fragment)));
}
}
public class TabsListener implements ActionBar.TabListener {
private Fragment frag;
// Called to create an instance of the listener when adding a new tab
public TabsListener(Fragment networksFragment) {
frag = networksFragment;
}
#Override
public void onTabReselected(Tab arg0, FragmentTransaction arg1) {
// TODO Auto-generated method stub
}
#Override
public void onTabSelected(Tab tab, FragmentTransaction ft) {
ft.add(R.id.fragment_container, frag, null);
}
#Override
public void onTabUnselected(Tab tab, FragmentTransaction ft) {
ft.remove(frag);
}
}
public class ListFragment1 extends ListFragment {
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
getListView().setItemChecked(position, true);
ListFragment2 fragment2 = ListFragment2.newInstance(position);
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.replace(R.id.fragment_container, fragment2);
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
ft.addToBackStack(null);
ft.commit();
}
}
You're not missing anything (or I'm missing it too).
I searched long and hard for a way to do this "properly" but I couldn't find anything. What I ended up doing is writing my own backstack logic.
Unfortunately my employer owns my code so I can't share any of that verbatim, but here was my approach:
Create an enum with one entry for each of your tabs. Let's call it TabType.
Now create an instance variable tabStacks of type HashMap<TabType, Stack<String>>. Now you can instantiate one stack for each tab - each stack is a list of tags, as specified by Fragment.getTag(). This way you don't have to worry about storing references to Fragments and whether they're going to disappear on you when you rotate the device. Any time you need a reference to a Fragment, grab the right tag off the stack and use FragmentManager.findFragmentByTag().
Now whenever you want to push a Fragment onto a tab, generate a new tag (I used UUID.randomUUID().toString()) and use it in your call to FragmentTransaction.add(). Then push the tag on top of the stack for the currently displayed tab.
Be careful: when you want to push a new fragment on top of an old one, don't remove() the old one, since the FragmentManager will consider it gone and it will be cleaned up. Be sure to detach() it, and then attach() it later. Only use remove() when you're permanently popping a Fragment, and only use add() the first time you want to show it.
Then, you'll have to add some relatively simple logic to your TabListener. When a tab is unselected, simply peek() at its stack and detatch() the associated Fragment. When a tab is selected, peek() at the top of that stack and attach() that fragment.
Lastly, you'll have to deal with Activity lifecycle quirks (like orientation changes). Persist your Map of Stacks as well as the currently selected tab, and unpack it again in your onCreate(). (You don't get this packing and unpacking for free, but it's pretty easy to do.) Luckily your TabType enum is Serializable so it should be trivial to put into a Bundle.

Tabhost in the action bar of Honeycomb app?

I have an app (for Honeycomb) with a main activity that shows a sort of dashboard, with three buttons and a title. When the user clicks a button they are taken to a screen where they can enter data and do a calculation. I would like to have two approaches to the calculation in this second ('calculator') activity, and would like to implement this through having two tabs in the action bar (only when you are in this calculator activity).
I haven't used a tabhost widget or tabs ever before, so how do I go about having a tab widget in the action bar and changing the rest of the screen (everything but the action bar and system bar) when the other tab is selected?
If someone could point me towards some source code specifically for Honeycomb action bar tabs, that would be great.
Thanks for any help, and have a great day.
See Honycomb Gallery which makes use of action bar tabs.
Tabs in the action bar is a very neat feature. To make this question complete here on SO, I'll provide an example; This code goes in your Activity's onCreate
final ActionBar actionBar = getActionBar();
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
// remove the activity title to make space for tabs
actionBar.setDisplayShowTitleEnabled(false);
// instantiate some fragments for the tabs
Fragment fragment1 = new Fragment1();
Fragment fragment2 = new Fragment2();
// add a new tab and set its title text and tab listener
actionBar.addTab(actionBar.newTab().setText(R.string.title_tab1)
.setTabListener(new MyTabListener(fragment1)));
actionBar.addTab(actionBar.newTab().setText(R.string.title_tab2)
.setTabListener(new MyTabListener(fragment2)));
You can put the MyTablListener as an inner class of your activity, It could look something like this;
class MyTabListener implements ActionBar.TabListener {
private Fragment fragment;
public MyTabListener(Fragment fragment) {
this.fragment = fragment;
}
public void onTabSelected(ActionBar.Tab tab, FragmentTransaction ft) {
ft.replace(R.id.activity_new_formula_fragment_content, fragment, null);
}
public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction ft) {
ft.remove(fragment);
}
public void onTabReselected(ActionBar.Tab tab, FragmentTransaction ft) {
}
}

Categories

Resources