I have an app with two screens, and buttons that switch between them. One screen is mainly a listView, but the other has a game engine in it (AndEngine) and it can take a while for the game engine to init the first time the activity is created.
So switching between them can cause big delays when going to a new game Engine. However, if I just use the back button, the previous game engine loads much faster.
Is there a way I can specify to crete only one game engine and always bring up that instance of the activity? Right now I am using startIntent() to swap screens. Is there some other way?
A way that allows me to just have a single instance of each activity and swap between them?
Android provides a simple way to switch between activities using a TabHost. You can also use it to switch between Fragments as explained under TabActivity. Alternatively, you can add a FrameLayout to your activity, programmatically instantiate the fragments and attach/show/hide them when needed.
Your res/layout/main.xml would look like this:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="fill_parent"
android:layout_width="match_parent"
android:id="#+id/mainframe">
</FrameLayout>
And assuming the v4 support library is used, your activity would look like this:
public MyActivity extends FragmentActivity {
private Fragment mListFrag;
private Fragment mGameFrag;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mListFrag = new MyListFragment();
mGameFrag = new MyGameFragment();
FragmentManager fm = getSupportFragmentManager();
fm.beginTransaction()
.add(
R.id.mainframe,
mListFrag,
MyListFragment.class.getName())
.add(
R.id.mainframe,
mGameFrag,
MyGameFragment.class.getName())
.detach(mGameFrag)
.commit();
fm.executePendingTransactions();
}
public void showList() {
getSupportFragmentManager().beginTransaction()
.hide(mGameFrag)
.show(mListFrag)
.commit();
}
public void showGame() {
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
if (mGameFrag.isDetached()) {
ft.attach(mGameFrag);
}
ft.hide(mListFrag).show(mGameFrag).commit();
}
}
Observe that MyGameFragment.onCreateView isn't called until it is first attached. After that, hiding and showing the fragments allows the user to switch without delay.
Edit: I realise now you wanted 2 activities with their own button. I've updated the code to reflect this. From the OnClickListeners simply call the relevant activity functions like so:
((MyActivity) MyListFragment.this.getActivity()).showGame();
Related
In my Activity I have a FrameLayout that covers the entire screen, but is empty at startup. When the user taps a button, a Fragment is displayed in the FrameLayout using this code:
android.support.v4.app.FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.setCustomAnimations(R.anim.grow_from_bottom_left, R.anim.stay);
transaction.replace(R.id.overlayLayout, new MyFragment(), MyFragment.TAG);
transaction.commit();
As you can see, I'm creating a new MyFragment every time the user taps the button (the fragment covers the button, and is removed when tapping back using transaction.remove()).
I'm thinking that it's probably not good for performance to always create a new fragment, so instead I wanted to create it once and then just show/hide it in the FrameLayout. My first thought was to simply store it as a member in my activity and reuse the same instance:
MyFragment mMyFragment;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mMyFragment = new MyFragment();
}
Is this the proper way to handle this scenario?
Or should I be using the SupportFragmentManager to store the fragment? I tried using it, but then my fragment doesn't display when I use it in transaction.replace(), even though I can find the fragment perfectly fine using findFragmentByTag()... I'm suspecting it's because I don't add it to a view when I first create it - but I don't want to add it to a view until the user taps the button.
I do not want to create new Activity for each task as the business logic is handled by custom Fragment already. There is a generic layout:
<FrameLayout .. >
<LinearLayout ..
android:id="#+id/fragment" />
</FrameLayout>
I wanted to create generic Activity that would use it and be initialized by a specific Fragment.
public class LeliFragmentActivity extends LeliBaseActivity {
public static final String KEY_FRAGMENT = BuildConfig.APPLICATION_ID + ".FRAGMENT";
protected void onCreate(Bundle state) {
super.onCreate(state);
setContentView(R.layout.activity_with_fragment);
Fragment fragment = (Fragment ) getIntent().getSerializableExtra(KEY_FRAGMENT);
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.fragment, calcFragment);
transaction.commit();
}
public static void start(Context c, LeliBaseFragment fragment) {
Intent intent = new Intent(c, LeliFragmentActivity.class);
intent.putExtra(KEY_FRAGMENT, fragment);
c.startActivity(intent);
}
But I feel that this not a good way. Passing Fragment in the Intent seems to me as overkill. All this serialization is costly. I could pass class name instead .. Or is there better way? Or shall I have one Activity for each (full screen) Fragment? Thanks for hint.
First
You can use one Activity for whole project if it's convenient for you and project is not too big. But if project is big and contains a lot of Fragments it's better to divide them into logical order on different activities. For example LoginActivity will represent all sing/registration flow with many Fragments. Than LeliFragmentActivity with all needed Fragments
Second
You don't have to pass your Fragments to each other. To communication between Fragment the best and simple way to use intrefaces like documentation says.
Other possible way to communication is event bus libraries like EventBus or Otto.
Try this:
https://gist.github.com/burnermanx/8e80e160dfba2f758546c037fce5269a
I made it based on your question, but it will not be useful for me for now.
So I basically have a button in 'DemosFragment' and when I click it, I want it to open another fragment (SettingsFragment), I understand now that I need an activity to fix this issue, as the button currently has an onClick method using intent/startActivity, so how would I go about creating an activity that just holds my fragment? I know that may sound weird they way I wrote it, I just started Android development, but basically I have a fragment and because I want a fragment to have a button to open another fragment, I figure I need an activity for the fragment I am trying to open, so how do I create that activity and what do I need to put in it? Thanks.
You need an activity with the following code:
public class ShowFragmentActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_show_fragment);
}
}
You also have to create a layout xml file called activity_show_fragment.xml in your res/layout folder:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment class="com.example.yourFragmentsClassName"
android:id="#+id/fragment_id"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
This should work for just displaying your fragment.
To launch the activity, paste this code in your button's onClick method:
Intent i = new Intent(this, ShowFragmentActivity.class);
startActivity(i);
It's always a good decision to look at the official docs: http://developer.android.com/reference/android/app/Fragment.html.
Hope that helps!
Wow! Your question requires a long answer, however is a good practice (and madatory too) that Fragments cannot communicates between each others, but they can be hosted by an Activity; in that case an Activity can manage the communication flow between them (fragments) and can be developed in several ways, Bundle, Intent and the Handler. Have a look to the ufficial Android documentation here:
http://developer.android.com/training/basics/fragments/index.html
The android docs section on building a flexible UI is a good example of how to start/load a Fragment from an Activity. In the example you will see that a FrameLayout in the Activity XML is used a the fragment container. This will be the View in which all of your fragments are displayed.
When you load your fragment with a FragmentTransaction the contents of your fragments layout will be displayed in the container View. In the above referenced example this takes place with SupportFragmentManager a class included with the android support library, for facilitating fragment transactions in earlier version of the operating system. SupportFramgnetManager requires that you extend FramentActivity and not just Activity. If you're not worried about backwards compatibility and are extending activity, not fragment activity, you can simply use getFragmentManager() instead.
getFragmentManager().beginTransaction()
.add(R.id.fragment_container, firstFragment).commit();
After the initial add transaction you can switch between fragments using the replace method for your fragment transaction. Replace does exactly what it sounds like, it swaps one fragment for another. To accomplish this from within your firstframgnet use
SecondFragment secondFragment = new SecondFragment();
getActivity().getFragmentManager().beginTransaction()
.replace(R.id.fragment_container, secondFragment).commit();
Notice that from within the fragment I used getActivity(). This allows you to reference the context of the host activity to access the fragment manager. When you are within the activity you do not need to use getactivity because the fragment manager is already accessible from that context.
What is the correct way to use fragments? Should i use only one FragmentActivity per interface or use a unique FragmentActivity for all interfaces?
My app is quite simple: it has 4 distinct interfaces, a fixed options menu on top and i only show contextual menu in a interface called 'actions'.
I was thinking to use only one FragmentActivity with an area where i could switch between fragments. Is it a good practice or should i keep one FragmentActivity per interface?
By now, i'm using one FragmentActivity per interface but when i change ( start ) FragmentActivity, it looks like that is creating a new object and my app blinks before open the new activity.
Thank in advance.
edit:
Here is a drawing of my app: http://postimg.org/image/mvxyk2527/
It's a lot of personal preference and requirement really. I'm not a big believer of the "correct way" concept, if you achieve your goal then it's not wrong, but there may be a nicer way to do it.
For me it would depend on how many nested fragments each FragmentActivity handles. If you have a tree of nested fragments you can get into a mess with nested fragment stacks, etc.
http://developer.android.com/training/implementing-navigation/temporal.html
Personally, if I'm writing an application with lots of fragments I tend to have a single Activity which makes navigation a bit more manageable with a single Fragment stack as using multiple FragmentActivity instances will use multiple fragment stacks.
http://developer.android.com/training/implementing-navigation/index.html
You could consider the ViewPager approach which is great if you want to use a tabular and swiping navigation. Although if you have heavy scrollable content views I would be hesitant as for some individuals multi-directional gesture interfaces can be quite frustrating.
http://developer.android.com/training/implementing-navigation/lateral.html
If your code is clean, manageable and not leaking references then I would probably stick with what you have and wouldn't waste your time re-factoring it.
I will try to explain it properly. In your case, you must use one FragmentActivity that includes fragments. You must create one fragment per interface and commit (exchange) when the user clicks on the menu. This may be an example of the structure of the project:
Java Classes:
MainActivity.class (extends FragmentActivity)
Interface1Fragment.class (extends Fragment)
Interface2Fragment.class (extends Fragment)
Interface3Fragment.class (extends Fragment)
Interface4Fragment.class (extends Fragment)
Layouts:
act_main.xml, frg_inter1, frg_inter2, frg_inter3 and frg_inter4.
In MainActivity and act_main.xml, you must create a tabhost o navigationDrawer for the menu and implement a method that allows to switch between fragments. Later, in each fragment you must implement the interface functionality.
Some code:
MainActivity:
public class ActMain extends FragmentActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.act_main);
}
private void createMenu() {
//you can create a menu with tabhost or navigation drawer
}
// replace a fragment
public void replaceFragment(Fragment launchFragment) {
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.replace(android.R.id.tabcontent, launchFragment).commit();
}
}
A Fragment:
public class Interface1Fragment extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
rootView = inflater.inflate(R.layout.frg_inicio, container, false);
return rootView;
}
}
MainActivity.xml
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:facebook="http://schemas.android.com/apk/res-auto"
android:id="#+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<!-- Main content -->
<FrameLayout
android:id="#android:id/tabcontent"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="0" />
<!-- Left Navigation Drawer -->
<LinearLayout
android:id="#+id/left_drawer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="start"
android:background="#color/black_actionbar"
android:choiceMode="singleChoice"
android:onClick="emptyOnClick"
android:orientation="vertical" >
/LinearLayout>
</android.support.v4.widget.DrawerLayout>
In fragments xml files you must design your interfaces.
Yeah, you should just change fragments, not restart the fragment activity if that part is gonna stay the same and work the same.
You should just switch between fragments like this:
getFragmentManager().beginTransaction().replace(R.id.container, new MyFragment()).addToBackStack(null).commit();
or like this if you are supporting API < 14
getSupportFragmentManager().beginTransaction().replace(R.id.container, new MyFragment()).addToBackStack(null).commit();
Note : remove addToBackStack(null) if you dont want to go back to previously loaded fragment onBackPressed
The main activity opens the main_fragment with this transaction:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (savedInstanceState == null) {
getFragmentManager().beginTransaction()
.add(R.id.fragment_main_container, new MainFragment())
.commit();
}
Then I replace that fragment with another one like this:
// method to handle Conversions button click
public void addConversionsFragment (View v) {
// replace the main fragment with the conversion fragment
UnitConversionFragment newFragment = new UnitConversionFragment();
FragmentTransaction transaction = getFragmentManager().beginTransaction();
// Replace whatever is in the fragment_container view with this fragment,
transaction.replace(R.id.fragment_main_container, newFragment);
// and add the transaction to the back stack so the user can navigate back
transaction.addToBackStack(null);
// Commit the transaction
transaction.commit();
}
Originally, I was using support.v4.app.fragment, and everything was working as expected (back button in the second fragment would pop that one out and return to the MainFragment. But then I decided to implement a PreferenceFragment elsewhere, which the support library didn't seem to like. So I converted the whole project to the regular app.fragment by deleting all the support imports, replacing with the regular imports, then editing all the getSupportFragmentManager() with getFragmentManager(), etc.
Good news is thePreferenceFragment works well, however any time I hit the back button in a fragment, it closes the hosting activity rather than reversing the transaction.
I did many searches and it seems that I am implementing the code correctly, but it is just not responding as I am expecting. Is there more involved in converting away from the support library? Or am I missing something else obvious? I saw a lot of answers out there overriding the onBackPressed(), but I really don't want to do that.
Is there some fundamental difference between the v4 support library and the regular library that requires me to handle the fragment transactions differently?
Preference Fragment has a bit of extra logic to handle hierarchical preferences. You can configure it to launch sub fragment screens, and navigate back, as demonstrated here.
<PreferenceScreen android:title="Sub Preferences"
android:fragment="com.example.SettingsDemo.SubPrefFragment"/>
For normal fragments , fragment back-stack pops first before reaching Activity back stack. This is what is documented.
The code in both android.app.Activity and android.support.v4.app.FragmentActivity is exactly same:
/**
* Take care of popping the fragment back stack or finishing the activity
* as appropriate.
*/
public void onBackPressed() {
if (!mFragments.popBackStackImmediate()) {
finish();
}
}
The only reason it is not happening as expected is that something else consumes "back press", this may happen when there are 3 levels of components:
The Activity is inside an ActivityGroup.
Fragment is inside another Fragment. (for this, there are some bugs with v4 fragments).