In my activity I have 2 fragments of a same type but different instances and I want to show one and hide the other when different button is pressed.
Here is the code for adding fragments to the activity:
_pardisCinemaFragment = NewsListingFragment.newInstance(TAB_CINEMA);
_shoppingFragment = NewsListingFragment.newInstance(TAB_SHOPPING);
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.setCustomAnimations(
R.anim.enter_from_left,
R.anim.exit_to_left,
R.anim.enter_from_left,
R.anim.exit_to_left);
transaction.addToBackStack(null);
transaction.add(R.id.news_list_fragment_container, _pardisCinemaFragment);
transaction.add(R.id.news_list_fragment_container, _shoppingFragment);
transaction.commit();
and here's the onclick event:
public void onTabClick(View view) {
String tag = view.getTag().toString();
Fragment fragmentToShow = null;
Fragment fragmentToHide = null;
if(tag.equals(TAB_CINEMA) && !currentTab.equals(TAB_CINEMA)) {
currentTab = TAB_CINEMA;
fragmentToShow = _pardisCinemaFragment;
fragmentToHide = _shoppingFragment;
}
else if(tag.equals(TAB_SHOPPING) && !currentTab.equals(TAB_SHOPPING)) {
currentTab = TAB_SHOPPING;
fragmentToShow = _shoppingFragment;
fragmentToHide = _pardisCinemaFragment;
}
if(fragmentToHide != null && fragmentToShow != null) {
getFragmentManager().beginTransaction()
.setCustomAnimations(android.R.animator.fade_in, android.R.animator.fade_out)
.hide(fragmentToHide)
.show(fragmentToShow)
.commit();
}
}
but nothing happens and it doesn't hide then show the fragments. is it because of the way they are added?
I understand that if one of the fragments are supposed to be hidden at first, there is no need to add it until the user presses the button but since some data has to be populated into the fragment view from phone's database I want it to be happening at the back while it's not visible.
Related
I have a bottom navigation bar with 4 menu items, I use FragmentTransaction show() , hide() methods to switch between fragments on tabs click , hide current fragment and show next fragment,
Before using show or hide method I must add fragment first and then use show or hide , until now everything work fine and fragment show with it's final state that's what I want.
But last added fragment with FragmentTransaction add() method is always active and it's view still clickable and switched fragment not clickable or touched.
I don't know why this happen .. any advice ?!!
This my code
private void changeFragment(Fragment fragment) {
if (fragment == null || fragment == currentFragment) return;
FragmentTransaction fragTransaction = getSupportFragmentManager().beginTransaction();
fragTransaction.setReorderingAllowed(true);
fragTransaction.setCustomAnimations(
R.anim.fade_in_transaction,
R.anim.fade_out_transaction
);
if (currentFragment != null && !currentFragment.isHidden()) {
fragTransaction.hide(currentFragment);
}
Fragment savedFragment = getSupportFragmentManager()
.findFragmentByTag(fragment.getClass().getSimpleName());
if (savedFragment == null) {
fragTransaction.add(R.id.container, fragment
, fragment.getClass().getSimpleName());
this.currentFragment = fragment;
} else {
if (savedFragment.isHidden()) {
fragTransaction.show(savedFragment);
}
this.currentFragment = savedFragment;
}
if (getSupportFragmentManager().getPrimaryNavigationFragment() == null) {
if (fragment instanceof FragmentHome) {
fragTransaction.setPrimaryNavigationFragment(fragment);
}
}
fragTransaction.commit();
}
For my application, some fragments require the user to enter a pin in a dialog before it is shown. I have a BaseFragment class that all my other fragments extend which stores if the pin is required. My issue right now is dealing with the back button as if the user tries to go back to a fragment that requires the pin it needs to show a pin dialog first.
Currently I'm overriding onBackPressed() in which I want to peek the backstack to see what fragment will be resumed if popBackstack() was called so I can check if the pin dialog should be shown. Below is my current code which popFragment is coming back null:
#Override
public void onBackPressed() {
if(getSupportFragmentManager().findFragmentById(R.id.fragment_container) instanceof BaseFragment) {
BaseFragment frag = (BaseFragment) getSupportFragmentManager().findFragmentById(R.id.fragment_container);
if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
int index = getSupportFragmentManager().getBackStackEntryCount() - 1;
Log.e("Back stack", Integer.toString(index));
FragmentManager.BackStackEntry backEntry = (FragmentManager.BackStackEntry) getSupportFragmentManager().getBackStackEntryAt(index);
BaseFragment popFragment = (BaseFragment) getSupportFragmentManager().findFragmentById(backEntry.getId());
BaseFragment.checkAuth(this, frag, popFragment, new NavigationCallback((AppCompatActivity) this));
} else {
super.onBackPressed();
}
} else {
super.onBackPressed();
}
}
I've checked if backEntry is null and its not. backEntry.getId() also comes back with a value of 1 so I'm not sure why findFragmentById() is then null. I also dont think it has to do with how I'm showing fragments but here is my code for that as well:
FragmentManager fragManager = mActivity.getSupportFragmentManager();
FragmentTransaction transaction = fragManager.beginTransaction();
transaction.replace(R.id.fragment_container, mFragment);
transaction.addToBackStack(null);
transaction.commit();
The BackStackEntry id and Fragment id have nothing to do with each other.
I have done something similar to what you are doing by using tags. Again, the BackStackEntry tag and Fragment tags have nothing to do with each other; however, by using the same value for both tags you can accomplish your goal.
String tag = ...
FragmentManager fragManager = mActivity.getSupportFragmentManager();
FragmentTransaction transaction = fragManager.beginTransaction();
// tag is associated with fragment
transaction.replace(R.id.fragment_container, mFragment, tag);
// same tag is associated with back stack entry
transaction.addToBackStack(tag);
transaction.commit();
...
int count = fragmentManager.getBackStackEntryCount();
if (count > 0) {
FragmentManager.BackStackEntry topBackStackEntry =
fragmentManager.getBackStackEntryAt(count - 1);
String tag = topBackStackEntry.getName();
if (tag != null) {
Fragment fragment = fragmentManager.findFragmentByTag(tag);
if (fragment != null) {
// we found the fragment
// you can also sanity-check here with fragment.isResumed()
}
}
}
You are gone Frag-A -> Frag-B -> Frag-C. Now you want to go back to Frag-A without creating a new Fragment view for Frag-A ?
Here is the solution:
Navigation.findNavController(requireView()).popBackStack(R.id.Frag_A, false)
Also delete your nav entry action_Frag_C_to_Frag_A. If you have accidentely created a navgraph.
I'm working on an android application, that uses a navigation drawer to switch between two fragments. However, each time I switch, the fragment is completely recreated.
Here is the code from my main activity.
/* The click listener for ListView in the navigation drawer */
private class DrawerItemClickListener implements ListView.OnItemClickListener {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
selectItem(position);
}
}
private void selectItem(int position) {
android.support.v4.app.Fragment fragment;
String tag;
android.support.v4.app.FragmentManager; fragmentManager = getSupportFragmentManager();
switch(position) {
case 0:
if(fragmentManager.findFragmentByTag("one") != null) {
fragment = fragmentManager.findFragmentByTag("one");
} else {
fragment = new OneFragment();
}
tag = "one";
break;
case 1:
if(fragmentManager.findFragmentByTag("two") != null) {
fragment = fragmentManager.findFragmentByTag("two");
} else {
fragment = new TwoFragment();
}
tag = "two";
break;
}
fragment.setRetainInstance(true);
fragmentManager.beginTransaction().replace(R.id.container, fragment, tag).commit();
// update selected item and title, then close the drawer
mDrawerList.setItemChecked(position, true);
setTitle(mNavTitles[position]);
mDrawerLayout.closeDrawer(mDrawerList);
}
I've set up some debug logging, and every time selectItem is called, one fragment is destroyed, while the other is created.
Is there any way to prevent the fragments from being recreated, and just reuse them instead?
After #meredrica pointed out that replace() destroys the fragments, I went back through the FragmentManager documentation. This is the solution I've come up with, that seems to be working.
/* The click listener for ListView in the navigation drawer */
private class DrawerItemClickListener implements ListView.OnItemClickListener {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
selectItem(position);
}
}
private void selectItem(int position) {
android.support.v4.app.FragmentManager; fragmentManager = getSupportFragmentManager();
switch(position) {
case 0:
if(fragmentManager.findFragmentByTag("one") != null) {
//if the fragment exists, show it.
fragmentManager.beginTransaction().show(fragmentManager.findFragmentByTag("one")).commit();
} else {
//if the fragment does not exist, add it to fragment manager.
fragmentManager.beginTransaction().add(R.id.container, new OneFragment(), "one").commit();
}
if(fragmentManager.findFragmentByTag("two") != null){
//if the other fragment is visible, hide it.
fragmentManager.beginTransaction().hide(fragmentManager.findFragmentByTag("two")).commit();
}
break;
case 1:
if(fragmentManager.findFragmentByTag("two") != null) {
//if the fragment exists, show it.
fragmentManager.beginTransaction().show(fragmentManager.findFragmentByTag("two")).commit();
} else {
//if the fragment does not exist, add it to fragment manager.
fragmentManager.beginTransaction().add(R.id.container, new TwoFragment(), "two").commit();
}
if(fragmentManager.findFragmentByTag("one") != null){
//if the other fragment is visible, hide it.
fragmentManager.beginTransaction().hide(fragmentManager.findFragmentByTag("one")).commit();
}
break;
}
// update selected item and title, then close the drawer
mDrawerList.setItemChecked(position, true);
setTitle(mNavTitles[position]);
mDrawerLayout.closeDrawer(mDrawerList);
}
I also added this bit, but I'm not sure if it's necessary or not.
#Override
public void onDestroy() {
super.onDestroy();
FragmentManager fragmentManager = getSupportFragmentManager();
if(fragmentManager.findFragmentByTag("one") != null){
fragmentManager.beginTransaction().remove(fragmentManager.findFragmentByTag("one")).commit();
}
if(fragmentManager.findFragmentByTag("two") != null){
fragmentManager.beginTransaction().remove(fragmentManager.findFragmentByTag("two")).commit();
}
}
Use the attach/detach method with tags:
Detach will destroy the view hirachy but keeps the state, like if on the backstack; this will let the "not-visible" fragment have a smaller memory footprint. But mind you that you need to correctly implement the fragment lifecycle (which you should do in the first place)
Detach the given fragment from the UI. This is the same state as when it is put on the back stack: the fragment is removed from the UI, however its state is still being actively managed by the fragment manager. When going into this state its view hierarchy is destroyed.
The first time you add the fragment
FragmentTransaction t = getSupportFragmentManager().beginTransaction();
t.add(android.R.id.content, new MyFragment(),MyFragment.class.getSimpleName());
t.commit();
then you detach it
FragmentTransaction t = getSupportFragmentManager().beginTransaction();
t.detach(MyFragment.class.getSimpleName());
t.commit();
and attach it again if switched back, state will be kept
FragmentTransaction t = getSupportFragmentManager().beginTransaction();
t.attach(getSupportFragmentManager().findFragmentByTag(MyFragment.class.getSimpleName()));
t.commit();
But you always have to check if the fragment was added yet, if not then add it, else just attach it:
if (getSupportFragmentManager().findFragmentByTag(MyFragment.class.getSimpleName()) == null) {
FragmentTransaction t = getSupportFragmentManager().beginTransaction();
t.add(android.R.id.content, new MyFragment(), MyFragment.class.getSimpleName());
t.commit();
} else {
FragmentTransaction t = getSupportFragmentManager().beginTransaction();
t.attach(getSupportFragmentManager().findFragmentByTag(MyFragment.class.getSimpleName()));
t.commit();
}
The replace method destroys your fragments. One workaround is to set them to Visibility.GONE, another (less easy) method is to hold them in a variable. If you do that, make sure you don't leak memory left and right.
I did this before like this:
if (mPrevFrag != fragment) {
// Change
FragmentTransaction ft = fragmentManager.beginTransaction();
if (mPrevFrag != null){
ft.hide(mPrevFrag);
}
ft.show(fragment);
ft.commit();
mPrevFrag = fragment;
}
(you will need to track your pervious fragment in this solution)
I guess you can not directly manipulate the lifecycle mechanisms of your Fragments. The very fact that you can findFragmentByTag is not very bad. It means that the Fragment object is not recreated fully, if it is already commited. The existing Fragment just passes all the lifecycle steps each Fragment has - that means that only UI is "recreated".
It is a very convenient and useful memory management strategy - and appropriate, in most cases. Fragment which is gone, has the resources which have to be utilized in order to de-allocate memory.
If you just cease using this strategy, the memory usage of your application could increase badly.
Nonetheless, there are retained fragments, which lifecycle is a bit different and do not correspond to the Activity they are attached to. Typically, they are used to retain some things you want to save, for example, to manage configuration changes
However, the fragment [re]creation strategy depends on the context - that is, what you would like to solve, and what are the trade-offs that you are willing to accept.
Just find the current fragment calling getFragmentById("id of your container") and then hide it and show needed fragment.
private void openFragment(Fragment fragment, String tag) {
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
Fragment existingFragment = fragmentManager.findFragmentByTag(tag);
if (existingFragment != null) {
Fragment currentFragment = fragmentManager.findFragmentById(R.id.container);
fragmentTransaction.hide(currentFragment);
fragmentTransaction.show(existingFragment);
}
else {
fragmentTransaction.add(R.id.container, fragment, tag);
}
fragmentTransaction.commit();
}
Same idea as Tester101 but this is what I ended up using.
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
Fragment oldFragment = fragmentManager.findFragmentByTag( "" + m_lastDrawerSelectPosition );
if ( oldFragment != null )
fragmentTransaction.hide( oldFragment );
Fragment newFragment = fragmentManager.findFragmentByTag( "" + position );
if ( newFragment == null )
{
newFragment = getFragment( position );
fragmentTransaction.add( R.id.home_content_frame, newFragment, "" + position );
}
fragmentTransaction.show( newFragment );
fragmentTransaction.commit();
Hide easily in kotlin using extensions:
fun FragmentManager.present(newFragment: Fragment, lastFragment: Fragment? = null, containerId: Int) {
if (lastFragment == newFragment) return
val transaction = beginTransaction()
if (lastFragment != null && findFragmentByTag(lastFragment.getTagg()) != null) {
transaction.hide(lastFragment)
}
val existingFragment = findFragmentByTag(newFragment.getTagg())
if (existingFragment != null) {
transaction.show(existingFragment).commit()
} else {
transaction.add(containerId, newFragment, newFragment.getTagg()).commit()
}
}
fun Fragment.getTagg(): String = this::class.java.simpleName
Usage
supportFragmentManager.present(fragment, lastFragment, R.id.fragmentPlaceHolder)
lastFragment = fragment
Here's what I'm using for a simple 2 fragment case in Kotlin:
private val advancedHome = HomeAdvancedFragment()
private val basicHome = HomeBasicFragment()
override fun onCreate(savedInstanceState: Bundle?) {
...
// Attach both fragments and hide one so we can swap out easily later
supportFragmentManager.commit {
setReorderingAllowed(true)
add(R.id.fragment_container_view, basicHome)
add(R.id.fragment_container_view, advancedHome)
hide(basicHome)
}
binding.displayModeToggle.onStateChanged {
when (it) {
0 -> swapFragments(advancedHome, basicHome)
1 -> swapFragments(basicHome, advancedHome)
}
}
...
}
With this FragmentActivity extension:
fun FragmentActivity.swapFragments(show: Fragment, hide: Fragment) {
supportFragmentManager.commit {
show(show)
hide(hide)
}
}
How about playing with the Visible attribute?
this is a little late response.
if you're using view pager for fragments, set the off screen page limit of the fragment to the number of fragments created.
mViewPager.setOffscreenPageLimit(3); // number of fragments here is 3
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();
}
I am having trouble figuring out the proper way to navigate through fragments without a pager and i am having problems during Configuration changes for screen orientation. I am using Show/Hide on the fragments to make them visible and functional but i am wondering if i should instead be using Detach/Attach. I am also having problems adding things to the back stack and i think it is also due to the use of show/hide. Is it better to use Attach/detatch or is there a way to override what the back button does to make it show/hide the last/current fragment.
The Behavior:
I have a map fragment and a List fragment along with a few others. everything starts up correctly and works initially with orientation changes. When i navigate to the list view it populates correctly but upon orientation change the list gets redrawn without the Data in it. The map view also gets redrawn and is visible behind my pager title indicator.
If anyone could please point me in right direction for solving this that would be awesome. I am suspecting that is is caused by the way that i am showing and hiding the fragments.
Here is where i create the Fragments and add them to the fragment manager. I have also shown where i show/hide fragments.
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_map_frags);
mapViewContainer = LayoutInflater.from(this)
.inflate(R.layout.map, null);
setupFragments();
showFragment(0);
}
public void setListData(String name) {
bName = name;
showFragment(1);
}
private void setupFragments() {
final FragmentManager fm = getSupportFragmentManager();
final FragmentTransaction ft = fm.beginTransaction();
mFragment1 = fm.findFragmentByTag("f1");
if (mFragment1 == null) {
mFragment1 = new MenuFragment();
ft.add(mFragment1, "f1");
ft.hide(mFragment1);
}
mMapFragment = (MapFragment) getSupportFragmentManager()
.findFragmentByTag(MapFragment.TAG);
if (mMapFragment == null) {
mMapFragment = MapFragment.newInstance(0);
ft.add(R.id.fragment_container, mMapFragment, MapFragment.TAG);
}
ft.hide(mMapFragment);
myListFragment = (ListFrag) getSupportFragmentManager()
.findFragmentByTag(ListFrag.TAG);
if (myListFragment == null) {
myListFragment = new ListFrag();
ft.add(R.id.fragment_container, myListFragment, ListFrag.TAG);
}
ft.hide(myListFragment);
frag = (frag) getSupportFragmentManager().findFragmentByTag(
frag.TAG);
if (frag == null) {
bacFrag = new frag();
ft.add(R.id.fragment_container, frag, frag.TAG);
}
ft.hide(bacFrag);
ft.commit();
}
public void showFragment(int fragIn) {
final FragmentTransaction ft = getSupportFragmentManager()
.beginTransaction();
ft.setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out);
if (mVisible != null) {
if (mVisible == mListFragment) {
ft.remove(mListFragment);
} else {
ft.hide(mVisible);
}
}
switch (fragIn) {
case 0:
ft.show(mMapFragment);
ft.commit();
mVisible = mMapFragment;
break;
case 1:
mListFragment = (ListFragmentDisplay) getSupportFragmentManager()
.findFragmentByTag(ListFragmentDisplay.TAG);
Toast.makeText(this, "startListFrag", Toast.LENGTH_LONG).show();
if (mListFragment == null) {
mListFragment = new ListFragmentDisplay();
ft.add(R.id.fragment_container, mListFragment,
ListFragmentDisplay.TAG);
}
ft.show(mListFragment).commit();
mVisible = mListFragment;
break;
case 2:
ft.show(myfragment).commit();
mVisible = myfragment;
break;
case 3:
ft.show(frag).commit();
mVisible = frag;
break;
}
}
It's not your fault. The problem is that when the orientation changes all the Activity is Destroyed, even all the fragments added. So none of the data within it is retained.
It's not advised to use android:configChanges="orientation|keyboardHidden".
Rather, set for every fragment setRetainInstance(true) and it will work well with your current code.
If you want to have a better persistence (for example when the activity is temporarily destroyed for space issues) also remember to save the state of your fragments with onSaveInstanceState. setRetainInstance will work only when a configuration change is about to come.