I'm currently working in a new android project .
I'm using ActionBar Compact with Tab navigation mode .
I added 3 tabs in my Activity.
In the first Fragment , there is a listView and TextView (witch contains the text of slected row of list)
Now , when i select the second tab (fragment (empty) ), and go back to my first fragment , the TextView contains the selected value
BUT when I select the third tab (fragment 3 empty) , and go back to my first fragent , the TextView was initilised .
I think that my problem is in the TabListener
can anyone help me please !
this is the code (simplified)
MainActivity:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
/** Getting a reference to action bar of this activity */
mActionbar = getSupportActionBar();
/** Set tab navigation mode */
mActionbar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
/** set HomeButton to true */
mActionbar.setHomeButtonEnabled(true);
/** Getting a reference to ViewPager from the layout */
mPager = (ViewPager) findViewById(R.id.pager);
/** Getting a reference to FragmentManager */
FragmentManager fm = getSupportFragmentManager();
/** Defining a listener for pageChange */
ViewPager.SimpleOnPageChangeListener pageChangeListener = new ViewPager.SimpleOnPageChangeListener(){
#Override
public void onPageSelected(int position) {
super.onPageSelected(position);
mActionbar.setSelectedNavigationItem(position);
}
};
/** Setting the pageChange listener to the viewPager */
mPager.setOnPageChangeListener(pageChangeListener);
/** Creating an instance of FragmentPagerAdapter */
MyFragmentPagerAdapter fragmentPagerAdapter = new MyFragmentPagerAdapter(fm);
/** Setting the FragmentPagerAdapter object to the viewPager object */
mPager.setAdapter(fragmentPagerAdapter);
mActionbar.setDisplayShowTitleEnabled(true);
/** Defining tab listener */
ActionBar.TabListener tabListener = new ActionBar.TabListener() {
#Override
public void onTabUnselected(Tab tab, FragmentTransaction ft) {
}
#Override
public void onTabSelected(Tab tab, FragmentTransaction ft) {
mPager.setCurrentItem(tab.getPosition());
}
#Override
public void onTabReselected(Tab tab, FragmentTransaction ft) {
}
};
/** Creating fragment1 Tab */
Tab tab = mActionbar.newTab()
.setText("Categories")
.setTabListener(tabListener);
mActionbar.addTab(tab, 0, false);
/** Creating fragment2 Tab */
tab = mActionbar.newTab()
.setText("Acceuil")
.setTabListener(tabListener);
mActionbar.addTab(tab, 1, true);
/** Creating fragment3 Tab */
tab = mActionbar.newTab()
.setText("Services")
.setTabListener(tabListener);
mActionbar.addTab(tab, 2, false);
}
Fragment 2 ::
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
categories = inflater.inflate(R.layout.fragment_categories, container, false);
list= (ListView)categories.findViewById(R.id.listCategories);
// Defined Array values to show in ListView
String[] values = new String[] { "Android List View",
"Adapter implementation",
"Simple List View In Android",
"Create List View Android",
"Android Example",
"List View Source Code",
"List View Array Adapter",
"Android Example List View"
};
ArrayAdapter<String> adapter = new ArrayAdapter<String>(getActivity().getApplicationContext(),android.R.layout.simple_list_item_1, android.R.id.text1, values);
// Assign adapter to ListView
list.setAdapter(adapter);
t =(TextView)categories.findViewById(R.id.textView1);
list.setOnItemClickListener(new OnItemClickListener()
{
public void onItemClick(AdapterView<?> parent,View v, int position, long id)
{
Toast.makeText(getActivity().getBaseContext(),"category " + (position + 1) +" selected",Toast.LENGTH_SHORT).show();
t.setText("category " + (position + 1) +" selected");
}
});
return categories;
}
ScreenShots
This when I select row from listView (fragment 1) ==> fragment 2 ==> go back fragment 1
This when I select row from listView (fragment 1) ==> fragment 3 ==> go back fragment 1
In onCreate() of your main Activity, after you initialize your ViewPager, add:
mPager.setOffscreenPageLimit(2);
Per the Android docs, setOffscreenPageLimit():
Sets the number of pages that should be retained to either side of the current page in the view hierarchy in an idle state. Pages beyond this limit will be recreated from the adapter when needed.
This is offered as an optimization. If you know in advance the number of pages you will need to support or have lazy-loading mechanisms in place on your pages, tweaking this setting can have benefits in perceived smoothness of paging animations and interaction. If you have a small number of pages (3-4) that you can keep active all at once, less time will be spent in layout for newly created view subtrees as the user pages back and forth.
You should keep this limit low, especially if your pages have complex layouts. This setting defaults to 1.
Since the default setting is 1, when you start on the first tab and navigate to the third tab, the first page (i.e., fragment) is being recreated when you navigate back to the first tab. Setting this to 2 should retain the fragment in the first tab even when you navigate to the third tab.
try using setRetainInstance() inside your fragments
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
This way your fragment instance is not destroyed, only its view hierarchy.
Related
I want to show a same fragment for example fragment having activities of a day of week in a viewpager for all days with different data. I will be giving the dayNumber parameter to each fragment being instantiated and showing related activities. The problem is I see same fragment in each tab no matter what parameter I passed. I think the last fragment added or instantiated by a pager overrides all the other tab fragments instance. Because when I open a list item in expendableList View it is opened in all fragments of the pager.
This is how I am using the pager and fragment.
Pager
mPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
#Override
public void onPageSelected(int position) {
// When swiping between pages, select the
// corresponding tab.
if (bar.getNavigationMode() == ActionBar.NAVIGATION_MODE_TABS)
if (bar.getSelectedNavigationIndex() != position)
bar.setSelectedNavigationItem(position);
// should be changed when some solution comes.
if (tab == 0) {
Fragment ev;
if ((ev = (Fragment) mPagerAdapter.instantiateItem(mPager,
0)) instanceof frTimetable)
((frTimetable) ev).refresh(day.Monday);
} else if (tab == 1) {
Fragment ac;
if ((ac = (Fragment) mPagerAdapter.instantiateItem(mPager,
1)) instanceof frTimetable)
((frTimetable) ac).refresh(day.Tuesday);
}
tab = position;
}
Fragement
{
//class other methods
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fr_timetable, container, false);
ExpandList = (ExpandableListView) view.findViewById(R.id.expActivityView);
//I will change the list items in refresh method of the fragment for a day type
ExpListItems = new ArrayList<Items>(Timetable_Provider.getAllActivites());
ExpAdapter = new ExpandListAdapter(getActivity(), ExpListItems);
ExpandList.setAdapter(ExpAdapter);
return view;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
}
instantiateItem() is certainly not what you want. That is called by ViewPager, not by a consumer of a ViewPager.
Normally, you would provide the data to the ViewPager as part of setting up the pages, inside of your PagerAdapter. For example, this sample app uses the arguments Bundle to pass the page number of the page to the Fragment that is the implementation of the page.
If the data inside a page needs to be updated, ideally the page itself determines on its own that this is needed and handles it. Or, use an event bus (e.g., LocalBroadcastManager, greenrobot's EventBus, Square's Otto) to publish information that relevant pages can pick up. There is no great way to get at an existing page from outside of the page itself using FragmentPagerAdapter or FragmentStatePagerAdapter (e.g., to have an activity push data into a page), which is one of the reasons I wrote ArrayPagerAdapter.
I have ActionBar Tabs setup. It consists of 4 tabs. Everything is fine until I navigate away from TabbedFragment and returning back.
I create tabs like this:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final ActionBar actionBar = getActionBar();
tabs = Lists.newArrayList();
tabs.add(new TabDefinition<>("Tab 1"));
tabs.add(new TabDefinition<>("Tab 2"));
tabs.add(new TabDefinition<>("Tab 3"));
tabs.add(new TabDefinition<>("Tab 4"));
for (TabDefinition tab : tabs) {
actionBar.addTab(actionBar.newTab()
.setText(tab.text)
.setTag(tab.tag)
.setTabListener(this));
}
}
And initialize adapter like this:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.paging_tab_container, container, false);
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
viewPager = (ViewPager) view.findViewById(R.id.pager);
viewPager.setAdapter(new FragmentStatePagerAdapter(getFragmentManager()) {
#Override
public Fragment getItem(int position) {
return tabs.get(position).fragment;
}
#Override
public int getCount() {
return tabs.size();
}
});
viewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
#Override
public void onPageSelected(int position) {
getActionBar().setSelectedNavigationItem(position);
}
});
viewPager.setCurrentItem(getActionBar().getSelectedNavigationIndex(), true);
}
When returning back to TabbedFragment selected tab and 1 next to it would not have any content. Just empty view. But if I select current + 2 fragment content is loaded. And then returning to that first fragment content is reloaded.
For example I have A, B, C, D tabs. Before leaving TabbedFragment I had selected tab A.
When returning to TabbedFragment I still am at tab A, but it's empty. So is tab B.
But when selecting tab C it is created and loaded. Returning to tab A it is recreated.
What could be the problem here?
After a while ran into the same problem again, so updating this question.
If you're using FragmentStatePagerAdapter you should provide FragmentManager via getChildFragmentManager() instead of getFragmentManager(). See Issue 55068: ViewPager doesn't refresh child fragments when back navigation via backstack
Okay so When using a FragmentStatePagerAdapter your fragments will be destroyed when you navigate anymore than one fragment Away since by default offScreenPageLimit is set to 1 by default just as mentioned above.
Typically this Class is used for an activity that has a very large set of Fragments, i.e have to scroll through a large amount of views. If your application does not need more than say 3-4 tabs I would suggest using FragmentPagerAdapter instead, and then specifying your offScreenPageLimit to something like 3, so if you get to the 4th Tab, all 3 tabs before will still be in memory.
Here is some Sample Code for a project on github that i created illustrating how to dynamically load the fragments if you don't want to add this offScreenPageLimit.
https://github.com/lt-tibs1984/InterfaceDemo/blob/master/src/com/divshark/interfacedemo/InterfaceDemoMain.java
Walk through all this code in this Class, and you will see how I'm dynamically loading the fragments, each time my ViewPager is slid over. Most notably at the bottom.
You can download this code, and use it as a test base for what you want to do.
Try adding the setOffScreenPageLimit(2) in the onCreate() method for the viewPager and notice the different behavior. To check the behavior, edit the text in fragment 1. Navigate Away and navigate back, with this set or not. You will see when it is set, the fragment's text remains what you change it to, since the fragment is never recreated.
Please provide additional questions if you have them.
GoodLuck
UPDATE
private static final String [] fragmentClasses = {"com.example.project.YourFragment1","com.example.project.YourFragment2","com.example.project.YourFragment3"};
viewPager.setAdapter(new FragmentStatePagerAdapter(getFragmentManager()) {
#Override
public Fragment getItem(int position) {
Fragment fragmentAtPosition = null;
// $$$$ This is the Important Part $$$$$
// Check to make sure that your array is not null, size is greater than 0 , current position is greater than equal to 0, and position is less than length
if((fragmentClasses != null) && (fragmentClasses.length > 0)&&(position >= 0)&& (position < fragmentClasses.length))
{
// Instantiate the Fragment at the current position of the Adapter
fragmentAtPosition = Fragment.instantiate(getBaseContext(), fragmentClasses[position]);
fragmentAtPosition.setRetainInstance(true);
}
return fragmentAtPosition;
}
#Override
public int getCount() {
return fragmentClasses.length;
}
});
The problem exists in the Fragments you use as tabs, I think. They seem to not show anything when they are resumed (see Fragment lifecycle). The "weird" issue that only the currently selected +/-1 tab is empty, is because the offScreenPageLimit of your ViewPager is 1 by default. All tabs above this threshold are re-created.
Therefore, increasing the value will -- in your case -- cause all your tabs to appear empty after resuming. Check in your Fragment code which lifecycle methods you use to inflate your layout, set adapters and so forth, because that's what's causing your trouble.
I guess this happens because while loading fragment android loads current and current+1, if you debug you would not see onPause getting called for the immediate next fragment.
You can reload content programmatically in onTabChanged() method of TabHost.OnTabChangeListener.
After doing much research, this worked for me.
I have a complex layout with 3 tabs in a fragment, that gets switched out for other fragments. I realized that the ViewpagerAdapter will retain state, even if you press the home button. My problem was switching back and forth would null out the child fragment UI view elements and crash. The key is to not new out your ViewPagerAdapter. Adding the null check for the Adapter worked for me. Also, be sure to allocate setOffscreenPageLimit() for your needs. Also, from what I understand setRetainInstance(true); should not be used for fragments that have UI, it is designed for headless fragments.
In the fragment that holds your Tabs:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_tab, container, false);
tabLayout = (TabLayout) view.findViewById(R.id.tablayout);
viewPager = (ViewPager) view.findViewById(R.id.viewPager);
//Important!!! Do not fire the existing adapter!!
if (viewPagerAdapter == null) {
viewPagerAdapter = new ViewPagerAdapter(getChildFragmentManager());
viewPagerAdapter.addFragments(new AFragment(), "A");
viewPagerAdapter.addFragments(new BFragment(), "B");
viewPagerAdapter.addFragments(new CFragment(), "C");
}
//Allocate retention buffers for three tabs, mandatory
viewPager.setOffscreenPageLimit(3);
tabLayout.setupWithViewPager(viewPager);
viewPager.setAdapter(viewPagerAdapter);
return view;
}
Or more simply when navigating back to tabbedfragment (assuming you use an intent and the fragment is within an activity) use:
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
This keeps the original activity and moves it to the top of the stack rather than recreating it, thus you never need to recreate the viewPager.
As we know, ActivityGroup is deprecated.I'm try to reconfigure my code.
this code use ActivityGroup :
public void lauchContentActivity(Intnet intent) {
View view = getLocationActivityManager().startActivity(
intent.getComponent().getShortClassName(),
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP))
.getDecorView();
setContentView(view);
}
So I can toogle any activity's visible ,and save the activity instance state which is hide.
But FragmentManager has only a backstack, and can't bring a fragment to front expect pressing the Back button.
How to manager Fragment like ActivityGroup manager Activity?
I achieved fragment tabs, Its was challenging for me to achieve and to understand fragment hierarchy while adding and removing fragment.
As the question of managing fragments, its depend on your requirement, this sample details you hierarchy of fragments & way to manage fragment with the help of HashMap.
Below class will explain you behaviorof fragment. (class present in that sample)
AppMainTabActivity.java
public class AppMainTabActivity 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>());
mStacks.put(AppConstants.TAB_C, 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.toolkittabicon));
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.followtabicon));
mTabHost.addTab(spec);
spec = mTabHost.newTabSpec(AppConstants.TAB_C);
spec.setContent(new TabHost.TabContentFactory() {
public View createTabContent(String tag) {
return findViewById(R.id.realtabcontent);
}
});
spec.setIndicator(createTabView(R.drawable.myhuddletabicion));
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 ToolKitFragment(), false, true);
} else if (tabId.equals(AppConstants.TAB_B)) {
pushFragments(tabId, new FollowFragment(), false, true);
} else if (tabId.equals(AppConstants.TAB_C)) {
pushFragments(tabId, new HuddleFragment(), 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 (((BaseFragment) mStacks.get(mCurrentTab).lastElement())
.onBackPressed() == false) {
Log.d("######", "on back press");
/*
* top fragment in current tab doesn't handles back press, we can do
* our thing, which is
*
* if current tab has only one fragment in stack, ie first fragment
* is showing for this tab. finish the activity else pop to previous
* fragment in stack for the same tab
*/
if (mStacks.get(mCurrentTab).size() == 1) {
super.onBackPressed(); // or call finish..
} else {
popFragments();
}
} else {
// do nothing.. fragment already handled back button press.
}
}
/*
* 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);
}
}
But if you have child fragment then it will create issue on back press which is not handle in that sample, solution# you have to Override onDetach() method and manage child fragment check below code snippet.
#Override
public void onDetach() {
super.onDetach();
try {
Field childFragmentManager = Fragment.class.getDeclaredField("mChildFragmentManager");
childFragmentManager.setAccessible(true);
childFragmentManager.set(this, null);
} catch (NoSuchFieldException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
Additionally refer developer site and dig fragment in details.
The feasibility of this answer will depend on what you want to accomplish with the different activities. I had a similar problem and I solved it by using Fragments. Imagine that you have one Activity for your task which has different facets. Then you can easily use the Activity to gather and persist data that you will need in each Fragment or to feed your business logic and your Fragments can each cater to different facets of the task. I would highly recommend this as Fragmentand FragmentManagerare supposed to replace the deprecated ActivityGroup. Here is some documentation on the matter:
FragmentManager
Fragment
Now, using Fragments is a bit different from using Activity but not to much. Basically, you declare your Fragmentlike any other object. To show your Fragment you will use:
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
YourFragment yourFragment = new YourFragment();
fragmentTransaction.replace(R.id.containerID, yourFragment);
fragmentTransaction.addToBackStack();
fragmentTransaction.commit();
The documentation that you can download using ADK also contains a lot of samples that use Fragments. I believe it is a good start if you want to get some quality code snippets!
I want a tab layout in which, on every tab there are two fragments, One above showing a progress of a task that is completed by giving input in 3 tabs, from start to end, and another main fragment below that progress fragment that will be taking input.
That makes a title and detail in every tab, title fragment same and detail fragment (input fragment different on each tab)!
I tried all methods for two days :(
My Solution
i tried a layout that contains two framelayout for main fragment that is added into two tabs from ViewPager,showed here:
public static class ScreenSlidePagerAdapter extends
FragmentStatePagerAdapter {
public static ArrayList<Fragment> tabs_fragments = new ArrayList<Fragment>(
3);
public ScreenSlidePagerAdapter(FragmentManager fm,
ArrayList<Fragment> tabs) {
super(fm);
ScreenSlidePagerAdapter.tabs_fragments = tabs;
}
#Override
public Fragment getItem(int arg0) {
return tabs_fragments.get(arg0);
}
#Override
public int getCount() {
return tabs_fragments.size();
}
#Override
public int getItemPosition(Object object) {
if (tabs_fragments.contains(object)) {
return POSITION_UNCHANGED;
}
return POSITION_NONE;
}
public static void setItem(Fragment fr, int position) {
if (position <= 2 && position >= 0) {
tabs_fragments.remove(position);
tabs_fragments.add(position, fr);
} else
Log.d("adding tab", "wrong tab position to add fragment at");
}
}
On activity start i do this, see i get tab position that is used in main fragment for loading detail fragment for different tab positions:
mPager = (ViewPager) findViewById(R.id.pager);
// fragments for pagers
ArrayList<Fragment> tabs = new ArrayList<Fragment>();
tabs.add(new FrEventDetails());
tabs.add(new FrEventDetails());
tabs.add(new FrRuleSave());
mPagerAdapter = new ScreenSlidePagerAdapter(getFragmentManager(), tabs);
mPager.setAdapter(mPagerAdapter);
bar = getActionBar();
bar.setTitle("Add Rule");
bar.setSubtitle("select event for rule");
bar.setDisplayShowTitleEnabled(true);
bar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
bar.setDisplayOptions(0, ActionBar.DISPLAY_SHOW_TITLE);
bar.setDisplayHomeAsUpEnabled(true);
bar.addTab(bar.newTab().setText("Select Event")
.setTabListener(new TabListener(mPager)));
bar.addTab(bar.newTab().setText("Select Action")
.setTabListener(new TabListener(mPager)));
bar.addTab(bar.newTab().setText("Save Rule")
.setTabListener(new TabListener(mPager)));
if (savedInstanceState != null) {
bar.setSelectedNavigationItem(savedInstanceState.getInt("tab", 0));
}
mPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
#Override
public void onPageSelected(int position) {
// When swiping between pages, select the
// corresponding tab.
if (bar.getSelectedNavigationIndex() != position)
bar.setSelectedNavigationItem(position);
tab = position;
}
and then when this fragment is attaching to activity I add a progress layout (title( for one frame-layout and based on the selected tab, add another fragment for detail frame layout(for input).
showed here:
public class FrEventDetails extends Fragment {
Context context;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
context = (Context) activity;
int tab = AddRule1.tab;
android.app.FragmentTransaction ft = activity.getFragmentManager()
.beginTransaction();
ft.add(R.id.fr_rule_progress_container, new FrProgress());
if (tab == 1) {
ft.replace(R.id.fr_event_input_container, new FrActionSelect(),
"cell1_1").commit();
} else if (tab == 0) {
ft.replace(R.id.fr_event_action_container, new FrEventSelect(),
"cell1_2").commit();
}
}
NOTE* layout for this main fragment is simple containing two framelayouts.
result : It shows on start a title and detail beautifully on first tab, but on second tab, only main fragment and,
when i swipe to third fragment and come back, second fragment is filled beautifully (after 1 second of showing) but 1st fragment is now empty.
i can't understand what to do with this.
please give some solution to this tearful problem of two days
To Show all Tabs at the same time in start, increase PagerOffSetLimit (to 2 in this case for 3 tabs)
For refreshing state of fragment or calling some methods on your fragment in the tab beside your current tab, use the Pager.instantiateItem method.
One second delay is because you have on the animateLayoutChanges in your xml.
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.