Here is my use case:
I need to create 3 tabs using ActionBar Navigation Tabs, and I am using ActionBarSherlock to accomplish this. Each of the 3 tabs is it's own Fragment. However, there is some common information that is shown in each of the tabs (in my case, product title, description). I have created another Fragment for this common information, and am referencing this Fragment in each of the main Fragment layouts, like this.
Here is my problem:
I want to reuse the Fragment instance that retrieves and displays the common info. I am using the code below, but it always seems to create a new instance of the common fragment in each of the main fragments.
FragmentManager fm = getFragmentManager();
f = (ProductDetailsInfoFragment) fm.findFragmentByTag("prodinfo");
if (f == null) {
Log.d(TAG, "fragment not found...creating new instance");
f = new ProductDetailsInfoFragment();
f.setTargetFragment(this, 0);
fm.beginTransaction().replace(R.id.prod_info_fragment, f, "prodinfo").commit();
}
You can share fragments if you want to. You will need to implement ActionBar.TabListener and in your onTabSelected just pick what fragment you want to use.
You could do something like this: https://gist.github.com/anonymous/5415274
A better option is to store the data that is needed by both of these fragments in a separate object that you can share between them. This will allow you to test the retrieval without having a UI attached to it if you wish. This also allows the two fragments to diverge as they need to, making them a single purpose thing vs. having to keep all the code needed for both actions in a single fragment.
Related
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.
My app has one MainActivity with three tabs (A, B, C).
Tab A shows FragmentA1. When I click a list entry in this fragment then FragmentA2 is shown (still in tab A). The same applies to the other tabs, some hierarchies go even deeper (FragmentC4).
All the switching and replacing of all the fragments is handled in MainActivity by Listeners. (Edit: I don't define my fragment in XML layouts but in the code only).
My Question is:
Should I hold references to all fragments in MainActivity or should I create them new everytime I need them?
What are the (dis)advantages? Can I reuse fragments by using Alternative 1, instead of recreating them everytime?
Alternative 1:
class MainActivity
private Fragment fgmtA1;
private Fragment fgmtA2;
private Fragment fgmtA3;
...
public onClickItemInA1(int itemId) {
fgmtA2 = new FragmentA2();
// put args
// replace
}
...
}
Alternative 2:
class MainActivity
...
public onClickItemInA1(int itemId) {
FragmentA2 fgmtA2 = new FragmentA2();
// put args
// replace
}
...
}
Alternative 3:
Maybe the best solution is a completely different approach?
Should I hold references to all fragments in MainActivity or should I
create them new everytime I need them?
It depends...
The only two reasons which i can think of are performance and keeping the state of a Fragment.
If you always create a new Fragment the GC will have a lot to do, which could cause some performance issues if you use a lot of bitmaps or huge data. You can reuse a Fragment by holding a reference to it in the Activity or getting the Fragment by tag or id using the methods FragmentManager.findFragmentByTag(String) or FragmentManager.findFragmentById(int). With them you can reuse already created Fragments, which should be done by default.
Furthermore if your Fragments hold some data, you will lose them or you cache it somewhere else to reacreate it if the Fragment is destroyed. While reusing a Fragment you can use onSavedInstanceState() to reacreate your state.
Hence, yes you should reuse a Fragment because it could cause system performance or headaches using anti-patterns to save some data.
I'm creating a data entry application, which is currently divided into three Activities - a login activity, a 'main' activity (user information, summaries, etc), and a data entry activity for a single record. Each of these activities has several different 'screens' (i.e. full-page layouts containing a set of views), and each of those screens is a single fragment.
I have two questions:
Is this an appropriate overall architecture? The division of activities/fragments is somewhat arbitrary but these partitions make semantic sense to me.
What is the best way to manage switching between fragments within an activity? My main activity uses a ViewPager and FragmentPagerAdapter since it is based on a tabs + swipe navigation structure, but all activities will have fragments which are not represented as menu items. Should I use a FragmentPagerAdapter without a ViewPager for this (and if so, how)? Should I be writing my own abstraction layer to handle the transitions?
I've read a number of tutorials and examples but haven't seen this particular pattern used. As I'm not very experienced in Android development, I thought it best to ask whether there's a ready-made solution before I try to write my own. Any advice is appreciated. Thanks!
EDIT: Here's what I have at the moment, as a very simplified version to switch between two 'screens' - a title page and a login page. This shows most of the things I want it to handle (holding fragment instances, managing transactions, placing them into the layout). Still seems like it's replicating the functionality of the PagerAdapter and ViewPager in some ways but I don't want it to be tied to menus, tabs, swiping, etc. since the primary navigation will be through buttons within the app. I'll also need to pass some initial data to the fragment initialization for more complex fragments.
public class LoginFragmentSwitcher {
private int mCurFragIndex;
private ArrayList<Fragment> mFragList;
public LoginFragmentSwitcher() {
//set initial index
mCurFragIndex = 0;
//create fragments
mFragList = new ArrayList<Fragment>();
mFragList.add(new TitleFragment());
mFragList.add(new LoginFragment());
//TODO: more fragments will be added here
//display the first fragment
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.add(R.id.fragment_container, mFragList.get(0));
ft.commit();
}
// Perform a fragment transition to the specified index
public void showFragment(int newFragIndex) {
//only switch if you're not already showing the appropriate fragment
if (newFragIndex != mCurFragIndex) {
//start the fragment transaction
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
//TODO: apply transition style if desired
//switch content to the new fragment
ft.replace(R.id.fragment_container, mFragList.get(newFragIndex));
//register entry in the back stack and complete transaction
ft.addToBackStack(null);
ft.commit();
//update index
mCurFragIndex = newFragIndex;
}
}
}
I'm building an app that the interface is based on this http://code.google.com/p/android-playground/, and I need to have a fragment inside of each tab (that are fragments). The tabs are all inflated from the same xml, and in that xml I have a fragment tag.
The problem is that when the activity is created, as the id of each fragment in the tabs are equal, the contents that should go to the second tab, go in the first.
I'm using this code to replace the fragment in the tab
FragmentTransaction ft = x.getSupportFragmentManager().beginTransaction();
ft.replace(R.id.details, fragment);
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
ft.commit();
I don't know if is possible to correct this.
do you want all the tabs to have the exactly the same content?
Usually when putting fragments in tabs each tab would be showing differnt content, so a xml file per tab would not be uncommon. You can have a seperate xml layout for each tab that just declares your fragment with a different id each time. Without declaring a seperate (read unique) id for each fragment there is no efficent / simple operation i know to get a handle to a specific fragment (as the id is the unique handle).
You also may be able to use a FragmentPagerAdapter depending on your needs. You could then fade your current tab fragment out, then call public void notifyDataSetChanged () and provide a new fragment. This is not really the standard way of doing it though and will not be preserved on the back stack.
Optionally you could create each tab programatically in the PagerAdapter and set a tag for each fragment when calling FragmentTransaction.add(..) and then use this tab-unique tag in future fragment transactions Ignore this, it does not look like you can switch fragements with a tag, id only im afraid. Go with my first suggestion I would!
I've struggled with ths as well and the way I reference a Fragment from its Activity later on is by accessing the Adapter for the tabs.
Assuming you use an Adapter, you could do something like this
private mTabsAdapter;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// etc
this.mTabsAdapter = new TabsAdapter(this, this.mViewPager);
// Tab1
Tab tab = actionBar.newTab();
this.mTabsAdapter.addTab(tab, MyFragment1.class, null);
// Tab2
tab = actionBar.newTab();
this.mTabsAdapter.addTab(tab, MyFragment2.class, null);
}
public MyFragment1 getTab1() {
return (MyFragment1)this.mTabsAdapter.getItem(0);
}
public MyFragment2 getTab2() {
return (MyFragment2)this.mTabsAdapter.getItem(1);
}
Then inside your fragment, you would have a method to access
public class MyFragment1 {
// ...
public void reloadData() {
// reload data here
}
}
Now you can access the Fragment such as
this.getTab1.reloadData();
This feels like a sad way to access a fragment, but you can't rely on a Tag in every scenario. Also, you must take care to only reference this where the reference to the Fragment exists.
There may be times that this will be null. For example if you nave a bunch of tabs, they may become garbage collected at some point. you should communicate between Fragments via a callback. This example is for only a few scenarios.
The callback method is described at Communicating with the Activity and I'd suggest this method if it fits your application. This will prevent the need for directly accessing tabs in most cases.
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();
}
}