I use a mainActivity which contains a frameLayout.
In this framelayout i load different fragments (by clicking on a menuitem in a navigation drawer) When i switch between fragments with a static layout (.xml layout file) everything works fine. 1 fragment has a dynamic created layout (which is created in the onCreateView method). When i navigate to this fragment and navigate back to another fragment i can still see the "old" fragment with the dynamic created controls trough the "new" fragment.
The code what i use to switch between the fragment is:
mFragmentManager = getSupportFragmentManager();
mFragmentTransaction = mFragmentManager.beginTransaction();
mFragmentTransaction.replace(R.id.fr_content_container,new AlertOverviewFragment()).commit();
mNavigationView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(MenuItem menuItem) {
mDrawerLayout.closeDrawers();
if (menuItem.getItemId() == R.id.nav_item_alerts) {
FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.fr_content_container, new AlertOverviewFragment()).commit();
}
if (menuItem.getItemId() == R.id.nav_item_news) {
FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.fr_content_container, new NewsFragment()).commit();
}
if (menuItem.getItemId() == R.id.nav_item_add_country) {
FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.fr_content_container, new AddCountryFragment()).commit();
}
return false;
}
});
I can give the background of the new activity a background color so the old fragment isn't visible anymore but i think this is not the best practice solution. The old fragment will then still exist. What is the best way to solve this problem?
AND when i have this problem with this fragmen, are there then also other fragments that still exist in the background after navigate to another fragment? when yes, do i need to "destroy" these before switch to the new fragment or something?
I read something about the live cycles and understand this but i thought Android manages these states by him self?
Related
I'm creating a fragment which is supposed to act like a menu. I have successfully inflated it to the activity where I wanted it to be, however I now I realise that I cannot close the fragment. Furthermore I am able to scroll the contents of the activity which the fragment is placed over. How can I edit my code in such a way that the fragment will close after an action on the activity is detected, or one of it's contents is clicked?
I created the fragment by simply adding a fragment via new -> Fragment -> Fragment(Blank). I have not touched any of the code and have initialized the fragment like so in a on click:
findViewById(R.id.Menu).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.setCustomAnimations(R.anim.enter_from_left, R.anim.exit_to_left, R.anim.enter_from_left, R.anim.exit_to_left);
MenuFragment menuFragment = new MenuFragment();
fragmentTransaction.replace(android.R.id.content, menuFragment);
fragmentTransaction.commit();
}
});
This is what it looks like, ignore the horrendous design.
I am using NavigationDrawer in my application and each menu item in drawer is a fragment.Whenever user chooses a menu item I replace the current fragment in the main container with the requested one but it recreates the fragment every-time, so i updated my code to reuse the existing fragments instead of creating them again and again as content of fragments remain same. My updated code to show fragment is :
public void showTabFragment() {
TabFragment Tf = (TabFragment) mFragmentManager.findFragmentByTag(Constants.TAB_FRAGMENT);
mFragmentTransaction = mFragmentManager.beginTransaction();
if (Tf != null) {
mFragmentTransaction.replace(R.id.containerView, Tf, Constants.TAB_FRAGMENT);
} else {
mFragmentTransaction.replace(R.id.containerView, new TabFragment(), Constants.TAB_FRAGMENT);
}
mFragmentTransaction.commit();
}
In above code I am trying to get fragments by Tag but it always returns null and executes the else case(new fragment).Could someone please guide me what am I doing wrong in my code?
I guess the code you've shown is for one of your menu fragment? If that's the case, what is probably happening is every time you open a menu item, the container is replaced with the new fragment(say, Fragment B) with its new tag(say, TAG 'B'). So, when you try to open the previous fragment(say, Fragment A) using it's tag(TAG 'A'), it won't be there, because that's what you replaced.
One possible solution is to hold references to the fragment as they are created, in, say a hashmap, and reuse them instead.
private HashMap<String, Fragment> menuFragments = new HashMap<>();
public void showMenu(String fragmentID)
{
MenuFragment fragment = menuFragments.get(fragmentID);
if(fragment == null)
{
fragment = new MenuFragment(); //Create the respective menu fragment based on the ID.
menuFragments.put(fragmentID, fragment);
}
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.containerView, fragment, fragmentID);
transaction.commit();
}
I have a navigation drawer and clicking on items shows/hides/creates full screen fragments.
For the most part, this code works great. But sometimes, maybe 1% of the time, I will get crazy full screen fragment overlapping when opening the app while it has already been running.
Is the problem with my code..? Or maybe something else in Android where it does not recognize I have the fragments with the tags already created?
Here is the relevant code for how I show/hide/create fragments:
#SuppressWarnings("StatementWithEmptyBody")
#Override
public boolean onNavigationItemSelected(MenuItem item) {
// Get to drawer layout so we can interact with it
mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
// Get the fragment manager to remove/add fragments
FragmentManager fragmentManager = getSupportFragmentManager();
// Handle navigation view item clicks here.
int id = item.getItemId();
if (id == R.id.nav_profile) {
// Hide visible fragment
fragmentManager.beginTransaction().hide(getVisibleFragment()).commit();
// Check if the fragment exists first.
if(fragmentManager.findFragmentByTag("profileFragment") != null) {
// If the fragment exists, show it (no reason to recreate it).
fragmentManager.beginTransaction()
.show(fragmentManager.findFragmentByTag("profileFragment"))
.commit();
} else {
// If the fragment does not exist, add it to fragment manager with a tag to identify it.
// Create new fragment instance with required argument(s).
ProfileFragment fragment = ProfileFragment.newInstance();
fragmentManager.beginTransaction()
.add(R.id.content_frame, fragment, "profileFragment")
.commit();
}
// Set the title
mToolbarTitleTextView.setText(R.string.title_activity_profile);
} else if (id == R.id.nav_feed) {
// Hide visible fragment
fragmentManager.beginTransaction().hide(getVisibleFragment()).commit();
// Check if the fragment exists first.
if(fragmentManager.findFragmentByTag("feedFragment") != null) {
// If the fragment exists, show it (no reason to recreate it).
fragmentManager.beginTransaction()
.show(fragmentManager.findFragmentByTag("feedFragment"))
.commit();
} else {
// If the fragment does not exist, add it to fragment manager with a tag to identify it.
fragmentManager.beginTransaction()
.add(R.id.content_frame, new feedFragment(), "feedFragment")
.commit();
}
// Set the title
mToolbarTitleTextView.setText(R.string.title_activity_feed);
} else if (id == R.id.nav_notifications) {
// Hide visible fragment
fragmentManager.beginTransaction().hide(getVisibleFragment()).commit();
// Hide the post button
mPostButton.setVisibility(View.GONE);
// Check if the fragment exists first.
if(fragmentManager.findFragmentByTag("notificationsFragment") != null) {
// If the fragment exists, show it (no reason to recreate it).
fragmentManager.beginTransaction()
.show(fragmentManager.findFragmentByTag("notificationsFragment"))
.commit();
} else {
// If the fragment does not exist, add it to fragment manager with a tag to identify it.
fragmentManager.beginTransaction()
.add(R.id.content_frame, new NotificationsFragment(), "notificationsFragment")
.commit();
}
// Set the title
mToolbarTitleTextView.setText(R.string.title_activity_notifications);
}
mDrawerLayout.closeDrawer(GravityCompat.START);
return true;
}
// Useful method to hide the currently visible fragment
public Fragment getVisibleFragment(){
FragmentManager fragmentManager = MainActivity.this.getSupportFragmentManager();
List<Fragment> fragments = fragmentManager.getFragments();
if(fragments != null){
for(Fragment fragment : fragments){
if(fragment != null && fragment.isVisible())
return fragment;
}
}
return null;
}
EDIT: It is really hard to reproduce this error which makes it hard to debug. It seems to randomly happen.
Why hide and keep all the fragments with fragmentManager.beginTransaction().add(); you can avoid this error by keeping only one fragment in memory and avoiding the hassle of hiding fragments by using fragmentManager.beginTransaction().replace() and using the fragment lifecycle methods to store the fragment state if necessary.
Here is how I solved the problem. In my MainActivity I did this:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(null);
setContentView(R.layout.activity_main);
}
Basically what was happening is if I had 1+ fragments on the screen, if the android system ran low on resources while the app was in the background and shut it down, when restored, MainActivity.onCreate() would be called and it would re-instantiate all the fragments with the call
super.onCreate(savedInstanceState);
So I just made it null and this prevents from all those fragments to be recreated.
The reason they are overlapping is because they were all getting shown at once.
Definitely not the correct way to do it, but it solves my problem right now =P
Is it possible to use multiple content_main.xml to change the MainActivity onNavigationItemSelected or is there a better way to do this?
the right way to It would be using multiple fragments in the main activity
GO to android studio and create a new project select the navigation drawer template you will get an example code now create fragments and add them on the navigation menu onNavigationItemSelected event.
If what you are looking for is to have different layouts for your items in
NavigationDrawer. Better consider using Fragments.
public boolean onNavigationItemSelected(MenuItem item) {
// Handle navigation view item clicks here.
int id = item.getItemId();
if (id == R.id.nav_list) {
ListFragment fragment = new ListFragment();
android.support.v4.app.FragmentTransaction fragmentTransaction =
getSupportFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.fragment_container, fragment);
fragmentTransaction.commit();
} else if (id == R.id.nav_gallery) {
GalleryFragment fragment = new GalleryFragment();
android.support.v4.app.FragmentTransaction fragmentTransaction =
getSupportFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.fragment_container, fragment);
fragmentTransaction.commit();
}
return true;
}
this would help you to navigate to different fragment in your NavigationItem.
Hope it helps.
I am using SLiding menu at My App and according to menu item , I am changing always the fragment at activity which have binded already to menu. But There is performance issue. it is freezing when i am attaching the fragments. but after the wiew created. the performance is normal. I am replacing the fragments after call the toggle function at menu.
is there anyone to have any opinion about it ?
just call it as:
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Pass the event to ActionBarDrawerToggle, if it returns
// true, then it has handled the app icon touch event
if (item.getItemId() == R.id.evnetCalender) {
if (!isToggle) {
item.setIcon(R.drawable.ic_view_list_white_24dp);
setFragment(caldroidCalendarFragment);
isToggle = true;
} else {
item.setIcon(R.drawable.ic_event_white_24dp);
setFragment(scheduleEventFragment);
isToggle = false;
}
}
and set your fragment when you toggle it
private void setFragment(Fragment fragment1) {
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction =
fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.content, fragment1);
fragmentTransaction.commit();
}
this content identifier is your frame layout id in which your fragment take place