I am using android DrawerLayout from Support Library to show slide menu.I am using a single activity and 5-6 fragments to show them upon selection in DrawerLayout menu.But I have a small problem which is "How do I check which fragment is currently visible so if user selected the menu item which corresponds to already opened fragment. Currently it creating the Fragment again and displaying it which is not good.The function that triggers when clicked on menu item is:
private void selectItem(int position) {
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
// Locate Position
switch (position) {
case 0:
ft.replace(R.id.content_frame, fragment1);
break;
case 1:
ft.replace(R.id.content_frame, fragment2);
break;
case 2:
ft.replace(R.id.content_frame, fragment3);
break;
}
ft.commit();
mDrawerList.setItemChecked(position, true);
// Close drawer
mDrawerLayout.closeDrawer(mDrawerList);
}
How do I check if the requested fragment is already opened so not to create it again?Is their any method to check this through FragmentManager?
I would add to #Plato's answer.
Check get currently displayed fragment question if you haven't already.
The answer says that when you add the fragment in your transaction, you can use a tag to represent a particular fragment. Something like:
ft.replace(android.R.id.content, fragment, "MY_FRAGMENT");
Later if you want to check if that fragment is visible, you can do something like:
RequestedFragment fragment = (RequestedFragment)getFragmentManager().findFragmentByTag("MY_FRAGMENT"); //"My_FRAGMENT" is its tag
if (fragment.isVisible()) {
// add your code here
}
Hope this helps.
the simplest approach would be to store last used position and compare next time. Just make your selectItem method like:
int lastPosition = -1;
private void selectItem(int position) {
if( position != lastPosition ) {
[ old method body here ]
lastPostion = position;
}
}
Assign different tags to each one of your fragments and call findFragmentByTag() of the FragmentManager to check if each one is already present.
You can also use the findFragmentById() of the FragmentManager for the same purpose and use the ids of your fragments.
If you are absolutely sure that each of fragment1, fragment2, fragment3 cannot be visible at the same time and if you reuse the same fragments (i.e. do not create a new fragment each time you change position) then the method isVisible() from the class Fragment should tell you if that fragment is currently visible.
Your switch should look like:
switch (position) {
case 0:
if(!fragment1.isVisible()) {
ft.replace(R.id.content_frame, fragment1);
}
break;
case 1:
if(!fragment2.isVisible()) {
ft.replace(R.id.content_frame, fragment2);
}
break;
case 2:
if(!fragment3.isVisible()) {
ft.replace(R.id.content_frame, fragment3);
}
break;
}
Related
i have an activity with a navigation drawer built using the library https://github.com/mikepenz/MaterialDrawer
I want to load 4 different pages from selecting 4 different items from the navigation drawer.
I decided to use fragments for each item in the navigation drawer, so when any item is pressed, a new fragment for that option is created and shown to the user.
What i want is that, regardless of which fragment the user is currently in, or which fragment he came from, whenever he presses the back button, he is returned to the original activity.
This is the code i have so far, but apparently the Fragment Backstack is not being updated.
result = new DrawerBuilder().withSelectedItem(-1)
.withActivity(this)
.withToolbar(toolbar)
.withAccountHeader(headerResult)
.addDrawerItems(
item1,item2,item3,item4
)
.withOnDrawerItemClickListener(new Drawer.OnDrawerItemClickListener() {
#Override
public boolean onItemClick(View view, int position, IDrawerItem drawerItem) {
result.setSelection(position,false);
clearStack();
switch (position) {
case 1:
fragment = new admin_fragment_data_entry();
break;
case 2:
fragment = new admin_fragment_data_edit();
break;
case 3:
break;
case 4:
break;
}
initiateFragment();
return false;
}
}
)
.build();
#Override
public void onBackPressed() {
int count = getFragmentManager().getBackStackEntryCount();
//handle the back press :D close the drawer first and if the drawer is closed close the activity
if (result != null && result.isDrawerOpen()) {
result.closeDrawer();
}
else if (count == 0) {
super.onBackPressed();
//additional code
}
else {
getFragmentManager().popBackStack();
}
}
private void initiateFragment(){
if (fragment != null) {
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.addToBackStack(null);
ft.replace(R.id.container_body, fragment);
ft.commit();
}
}
private void clearStack(){
int count = getFragmentManager().getBackStackEntryCount();
while(count > 0){
getFragmentManager().popBackStack();
count--;
}
}
Any help would be highly appreciated.
Also, should i have used fragments here? Or i should have just started 4 different activities for 4 different options from navigation drawer?
Are you using the correct fragment manager to get your backstack count?
I notice in other parts of your class you're using getSupportFragmentManager instead.
So I am currently developing an app which displays a json file via cards in the main activity. Depending on which tab is currently selected in my tabbed activity, a method which downloads the json file decides what json file to download (I pass an integer and there is a switch in the method).
Here is the method:
Fragment.Downloadjson(rootview,integer,context);
Now, for my tabbed Activity I have a SectionsPagerAdapter which has the usual stuff: getItem, getCount, and getPageTitle.
In getItem I am creating my new fragments:
#Override
public Fragment getItem(int position) {
View v1 = getWindow().getDecorView().getRootView();
switch (position) {
case 0:
//Fragment.Download(v1,0,getApplicationContext());
return new Fragment().f(Fragment.page.TODAY);
case 1:
//Fragment.Download(v1,1,getApplicationContext());
return new Fragment().f(Fragment.page.TOMORROW);
default:
return new Fragment();
}
}
Exception :
android.view.WindowManager$BadTokenException: Unable to add window -- token null is not for an application
I have found the source of this error to be the rootview parameter in my method, because it works in onCreateView with rootView as a parameter, because I define it there. Hovewer, I cannot make an if statement for the currently selected tab or currently displayed fragment there, because
a) I don't know how to get the currently selected tab
b) I'm not sure it would download the json file again after I switch the tab, because after all, the If statement would be in onCreateView
So, my question is,
how do I solve this?
Don't do it inside getItem() method. In getItem just create the fragment.
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return new MyFragment0();
case 1:
return new MyFragment1();
case 2:
return new MyFragment2();
}
return null;
}
Override the method instantiateItem and in there keep a map of fragment and their position:
private ArrayMap<Integer, MyFragmentBaseClass> mPagerFragmentMap = new ArrayMap<>();
#Override
public Object instantiateItem(ViewGroup container, int position) {
MyFragmentBaseClass fragment = (MyFragmentBaseClass) super.instantiateItem(container, position);
mPagerFragmentMap.put(position, fragment);
return fragment;
}
note MyFragmentBaseClass can be a marker interface that all fragments implement.
With the code above, you can already map each fragment to its tab.
If you're using TabLayout you can now set a listener using setOnTabSelectedListener and use one of its methods onTabSelected to know when the user selects that tab and perform any operation you want.
I have four tabs in navigation tab, and each tab has a fragment:
public Fragment getItem(int position) {
// getItem is called to instantiate the fragment for the given page.
// Return a PlaceholderFragment (defined as a static inner class below).
switch (position){
case 0:
return new PostJob();
case 1:{
return new PostedJobHistory();
}
case 2:{
return new SearchCandidates();
}
case 3:{
return new EmployerSettings();
}
default:
break;
}
return PlaceholderFragment.newInstance(position + 1);
}
I enter job details in first tab - PostJob fragment. and want to get it updated in the second tab when I go to Posted Job history fragment.
How can I achieve this.
In tab 1 I enter details in database
and in tab 2 i read database to show posted job history, but posted job history is not updated.
use this
List<Fragment> fragmentsList = getSupportFragmentManager().getFragments();
if (fragmentsList == null || fragmentsList.size() == 0) {
return;
}
for (Fragment fragment : fragmentsList) {
if (fragment instanceof PostedJobHistory) {
((PostedJobHistory) fragment).yourUpdateMethod();
}
}
I am trying to show multiple fragments in a FrameLayout container. The basic idea is to use a spinner to select a different fragment to be displayed in the container. I am trying to add them programmatically and for this I have created one xml file for each day of the week which is what I am trying to show.
// Set up the spinner to select days of the week.
Spinner spinner = (Spinner) weekView.findViewById(R.id.day_selecter);
spinner.setOnItemSelectedListener(new OnItemSelectedListener(){
#Override
public void onItemSelected(AdapterView<?> parent, View view,
int pos, long id) {
// For each day of the week, create a new instance of Switches
// get an instance of FragmentTransaction from your Activity
FragmentManager fm = getFragmentManager();
FragmentTransaction fragmentTransaction = fm.beginTransaction();
Fragment frag = fm.findFragmentByTag(Integer.toString(pos));
if(frag == null) {
switch(pos) {
case 0: frag = new mondayFragment();
break;
case 1: frag = new tuesdayFragment();
break;
case 2: frag = new wednesdayFragment();
break;
case 3: frag = new thursdayFragment();
break;
case 4: frag = new fridayFragment();
break;
case 5: frag = new saturdayFragment();
break;
case 6: frag = new sundayFragment();
break;
}
fragmentTransaction.add(R.id.week_program_switches, frag, Integer.toString(pos));
}
else
fragmentTransaction.replace(R.id.week_program_switches, frag, Integer.toString(pos));
fragmentTransaction.commit();
}
#Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
The program manages to load the fragment corresponding to the first day of the week, i.e. the mondayFragment into the container. Each fragment contains textboxes and checkboxes so i need to save the view of each fragment loaded. At times the app would allow me to select a different day but the content from the previous fragment gets carried over and it eventually crashes the app with an IllegalStateException error: Day has already been added to fragment transaction. other times, simply selecting a different day in the spinner immediately crashes the app with the same error. I really need your help in this as I'm all out of ideas.
Few Notes:
1. I have not added any fragment statically into any layout. I have seen this to be the cause of many related issues.
2. I originally intended to use a single fragment and create multiple instances in a frameLayout container but i had similar issues of saving the content in each instance.
I have three fragments which are bound to Tabs as you swipe from TabA to TabB to TabC and it respectively loads FragA, FragB and FragC using ViewPager(Hope you understand this part). This swipe Tabs works just fine but I have a few fixed buttons attached to the bottom of the layout and when each button is clicked i want to load a new fragment to replace any of the Fragment A, B or C. The replacement works fine but when i return to the replaced/previous Fragment all the UI components on that layout completely disappears and it does not indicate if it has been paused, stopped or destroyed. Heres the Code:
Replacing previous Fragment Code:
if(actionBar.isShowing())
{
actionBar.hide();
}
FragmentManager fms = getSupportFragmentManager();
FragmentTransaction fts = fms.beginTransaction();
search s = new search();
if(getCurrentDisplayFragment() == Type1)
{
setcurrentItem(viewPager.getCurrentItem());
Fragment frgs = mAdapter.getItem(viewPager.getCurrentItem());
viewPager.removeViewAt(viewPager.getCurrentItem());
frgs.setUserVisibleHint(false);
Log.i("if say", "Not Responding");
}
fts.replace(R.id.hoster, s);
fts.addToBackStack("search");
fts.commit();
setCurrentDisplayFragment(s,Type2);
The setCurrentDisplayFragment(s,Type2); i built that code to help indicate which fragment is being replaced and what measures to take. S is a Fragment and Type2 is a string
Thus when the back button is pressed i want to return back to any of the Fragments A, B or C that was previously replaced.
Here is the code:
#Override
public void onBackPressed() {
// TODO Auto-generated method stub
//super.onBackPressed();
Log.i("event in back","I have been presssed ");
if(getCurrentDisplayFragment() == Type2)
{
actionBar.show();
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
fm.popBackStack();
ft.remove(getCurrentDisplayFragment(Type2));
ft.commit();
viewPager.setCurrentItem(getcurrentItem());
actionBar.setSelectedNavigationItem(getcurrentItem());
Fragment frags = mAdapter.getItem(viewPager.getCurrentItem());
frags.setUserVisibleHint(true);
setCurrentDisplayFragment(frags, Type1);
}
else
{
}
}
Though some variable names and method declaration are not shown but you should get the picture of what am doing because all declarations have been done. its just The Replacement and retaining of their UI states thats giving me a problem.
Why are you complicating this so much? If you want to replace any of the fragments in the view pager, then just replace the view pager. It would be a lot easier if you would have a fragment holding the view pager with the tabs and when the bottom buttons are pressed, you just replace the fragment holding the view pager with the new fragment. In this way you won't need to do anything when the back button will be pressed.