So I am trying to integrate Google Maps into my application, I came across a concept that I don't entirely understand. I have seen that adding google maps into an app and it seems the most common way to do so is with an activity.
I found some websites and a SO question showing how to put Google Maps in a fragment, but would that be an issue if the user is constantly clicking on profiles and going back? Causing the map to be recreated or resumed constantly. Would that performance be better if the map was an activity instead?
Basically, Im not sure the best way to transition from an activity GUI to a fragment? I've had an app that only used 1 activity, I just used multiple different fragments changing them with this code
FragmentManager fm = getFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
Fragment fragment = getFragmentManager().findFragmentById(R.id.framecontainer);
if (fragment == null) {
ft.add(R.id.framecontainer, frag, tag);
} else {
ft.replace(R.id.framecontainer, frag, tag);
}
ft.addToBackStack(null);
ft.commit();
I am confused because when I created my main activity, I called
setContentView(R.layout.baselayout);
This layout contained only a frame container, I would then add in a HomeScreenFragment right away and when I needed to change, I would use the FragmentTransaction code above.
However, if my new MapActivity used setContentView(R.layout.maplayout); how would I best change screens? If R.id.maplayout does not contain a framelayout, is it best to start a new activity that uses many fragments like the one I mentioned before? I remember hearing that calling setContentView more than once or outside onCreate() is bad practice.
It seems I am missing something because so far it seems like there is 2 ways of using activities with different layouts
Starting a new activity every time and just trying to minimize the amount of activities.
Make an activity with a FrameLayout and just swap fragments everytime
To address my actual problem
I want my users to click another user marked on the map which will bring them to a profilelayout and view that user's profile, should I use one of the 2 methods above or how should I go about doing so?
Do you guys have any input to point me in the best direction? Thanks!
If your current application is already fragment heavy, have you considered using MapFragment? I think if you're cleaning up your objects/resources appropriately it shouldn't really be a performance issue.
Also according to the documentation it says the following about the MapFragment:
It's a wrapper around a view of a map to automatically handle the necessary life cycle needs.
I think it's also good to note that it's possible for you to do layout manipulation by adding/removing views using the LayoutInflater.
Related
I working on a app whitch has 1 activity with 2 buttons (Map and Listview). By clicking on the buttons the fragment into the FrameLayout needs to change from a listview to a mapview (or from map to listview).
I got this working and the fragments are show the data exacly as i want BUT...
I have a few problems by switching between the fragments. I struggled with the code and the best i got working is this:
FragmentManager fragmentManager;
Fragment listOverview;
AlertOverviewMapsFragment mapsOverview;
#Bind(R.id.fr_overviewContainer) FrameLayout fr_overviewContainer;
#OnClick(R.id.btn_list_overview) void openListOverview()
{
// Add the fragment to the 'fragment_container' FrameLayout
if (listOverview == null) { listOverview = new AlertOverviewListFragment();}
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fr_overviewContainer.removeAllViews();
fragmentTransaction.add(R.id.fr_overviewContainer, listOverview).commit();
}
#OnClick(R.id.btn_maps_overview) void openMapsOverview()
{
// Add the fragment to the 'fragment_container' FrameLayout
if (mapsOverview == null){ mapsOverview = new AlertOverviewMapsFragment();}
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fr_overviewContainer.removeAllViews();
fragmentTransaction.add(R.id.fr_overviewContainer, mapsOverview).commit();
}
The problem is when i click a second time on a button the app crashes and i get a java.lang.IllegalStateException: Fragment already added error. I understand that it means that i added the fragment to the manager (or transaction, dunno). I can create or find some workarounds for this but since this is nothing new i'am looking for the usual way to switch (replace) between the 2 fragments.
The fragments contains data which are loaded from the internet, it will be nice when the data is saved and loaded again by reloading the fragment. (so the user sees the old data when it is updated again in the background)
A CLEAR code example would be nice.....
i read something about the fragment manager and transactions and so as i understand the manager makes it posible to add, remove, delete......... fragments from a transaction? so i need 1 transaction per activity? or is this twisted? so the manager contains the fragments? this part is not clear and realy complicated for me. I'am happy when someone can help me with a solution for my problem and it would be nice when someone can explain how the transactions and the managers work in a realy SIMPLE way becouse i'am just started with programming Android apps.
Thanks for reading my problem and lot more thanks for the people who takes the time and patience for writing an answer!
I have recently started to take a look at developing apps for my android device. What started this interest for me is I was playing around with a few arduinos had the great idea to make them communicate with my phone, as say an interface for whatever values I am measuring on the arduino itself. Now I could take the easy way out and use a public source to accomplish this but there isn't as much to learn that way, and I would like it the way I want it.
Now I suppose the first question I need to ask is, would multiple fragments/single activity be the best way for me to accomplish this? Basically, I want 1 connection to the arduino, pull all the values, but depending on the "tab" I have selected I want certain values displayed certain ways. I decided to make each tab a different fragment and just display the values different ways. Like I said, I am just beginning android development experience so don't have much to base this choice off of.
So being fixated on this multiple fragment idea I have:
Created multiple Fragment.xml files
Defined a class for each separate view
Created a List to display available Fragments
Instantiated and displayed fragment when selected
So essentially my onMenuItemSelect looked like this.
FragmentTransaction FT = getFragmentManager.beginTransaction();
switch(position){
case 1:
FT.replace(R.id.fragment_container, new MyFragment()).commit();
break;
case 2:
FT.replace(R.id.fragment_container, new MySecondFragment()).commit();
break;
}
The above code worked, it did what I wanted it to without any issues. I don't really like this though, because for each and every Fragment I wanted to add I would need to add a new case to the switch. Also this instantiates a new fragment every time, even if one was already created. Is that a problem?
The biggest problem I had with it is that it isn't the easiest to scale. For 2-3 fragments this isn't the worst way to handle it (in my eyes). I want to be able to have as many fragments I want without an individual case for each one in the switch. So what I did was created a fragmentList to hold a single instance of each of my fragment.
List<Fragment> fragmentList;
private void populateFragmentList();{
fragmentList = new ArrayList<Fragment>();
fragmentList.add(new HomeFrag());
fragmentList.add(new BluetoothFragment());
fragmentList.add(new USBFragment());
fragmentList.add(new RCInfoFragment());
fragmentList.add(new ControllerFragment());
fragmentList.add(new FingerCordsFrag());
}
public void onMenuItemSelect(int position, int curPosition){
if(fragmentList.get(position).isAdded()){
getFragmentManager().beginTransaction().show(fragmentList.get(postition))
.hide(fragmentList.get(curPosition)).commit();
}
else
getFragmentManager().beginTransaction().add(R.id.fragment_container, fragmentList.get(position)).show(fragmentList.get(position)).hide(fragmentList.get(curPosition)).commit();
}
And this method also worked. I could get it to display all of my fragments, without have to re-instantiate each fragment each time. I believe this does what I am wanting it to do, it scales fairly well(better than a switch/case IMO). The problem that I have now is that it all goes crazy when I change orientation. Up until now I was only testing portrait mode, I am not able to view any of my fragments when I select them in other orientation. I can start it in either orientation, and it works, but when I change it during run-time, I am only able to see the one fragment I had open when I changed orientation.
Now, each fragments "onCreateView" is being called, it is just that the display isn't being shown. I believe I have it narrowed down to it isn't being attach to the new activity created from the orientation change. Is There anyway I can reattach fragments that are already created to a new activity.
In summary, the questions I have are:
Is this model even a good way for my application?
Is there a decent way to handle Fragments that scales well? Can't
seem to find any examples.
Is using a ''new MyFragment()'' each time I open a different tab a
reasonable way to achieve this?
Is my way of storing my Fragments in a list a reasonable way to
handle them?
How do I reattach a fragment to the new Activity after an
orientation change?
Thank you for your time.
*Had to type all this code on the fly because I, for some reason, couldn't get my C/P'd code to format correctly.
I believe it a good choice to use fragments and start with this example...
You should definitely override some "Adapter" to handle all the transactions more easily...
Check here for the orientation problem...
I'm interested in the best way to have a single activity that switches between two fragments.
I've read probably 15 Stack Overflow posts and 5 blogs posts on how to do this, and, while I think I cobbled together a solution, I'm not convinced it's the best one. So, I want to hear people's opinions on the right way to handle this, especially with regards to the lifecycle of the parent activity and the fragments.
Here is the situation in detail:
A parent activity that can display one of two possible fragments.
The two fragments have state that I would like to persist across a session, but does not necessarily need to be persisted between sessions.
A number of other activities, such that the parent activity and the fragments could get buried in the back stack and destroyed due to low memory.
I want the ability to use the back button to move between the fragments (So as I understand it, I can't use setRetainInstance).
In addition to general architecture advice, I have the following outstanding questions:
If the parent activity is destroyed due to low memory, how do I guarantee that the states of both fragments will be retained, as per this post: When a Fragment is replaced and put in the back stack (or removed) does it stay in memory?. Do I just need a pointer to each fragment in the parent activity?
What is the best way for the parent activity to keep track of which fragment it is currently displaying?
Thanks in advance!
I ended up adding both of the fragments using the support fragment manager and then using detach/attach to switch between them. I was able to use commitAllowingStateLoss() because I retain the state of the view elsewhere, and manually set the correct fragment in onResume().
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.add(R.id.my_layout, new AFragment(), TAG_A);
fragmentTransaction.add(R.id.my_layout, new BFragment(), TAG_B);
fragmentTransaction.commit();
}
public void onResume() {
super.onResume();
if (this.shouldShowA) {
switchToA();
} else {
switchToB();
}
}
private void switchToA() {
AFragment fragA = (AFragment) getSupportFragmentManager().findFragmentByTag(TAG_A);
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.detach(getSupportFragmentManager().findFragmentByTag(TAG_B));
fragmentTransaction.attach(fragA);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commitAllowingStateLoss();
getSupportFragmentManager().executePendingTransactions();
}
You might want to consider using a ViewPager in your parent Activity so you can switch between the Fragments.
So you would be able to swipe through them.
if you want to persist their state during a session even if the parent activity is destroyed, you need to make them Parcelable, so you can save the state even if the class isn't instantiated at that time. You also need to do this if your rotating your device and want to keep the current situation/data on the Screen.
You should write them to a Parcelable in their onPause methods and recreate them from it in the onResume one. This way it doesn't matter if they are destroyed or have to be recreated due to changes in the devices orientation.
if you want to be able to switch between those fragments with the Backbutton, you can catch the buttonClick for onBackPressed and handle it accordingly.
If you need to figure out what Fragment your displaying at a given time you ask your ViewPager what Fragment he is displaying at that time, so you don't have to keep track, you can just ask someone who knows, if you need to know it.
I'm looking for some advice on the best way to handle fragments which launch other fragments.
I'm converting an app which I started writing using a more Activity based approach and have since begun moving it over to using Fragments. I have some Fragments which used to launch a new Activity and I want to move them over to launching other Fragments in the same view that the current Fragment is residing.
For example - I have an Activity which has a WebView which uses a WebViewClient to handle internal js->java interactions. My WebViewClient can launch other Activities, which I used to do with :
i = new Intent(context, GoogleMapActivity.class);
startActivity(i);
This webview activity can either be fullscreen or in a view with a menu on the side, but I want the webview to respect the layout - so if the menu is present, it should stay present when launching new Fragments - I just don't know the best approach to writing the code which launches the Fragments.
So...is there a way, within a Fragment, of essentially telling a new Fragment to load in to the same space as the current Fragment or does there need to be some interaction with the Activity?
** EDIT **
Given that there are a few different layouts which could be used, I don't always know which id I should be targeting to put the fragment in - hence I need to know if there's a way to do this without knowing the id (as in the replace method for example).
getFragmentManager().beginTransaction()
.replace(((ViewGroup) getView().getParent()).getId(), fragment)
.addToBackStack(null)
.commit();
This should replace parent container with desired fragment.
That should be doable via FragementManager.replace(). Have a look at the documentation for Fragment and especially the longer example in the "Layout" section there.
If you want to add Fragment rather replace it, use:
getFragmentManager().beginTransaction().add(R.id.fragment_container, new Fragment()).commit();
This is a design question, rather than a technical one.
General case: I want an UI event in a Fragment to make Activity-wide changes.
Specific case: I have two fragments, hosted in the same activity. When the user clicks a button in one of those fragments, I want it to be replaced by the other.
I don't want, however, my Fragments touching my activity. I may want to change the behavior later (maybe, in a bigger screen, show both fragments instead of replacing the first), and I don't want my Fragment code to have that logic.
What I did was implement a Listener class in my fragments, that reports events back to the Activity. This way, if I want to use another Activity class with different display behavior, I can just change the listener and leave the Fragment code untouched.
Is this a good way to go about it? Is there a standard good practice, or a better design pattern?
Using listeners is the recommended way of communicating between Fragment and your activity.
See this Android documentatin section for infromation. Long story short they just implement a listener interface by the Activity class and cast getActivity() result in a fragment to a listener.
From my personal experience this is very convenient because lets you to:
Easilly switch underlying activity (e.g. you host entire fragment in a wrapper activity for compatibility in pre-3.0 and host this fragment along with others in 11+)
Easilly control if the wrapper activity supports callbacks or not. Just check is it does implement the listener and do your app specific actions if it doesn't.
You are right on about using a Listener. This is something I also had to deal with in a project at work. The best way to handle it is to make the Fragment stand-alone in nature. Anything wishing to interact with the Fragment should use its public API and/or set listeners for specific events. If you are familiar with Design Patterns, this is the Observer pattern. The events can be general or specific as well as contain data or no data.
As an example of my project, I had two Fragments. A ListFragment and an InfoFragment that displayed the selected ListItem. The ListFragment already has a Listener interface for my Activity to hook into, but the InfoFragment does not since its your basic Fragment. I added a Listener interface to the InfoFragment that would be notified when the Fragment wanted to close. For the Fragment, this could be by a button press, or specific action occured, but as far as my Activity is concerned, when the Event is triggered, it would close up the Fragment view.
Don't be afraid to use a lot of Listeners for Fragments, but also try to group them by a specific action using data parameters to individualize them. Hope this helps!
A technical answer for:
I have two fragments, hosted in the same activity. When the user clicks a button in one of those fragments, I want it to be replaced by the other.
FragmentTransaction ft = this.getFragmentManager().beginTransaction();
Fragment mFragment = Fragment.instantiate(this.Activity(), Fragment2.class.getName());
ft.replace(android.R.id.content, mFragment);
ft.commit();
public class Example_3_Mainfile extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.example_3_mainfile);
Fragment fr ;//make class that extend to thefragment
fr = new Act_2_1();
FragmentManager fm = getFragmentManager();
FragmentTransaction fragmentTransaction = fm.beginTransaction();
fragmentTransaction.replace(R.id.fragment_place, fr);
//id get of fragment tag from xml file there decelar
fragmentTransaction.commit();
}
}