I have a google map inside a fragment. If I go to outside of the app while I am using the fragment, on my return I can't see the map. But if I generally move from one fragment to another, there is no problem. I am using navigation drawer, so I have to use the fragment.
Any solution would be appreciated. Thank you.
Here is the part of that fragment related to onPause(). I think I have done some mistakes with the code, plz kindly point that out to me.
#Override
public void onPause() {
super.onPause();
final FragmentManager fragManager = this.getFragmentManager();
final Fragment fragment = fragManager.findFragmentById(R.id.map1);
if(fragment!=null){
fragManager.beginTransaction().remove(fragment).commit();
}
}
#Override
public void onDestroy() {
super.onDestroy();
final FragmentManager fragManager = this.getFragmentManager();
final Fragment fragment = fragManager.findFragmentById(R.id.map1);
if(fragment!=null){
fragManager.beginTransaction().remove(fragment).commit();
}
}
You can fix that Exception . In your fragment where you are showing map, override onDestroyView().
public void onDestroyView() {
super.onDestroyView();
Fragment fragment = (getFragmentManager().findFragmentById(R.id.map));
FragmentTransaction ft = getActivity().getSupportFragmentManager().beginTransaction();
ft.remove(fragment);
ft.commit();
}
And i think you need to start from API 16.
Hope this helps.
At last I got the solution. Now it's working, the app doesn't crash anymore.
#Override
public void onDestroyView() {
super.onDestroyView();
try{
MapFragment fragment = ((MapFragment) getFragmentManager().findFragmentById(R.id.map));
FragmentTransaction fragTran = getActivity().getFragmentManager().beginTransaction();
ft.remove(fragment);
ft.commit();
}catch(Exception e){
}
}
Related
I show animation when user navigates away from fragment. For that I am using setCustomAndimations of support package.
"popEnter" and "popExit" work fine, but they are lost after activity gets rotated,
i.e. after rotation popping fragment happens without the animation.
Fragment creation in activity:
#Override
protected void onCreate(Bundle savedInstanceState) {
...
if (savedInstanceState == null) { // activity started for the first time, no fragment attached yet
fragment = MyFragment.newInstance(params);
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.setCustomAnimations(0, 0, // enter animations, not important here
// when popping fragment -> these are lost on rotation
R.anim.slide_in_right, R.anim.slide_out_right);
ft.add(R.id.content, fragment, MY_TAG).addToBackStack(null).commit();
}
}
Is there way / workaround to keep animating "popping out" of fragment after rotation ?
I found a temporarily solution for this problem here (answer #3)
fix this by adding:
android:configChanges="orientation|screenSize"
to your FragmentActivity in the manifest.
There are of course problems, when you i.e. inflate different layouts in your onCreateView, depending on the screen size. So thats also not a final answer.
Edit: you can create your own backstack:
public final class MBackStack {
public static Stack<Fragment> fragStack = new Stack<>();
private MBackStack(){}
public static void addFragment(Fragment frag){
fragStack.push(frag);
}
public static Fragment getFragment(){
if (fragStack.isEmpty()) {
return null;
}
fragStack.pop();
Fragment fragment = fragStack.peek();
return fragment;
}
public static int getStackSize(){
return fragStack.size();
}
public static void clearStack(){
while (fragStack.size()!=0){
fragStack.pop();
}
}
}
Now instead of
ft.addToBackStack(null);
You can add:
YOURFRAGMENT yf = new YOURFRAGMENT();
MBackStack.addFragment(yf);
ft = getSupportFragmentManager().beginTransaction();
ft.replace(R.id.content_frame, dts);
ft.commit();
And in your main activity you can override your onbackpressed:
#Override
public void onBackPressed() {
if(MBackStack.getStackSize()>1){
ft = getSupportFragmentManager().beginTransaction();
ft.setCustomAnimations(R.anim.in_left, R.anim.out_right);
ft.replace(R.id.content_frame, MBackStack.getFragment());
ft.commit();
}else{
finish();
overridePendingTransition(R.anim.no_animation, R.anim.slide_bottom_out);
}
}
I have tried it, and it works fine.
I am working on the following tutorial, it has problems : it recreates the fragments after each screen rotation.
I fixed it concerning the TitlesFragment class by adding if(savedInstanceState == null) in QuoteViewerActivity:
mFragmentManager = getFragmentManager();
//ADDED THIS CONDITION
if(savedInstanceState == null){
FragmentTransaction fragmentTransaction = mFragmentManager
.beginTransaction();
fragmentTransaction.add(R.id.title_fragment_container, mTitlesFragment);
fragmentTransaction.commit();
}
it fixed it for Fragment TitlesFragment however for Fragment QuoteFragment it is still recreating it on each screen orientation change because in this tutorial that fragment is created in an onclick event:
#Override
public void onListSelection(int index) {
if (!mDetailsFragment.isAdded()) {
FragmentTransaction fragmentTransaction = mFragmentManager
.beginTransaction();
fragmentTransaction.add(R.id.quote_fragment_container, mDetailsFragment);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
mFragmentManager.executePendingTransactions();
}
if (mDetailsFragment.getShownIndex() != index) {
mDetailsFragment.showIndex(index);
}
}
note that setRetainInstance(true) is set in both fragment's onCreate().
I tried to add this checking but it didn't fix it:
#Override
public void onListSelection(int index) {
//ADDED THE FOLLOWING TWO LINES
Fragment f = mFragmentManager.findFragmentById(R.id.quote_fragment_container);
if(f == null)
//===============================
if (!mDetailsFragment.isAdded()) {
FragmentTransaction fragmentTransaction = mFragmentManager
.beginTransaction();
fragmentTransaction.add(R.id.quote_fragment_container, mDetailsFragment);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
mFragmentManager.executePendingTransactions();
}
if (mDetailsFragment.getShownIndex() != index) {
mDetailsFragment.showIndex(index);
}
}
==> It recreates this Fragment each time I rotate the screen and duplicates existing menus (explained in this snapshot):
What am I doing wrong and what is the best practice to fix this? thanks!
This line won't find the fragment you're trying to find:
Fragment f = mFragmentManager.findFragmentById(R.id.quote_fragment_container);
You provided container (layout) id. This method can be used to find fragments that were inflated from XML layout.
If you want to manage fragments from code, use tag. Add a fragment using FragmentTransaction.add(int containerViewId, Fragment fragment, String tag). Providing a tag you can later find that fragment using FragmentManager.findFragmentByTag(String tag). It's a good idea to make tag some kind of static final String constant, making automatic refactoring a breeze.
You may be also interested with method FragmentTransaction.replace(int containerViewId, Fragment fragment, String tag) - it makes fragment replacement easier.
Solution:
In this specific tutorial the solution for this problem was solved by:
using onSaveInstanceState to store QuoteFragment state to its containing activity
Getting/handling the QuoteFragment by checking if it is found in the savedInstanceState
Here is what I added/changed in the code:
.....
private QuoteFragment mDetailsFragment = new QuoteFragment();//REMOVED final attribute
......
//ADDED
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if(mFragmentManager.findFragmentByTag(quote_fragment_tag)!=null)
getFragmentManager().putFragment(outState, QuoteFragment.class.getName(), mDetailsFragment);
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
TitleArray = getResources().getStringArray(R.array.Titles);
QuoteArray = getResources().getStringArray(R.array.Quotes);
setContentView(R.layout.main);
mFragmentManager = getFragmentManager();
// ADDED
if(savedInstanceState == null){
FragmentTransaction fragmentTransaction = mFragmentManager
.beginTransaction();
fragmentTransaction.add(R.id.title_fragment_container, mTitlesFragment);
fragmentTransaction.commit();
}
else{//ADDED
mDetailsFragment = (QuoteFragment) getFragmentManager()
.getFragment(savedInstanceState, QuoteFragment.class.getName());
if(mDetailsFragment == null){
mDetailsFragment = new QuoteFragment();
mFragmentManager.beginTransaction()
.add(R.id.quote_fragment_container, mDetailsFragment,quote_fragment_tag)
.addToBackStack(null)
.commit();
mFragmentManager.executePendingTransactions();
}
}
}
Note: in my humble opinion for best practices concerning fragments and config changes on runtime check Google's official tutorial.
Another simple solution.Add
menu.clear()
before inflating the menu in onCreateOptionsMenu method inside the fragment
#Override
public void onCreateOptionsMenu(Menu menu,MenuInflater inflater) {
menu.clear();
inflater.inflate(R.menu.menu_main, menu);
}
I am developing application which contains 2 fragments and i want to show hide according to my need. Following code has simple example of my problem.
This simple Fragmentactivity contains 1 button and one listfragment.
This simple example works flawless. but i am not satisfied with show hide fragment. If you remove layout.setVisibility(View.GONE); from the code then ft.hide(f); will not hide fragment. In fact we are not hiding fragment we are hiding container.
My Question is, IS this a way to show hide fragments? If not then please explain with tested example How to hide and show Fragments because lots of people are facing this problem.
public class MainActivity extends FragmentActivity implements OnClickListener {
Fragment1 f;
Button b;
LinearLayout layout;
Fragment myf;
#Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
b = (Button) findViewById(R.id.button1);
layout = (LinearLayout) findViewById(R.id.ll);
f = new Fragment1();
}
#Override
public void onClick(View v) {
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.setCustomAnimations(android.R.animator.fade_in, android.R.animator.fade_out);
if (f.isHidden()) {
ft.show(f);
layout.setVisibility(View.VISIBLE);
b.setText("Hide");
} else {
ft.hide(f);
b.setText("Show");
layout.setVisibility(View.GONE);
}
ft.commit();
// TODO Auto-generated method stub
}
Don't mess with the visibility flags of the container - FragmentTransaction.hide/show does that internally for you.
So the correct way to do this is:
FragmentManager fm = getFragmentManager();
fm.beginTransaction()
.setCustomAnimations(android.R.animator.fade_in, android.R.animator.fade_out)
.show(somefrag)
.commit();
OR if you are using android.support.v4.app.Fragment
FragmentManager fm = getSupportFragmentManager();
fm.beginTransaction()
.setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out)
.show(somefrag)
.commit();
In addittion, you can do in a Fragment (for example when getting server data failed):
getView().setVisibility(View.GONE);
Hi you do it by using this approach, all fragments will remain in the container once added initially and then we are simply revealing the desired fragment and hiding the others within the container.
// Within an activity
private FragmentA fragmentA;
private FragmentB fragmentB;
private FragmentC fragmentC;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (savedInstanceState == null) {
fragmentA = FragmentA.newInstance("foo");
fragmentB = FragmentB.newInstance("bar");
fragmentC = FragmentC.newInstance("baz");
}
}
// Replace the switch method
protected void displayFragmentA() {
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
if (fragmentA.isAdded()) { // if the fragment is already in container
ft.show(fragmentA);
} else { // fragment needs to be added to frame container
ft.add(R.id.flContainer, fragmentA, "A");
}
// Hide fragment B
if (fragmentB.isAdded()) { ft.hide(fragmentB); }
// Hide fragment C
if (fragmentC.isAdded()) { ft.hide(fragmentC); }
// Commit changes
ft.commit();
}
Please see https://github.com/codepath/android_guides/wiki/Creating-and-Using-Fragments for more info. I hope I get to help anyone. Even if it this is an old question.
public void showHideFragment(final Fragment fragment){
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.setCustomAnimations(android.R.animator.fade_in,
android.R.animator.fade_out);
if (fragment.isHidden()) {
ft.show(fragment);
Log.d("hidden","Show");
} else {
ft.hide(fragment);
Log.d("Shown","Hide");
}
ft.commit();
}
Try this:
MapFragment mapFragment = (MapFragment)getFragmentManager().findFragmentById(R.id.mapview);
mapFragment.getView().setVisibility(View.GONE);
I may be way way too late but it could help someone in the future.
This answer is a modification to mangu23 answer
I only added a for loop to avoid repetition and to easily add more fragments without boilerplate code.
We first need a list of the fragments that should be displayed
public class MainActivity extends AppCompatActivity{
//...
List<Fragment> fragmentList = new ArrayList<>();
}
Then we need to fill it with our fragments
#Override
protected void onCreate(Bundle savedInstanceState) {
//...
HomeFragment homeFragment = new HomeFragment();
MessagesFragment messagesFragment = new MessagesFragment();
UserFragment userFragment = new UserFragment();
FavoriteFragment favoriteFragment = new FavoriteFragment();
MapFragment mapFragment = new MapFragment();
fragmentList.add(homeFragment);
fragmentList.add(messagesFragment);
fragmentList.add(userFragment);
fragmentList.add(favoriteFragment);
fragmentList.add(mapFragment);
}
And we need a way to know which fragment were selected from the list, so we need getFragmentIndex function
private int getFragmentIndex(Fragment fragment) {
int index = -1;
for (int i = 0; i < fragmentList.size(); i++) {
if (fragment.hashCode() == fragmentList.get(i).hashCode()){
return i;
}
}
return index;
}
And finally, the displayFragment method will like this:
private void displayFragment(Fragment fragment) {
int index = getFragmentIndex(fragment);
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
if (fragment.isAdded()) { // if the fragment is already in container
transaction.show(fragment);
} else { // fragment needs to be added to frame container
transaction.add(R.id.placeholder, fragment);
}
// hiding the other fragments
for (int i = 0; i < fragmentList.size(); i++) {
if (fragmentList.get(i).isAdded() && i != index) {
transaction.hide(fragmentList.get(i));
}
}
transaction.commit();
}
In this way, we can call displayFragment(homeFragment) for example.
This will automatically show the HomeFragment and hide any other fragment in the list.
This solution allows you to append more fragments to the fragmentList without having to repeat the if statements in the old displayFragment version.
I hope someone will find this useful.
From my code, comparing to above solution, the simplest way is to define a layout which contains the fragment, then you could hide or unhide the fragment by controlling the layout attribute which is align with the general way of view. No additional code needed in this case and the additional deployment attributes of the fragment could be moved to the outer layout.
<LinearLayout style="#style/StHorizontalLinearView"
>
<fragment
android:layout_width="match_parent"
android:layout_height="390dp"
android:layout_alignParentTop="true"
/>
</LinearLayout>
final Fragment fragment1 = new fragment1();
final Fragment fragment2 = new fragment2();
final FragmentManager fm = getSupportFragmentManager();
Fragment active = fragment1;
In onCreate, after setContentView, i hid two fragments and committed them to the fragment manager, but i didn't hide the first fragment that will serve as home.
fm.beginTransaction().add(R.id.main_container, fragment2, "2").hide(fragment2).commit();
fm.beginTransaction().add(R.id.main_container,fragment1, "1").commit();
#Override
public void onClick(View v) {
Fragment another = fragment1;
if(active==fragment1){
another = fragment2;
}
fm.beginTransaction().hide(active).show(another).commit();
active = another;
}
Ref : https://medium.com/#oluwabukunmi.aluko/bottom-navigation-view-with-fragments-a074bfd08711
This worked for me
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
if(tag.equalsIgnoreCase("dashboard")){
DashboardFragment dashboardFragment = (DashboardFragment)
fragmentManager.findFragmentByTag("dashboard");
if(dashboardFragment!=null) ft.show(dashboardFragment);
ShowcaseFragment showcaseFragment = (ShowcaseFragment)
fragmentManager.findFragmentByTag("showcase");
if(showcaseFragment!=null) ft.hide(showcaseFragment);
} else if(tag.equalsIgnoreCase("showcase")){
DashboardFragment dashboardFragment = (DashboardFragment)
fragmentManager.findFragmentByTag("dashboard");
if(dashboardFragment!=null) ft.hide(dashboardFragment);
ShowcaseFragment showcaseFragment = (ShowcaseFragment)
fragmentManager.findFragmentByTag("showcase");
if(showcaseFragment!=null) ft.show(showcaseFragment);
}
ft.commit();
the answers here are correct and i liked #Jyo the Whiff idea of a show and hide fragment implementation except the way he has it currently would hide the fragment on the first run so i added a slight change in that i added the isAdded check and show the fragment if its not already
public void showHideCardPreview(int id) {
FragmentManager fm = getSupportFragmentManager();
Bundle b = new Bundle();
b.putInt(Constants.CARD, id);
cardPreviewFragment.setArguments(b);
FragmentTransaction ft = fm.beginTransaction()
.setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out);
if (!cardPreviewFragment.isAdded()){
ft.add(R.id.full_screen_container, cardPreviewFragment);
ft.show(cardPreviewFragment);
} else {
if (cardPreviewFragment.isHidden()) {
Log.d(TAG,"++++++++++++++++++++ show");
ft.show(cardPreviewFragment);
} else {
Log.d(TAG,"++++++++++++++++++++ hide");
ft.hide(cardPreviewFragment);
}
}
ft.commit();
}
Im trying to load an new fragment when a method is called. This method creates a new fragment and "replaces" the other fragment:
private void showTestFragment(Fragment oldFragment, boolean addBackStack, BaseAdapter adapter, int position) {
Cursor cursor = (Cursor)adapter.getItem(position);
if(cursor != null){
int idx = cursor.getColumnIndexOrThrow(Episode._ID);
long rowId = cursor.getLong(idx);
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
if(oldFragment != null){
Log.i(TAG, "Removing the old fragment");
fragmentTransaction.remove(oldFragment);
}
TestFragment testFragment = new TestFragment();
testFragment.setId(rowId);
fragmentTransaction.add(android.R.id.content, testFragment);
if(addBackStack){
Log.i(TAG, "Added to the backstack");
fragmentTransaction.addToBackStack(TAG);
}
fragmentTransaction.commit();
Fragment f = getFragmentManager()
.findFragmentById(R.id.index);
Log.i(TAG, "after commit, frag is "+ f);
}
}
This works fine, until i go back. The last fragment, should be removed when i go back. Before i'm going to implement methods on the activities
public void onBackPressed(){}
to remove the last fragment, i want to know if i handle the fragment change correctly. It looks like i'm missing something here..
If you really want to replace the fragment then use replace() methode instead of doing a remove() and an add().
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(..............);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
Don't forget to do the addToBackStack(null) so your previous state will be added to the backstack allowing you to go back with the back button.
See also https://developer.android.com/reference/android/app/FragmentTransaction.html#replace(int, android.app.Fragment, java.lang.String) .
Another good source is http://developer.android.com/guide/components/fragments.html (search for replace() function).
Just remove first and the call super.onBackPressed
public void onBackPressed(){
// here remove code for your last fragment
super.onBackPressed();
}
//lets wait for systems back to finish
super.onBackPressed();
//here we believe a fragment was popped, so we need to remove the fragment from ourbackstack
if(fragmentBackStack.size()>0)
{
Log.d("custombackstack","before back: "+fragmentBackStack.size()+" current:"+fragmentBackStack.peek());
fragmentBackStack.pop();
}
//after popping is the size > 0, if so we set current fragment from the top of stack, otherwise we default to home fragment.
if(fragmentBackStack.size()>0)
{
Log.d("custombackstack","after back: "+fragmentBackStack.peek());
currentFragment = fragmentBackStack.peek();
}
else
{
//back stack empty
currentFragment = HOME_FRAGMENT;
}
I recently refactored a application and replaced a ViewFlipper for a FrameLayout on which I swap between Fragments.
Each time user request one of the views:
public void showLibraryOf(long publisherId) {
library = new DownloadLibraryFragment(id, viewFactory());
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.replace(R.id.container, library);
ft.commit();
library.setAdapterObserver(this);
}
public void showMyLibraryOf(long publisherId) {
myLibrary = new MyLibraryFragment(id, viewFactory());
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.replace(R.id.container, myLibrary);
ft.commit();
}
public void showHelp() {
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.replace(R.id.container, new HelpFragment());
ft.commit();
}
I create a new Fragment and replace the old one. Those being removed from screen get onDestroy called, but the memory consumed by the bitmaps I load on the screen does not get removed, so the application crashes after some swap between the fragments.
I also tried to remove references at onDestroy
#Override
public void onDestroyView() {
destroy();
super.onDestroyView();
adapter.clear();
adapter.clearObservers();
adapter.notifyDataSetChanged();
view.setAdapter(new ArrayAdapter<Journal>(getActivity(), 0));
adapter = null;
view = null;
}
But the memory keeps growing.
Anyone knows any solution? maybe reuse fragments? effectively destroy it? I'm listening.
I forget which stackoverflow question I originally pulled this code from, but one method that seems to work well is to override onAttachFragment of the FragmentActivity, and then store a WeakReference to each fragment passed in. Then, instead of using the replace method of a FragmentTransaction, you recycle all the fragments (as relevant for the case).
Here's an example of additional members and methods on a FragmentActivity that creates a default fragment in onCreate and responds to changes via onNewIntent:
private List<WeakReference<Fragment>> mFragments =
new ArrayList<WeakReference<Fragment>>();
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.add(R.id.fragment_container, MyFragment.newInstance("default"));
ft.commit();
}
#Override
protected void onNewIntent(Intent intent) {
setIntent(intent);
String section = intent.getStringExtra("section");
recycleFragments();
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.add(R.id.fragment_container, MyFragment.newInstance(section));
ft.commit();
}
#Override
public void onAttachFragment(Fragment fragment) {
mFragments.add(new WeakReference<Fragment>(fragment));
}
private void recycleFragments() {
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
for (WeakReference<Fragment> ref : mFragments) {
Fragment fragment = ref.get();
if (fragment != null) {
ft.remove(fragment);
}
}
ft.commit();
}
Now if you monitor the heap, you should notice it's not blowing up in size. This solution mostly comes into play when you have nested fragments containing bitmaps which for some reason don't seem to get recycled properly. I'd love a more elegant solution but this one works.