I am doing an Android application with several tabs, let say that
have 2 tabs. One tab is an "android.support.v4.app.Fragment" that
contains a few textfields and a button, and the other one is an
"android.app.Fragment" that at the same time is a MapFragment.
The thing is that the first tap calls another
"android.support.v4.app.Fragment" with cardview (also support) and
they can be navigate between them with some hierarchy.
Now if first a click in the map tap, later on click the other tab and
finally I click the button to go to the frame that contains
cardviews, I can see these them but in the background I see also the
MapFragment.
I don't know how to solve this. I have tried to use replace, remove,
add, popBackStack, ... In addition I had tried to delete from the
rootview the frame of the fragment, but nothing happened.
The fact is I am going to throw your laptop out the window.
Thanks in advance!
If you are using .addToBackStack("") than remove this. and for calling fragment every time user .replace(); method for all your fragment.
Well I found out what happened. It is due to work with two different fragment managers (FragmentManager and SupportFragmentManager).
Depend on the fragment type (support or normal) you want to remove or replace you have to use one or another fragment manager.
I have created an enum to know what kind of fragment is active (in order to remove) and what kind of fragment is going to start (in order to replace).
More or less I made a function like this:
private void manageFragment (FragmentEnum nextFragment) {
switch (activedFragment) {
case SUPPORT:
getSupportFragmentManager().beginTransaction().remove(fragmentSupport).commit();
break;
case NORMAL:
getFragmentManager().beginTransaction().remove(fragmentNormal).commit();
break;
}
activedFragment = nextFragment;
switch (nextFragment) {
case SUPPORT:
getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, fragmentSupport).commit();
break;
case NORMAL:
getFragmentManager().beginTransaction().replace(R.id.fragment_container, fragmentNormal).commit();
break;
}
}
Related
I have an app with a Home screen that has 2 fragments (for now) and a navigation drawer. Currently I load the fragment A (Explore) on startup and load fragment B when clicked. From then on, I show and hide fragments. It's faster than recreating fragments on every click and my fragment A takes some time to load.
I've noticed that when I go to fragment B and go to another activity (let's call it activity 2) from there and leave the app and wait for it to be killed (or do something crazy like change the device language), and then come back to the same activity, it's still there. When I press back to go back to fragment B, sometimes (50% of times) the fragment B is drawn over fragment A. On clicking fragment A in the drawer, fragment A appears fine, but on clicking fragment B, there's another instance of fragment A and on top of that fragment B.
I've spent more than 2 days on this problem and got nowhere.
Here's my code for selecting the fragment:
private void selectItem(int position, boolean addExploreFragment) {
Log.d(tag, "selectItem: " + position);
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
//add explore fragment - this is called on app startup, but also when the app is killed and resumed which results in 2 explore fragments
if (addExploreFragment){
fragmentTransaction.replace(R.id.content_frame, mExploreFragment, EXPLORE_FRAGMENT_TAG);
Log.d(tag, "Replaced frame and added "+ mFragmentTags[position]);
} else {
//add fragment for the first time
if (getSupportFragmentManager().findFragmentByTag(mFragmentTags[position]) == null && position != 0) {
fragmentTransaction.add(R.id.content_frame, mFragments[position], mFragmentTags[position]);
Log.d(tag, "Added Fragment: "+ mFragmentTags[position]);
}
//shows and hides fragments
for (int i = 0; i < mFragments.length; i++) {
if (i == position) {
fragmentTransaction.show(mFragments[i]);
Log.d(tag, "Showing Fragment: "+ mFragmentTags[i]);
} else {
if (getSupportFragmentManager().findFragmentByTag(mFragmentTags[i]) != null) {
fragmentTransaction.hide(mFragments[i]);
Log.d(tag, "Hid Fragment: "+ mFragmentTags[i]);
}
}
}
}
fragmentTransaction.commit();
//not null check for calling selectItem(0) before loading the drawer
if (mDrawerList != null){
mDrawerList.setItemChecked(position, true);
}
}
I know for sure, the explore fragment is getting created twice and the two instances behave independently of each other (just sharing).
I'm lost what to do next. This is an issue which can be reproduced very easily on low end devices but on a device like Nexus 4 (my test device), the issue can be reproduced by changing the device language.
Has anyone got any ideas about this? Basically if the addExploreFragment block doesn't get called when there is already an exploreFragment, this issue could be solved, I think, but I've been unable to do so. Also, I tried removing all the fragments and then adding the exploreFragment but same thing happens (50% of times).
Thanks! and sorry for the long post, I felt I should share all the details.
Update: When I change the device language and come back to the app on Activity 2 and go back to Home activity, it has the fragment B open which is good, but fragment A get recreated because it's a heavy fragment and the system probably removed it from memory. Again, that's ok that it gets recreated IF it got removed by the system but why does it get recreated when it's not removed. I believe it's something with my code, on every 2nd attempt (without closing the app) this happens, 2 instances of the heavy fragment A. Out of ideas.
But shouldn't fragmentTransaction.replace remove all the previously added fragments and then add exploreFragment. It's not working like that. Neither fragment A nor Fragment B are getting removed.
I found out something new and rather odd to me. When you use fragmentTransaction.add, the listeners you have, like DrawerItemClickListener, on the previous fragment, are still active. And this is even if you use fragmentTransaction.commit.
So...I suspect when the add method is used, you actually clicked on another hidden button or hidden UI that has an event listener on the previous fragment. I don't like this of course and the effect may be very confusing. Yes, this happened to me and I didn't understand why for a while.
For now, I think the easiest code fix would be to use the replace method instead of add. The replace() makes listeners inactive. If it works, then you can make a better/elegant fix.
Let me know what happens....
I started to notice your post
when I go to fragment B and go to another activity
When you interact or start another Activity, you start a new set of Fragments. Look at this Google webpage # Fragments Lifecycle.
For clarification of my claim, there is a quote saying
A fragment must always be embedded in an activity and the fragment's
lifecycle is directly affected by the host activity's lifecycle.
You might as well read few paragraphs of it, at least.
I am not sure what your solution should be. Perhaps make the fragments distinctive, different and clear between the two Activities you have.
I have several fragments in my xml (4 of them). The first time I run the activity with this code:
private void loadSenderFragment(int sender_fragment) {
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
// TODO: Animation for later
if (loadRunOnce) {
//ft.setCustomAnimations(R.anim.fade_in, R.anim.fade_out, R.anim.fade_in, R.anim.fade_out);
}
switch (sender_fragment) {
case FILES_TO_SEND_FRAGMENT:
Log.i(StaticValues.TAG, "files to send fragment visisble");
ft.hide(fragmentSendDev);
ft.show(fragmentFilesSend);
break;
case SEND_TO_FRAGMENT:
Log.i(StaticValues.TAG, "hiding filesSend, loading senddev");
ft.hide(fragmentFilesSend);
ft.show(fragmentSendDev);
break;
}
if (loadRunOnce)
ft.addToBackStack(null);
else {
ft.hide(fragmentReceiveWait);
ft.hide(fragmentReceiving);
loadRunOnce = true;
}
ft.commit();
}
It shows the proper view (Files_to_send_fragment), then from that fragment via a callback I call loadSenderFragment again except this time with the case of SEND_TO_FRAGMENT.
I know that this gets called because my log : hiding fileSend, loading senddev shows up on my logcat and the ft.addToBackStack works as well because pressing theback button does not cancel the activity this is in . But the layout from fragmentFilesSend keeps showing while the other does not not(a page with a white background currently).
From what I understand from my code and my intention is, hide filessend and show senddev.
Does anyone know why that hide/show might not be working.
BTW I am using the compatibility library. (also tried the regular api Honeycomb+ library and still nothing).
I have figured out why this happens. For some reason if you use a style that has no window background, even though you hide/show different fragments all of them will be drawn (for some reason I have no idea why). So my style which I added a
<item name="windowBackground">#null</item>
in order to reduce how many pixels are drawn, was the thing that was blocking me. Who knew.
You have to add these fragments to the View they are meant to be displayed in before calling hide() and show() functions. Just initialize them beforehand.
I have the default Android project layout in Eclipse. The current one, that comes with a "dummyText" and a switcher on top. (I selected that one during the wizard.)
I want to use the top select bar to switch screens. Between Main, and Settings, and Result.
How do I detect the current activity?
Because.. if I have a switch, like:
switch (getArguments().getInt(ARG_SECTION_NUMBER)) { ... }
It will get into an infinite loop if the current screen is the selected one on the top.
(E.g.: Value 1 = Main screen. And you open the application, and it's value 1. And it's on main screen. It will indefinitely open up the main screen again and again. If you select an other value, like 2, it will go to the proper screen, and it won't loop.)
How am I supposed to fix this?
(I'm opening the other Activity with a new Intent, and then I call startActivityForResult(...).
Update #1:
The switch went into the "DummySectionFragment", which gets created at the onNavigationItemSelected.
Which looks like this:
Fragment fragment = new DummySectionFragment();
Bundle args = new Bundle();
args.putInt(DummySectionFragment.ARG_SECTION_NUMBER, position + 1);
fragment.setArguments(args);
getSupportFragmentManager().beginTransaction()
.replace(R.id.container, fragment).commit();
So basically that looks right... to me. The switch is activated, the corresponding value gets sent to Dummy, and a switch could just work to launch the proper Activity. I just need to write an if statement, that IF the current Activity matches the "to-be-invoked" one, the app should do nothing.
How am I supposed to implement this?
(I know the code is a little messy, blame Google for it's sample.)
In case you are not familiar with the Google example/code I used, here it is:
https://gist.github.com/anonymous/4edaefa42dd1be96e6e4
It's the "Blank Activity" -> "Dropdown" one.
I think you'r not using the sample as intended. One way would be to put the switch in the onNavigationItemSelected and within it launch the correct fragment (instead of DummySectionFragment) according to the selected item.
So this sample is built on Fragments, you should use them for the different sections, instead of launching a new activity.
The other way would be to have the DummySectionFragment use the ARG_SECTION_NUMBER to decide which layout to inflate, and inflate different layouts for different sections. In any case launching a new activity per section is not the way this sample is supposed to work.
Edit: Here are good guides for working with fragments:
http://developer.android.com/training/basics/fragments/index.html
http://developer.android.com/guide/components/fragments.html
I need to refresh my activity. I have bunch of question regrading the same which advice me to finish current activity and restart the current activity. OR again provide value to each widget. To avoid transition I used this code
Intent intent = getIntent();
overridePendingTransition(0, 0);
intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
this.finish();
overridePendingTransition(0, 0);
startActivity(intent);
But in my case In my activity I have four tabs, and I need to refresh all four tabs.
There are few more problems regarding the same
1) With above code, if I am on other screen, I come back to this screen when above piece of code runs.
2) Activity sequence gets disturb.
3) Maintaining selected tab will also be a part of problem
Even if I try to refresh each tab seperatley, One of my tab have webview. how to refresh that as webview.loadData() can't be called unless there is view and since I am not on that tab there will no view.
What can be the ideal way to tackle this problem. Any help will be appreciated.
It depends a bit on your use case. You may setContentView() again to inflate the layout every time. If you are just displaying a list, then you may just call the adapter to display the list. You have to do this for every fragment in your TabActivity.
To reload a WebView I would just call loadUrl() again.
As to remembering the selected tab, you have to store it and then set the current tab in the TabHost.
Question was not much complicated but solution what I wanted got to be optimised. Finally I took the second way, ie refreshing each component. This approach overcomes the problem of disturbing the sequence of activity. For web view I am still using webView.loadData(...).
In total I have handeled each tab separately. If I include my entire code then it would become clumsy, but still trying to incorporate as many important feature.
In my activity class when I need to refresh my activity, I called this method.
private void onRefrash()
{
refreshCurrentActivity();
int selectedTab = getActionBar().getSelectedNavigationIndex();
switch (selectedTab)
{
case TAB1:
fragment1.update();
break;
case TAB2:
fragment2.update();
break;
case TAB3:
fragment3.update();
break;
}
Toast.makeText(this, getString(R.string.msg_case_updates_received), Toast.LENGTH_LONG).show();
}
}
Each fragment is earlier initialised, and update method is called for each tab in which I am updating the corresponding web view.
Assume I have an Activity which contains two FrameLayouts (let's call them FrameA and FrameB) which in turn each contain a Fragment (let's call them FragmentA1 and FragmentB1 respectively). Now, I commit a series of individual fragment transactions using code similar to the following...
getFragmentManager()
.beginTransaction()
.replace(frameId, fragment)
.addToBackStack(null)
.commit();
... such that I replace FragmentA1 in FrameA with FragmentA2, then I replace FragmentB1 in FrameB with FragmentB2, then I replace FragmentA2 in FrameA with FragmentA3, then I replace FragmentB2 in Frame2 with FragmentB3, and the final state looks like the picture above (where only FragmentA3 and FragmentB3 are visible).
If I understood correctly how the back stack works, pressing 'back' will interleave popping of the Fragments between FrameA and FrameB (reflecting how I added them).
Does anyone know if it is possible to pop the last transaction on FrameA or FrameB selectively? (i.e. if I pressed 'Pop FrameA' then FrameA would be transitioned back from FragmentA3 to FragmentA2 and, instead, if I pressed 'Pop FrameB' then FrameB would be transitioned back from FragmentB3 to FragmentB2)
Supplement: I know I can get the Fragment last added to a given FrameLayout using the FragmentManager.findFragmentById(int framelayoutId) method, but calling FragmentTransaction.remove(fragment).commit() only removes the Fragment from the View and does not transition the View back to the Fragment it previously displayed.
Basically, no, there is only one back stack for an activity.
You will just need to implement your own separate back stacks.
As of Android 4.0 (and the associated support library) there are APIs that should make this relatively easy -- FragmentTransaction.detach(Fragment) lets you put a fragment into the same state it is when in the back stack, and FragmentManager.saveFragmentInstanceState(Fragment) lets you go further and completely throw away the Fragment object. Not coincidentally, these are used to implement ViewPager's FragmentPagerAdapter and FragmentStatePagerAdapter, respectively, so you could look at the code for these as an example of how to use them.
FragmentManager.popBackStack(String name, FragmentManager.POP_BACK_STACK_INCLUSIVE)
Here is the simplest answer, and the explanation is very clear: Well there are a few ways to go about this depending on the intended behavior, but this link should give you all the best solutions and not surprisingly is from Dianne Hackborn...