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.
Related
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.
I simply want to replace a fragment from the current fragment and add a new one.
I have studied that Replace is Equal to removeCurrent + Add anotherFragment
But in my situation i am facing a Problem. For the first two times it is running properly but after third attempt instead of replacing . it ads the fragment on me.
My approach fails now. Now i am asking that how can i send an id to other fragment like we do in Activities. I have a listView with a ReadMore button. I simply want to show the details in another fragment and want to send the index of a List.
I listen about some Interface but don't know how to implement . or there an easy way for communication. I tried doing it by passing it in Constructor . but it will give me an error . because
how to get currentFragment and hide it?
how to add onBackPressed to fragment.
how to send data between fragments The easy way.
i know how to hide or show a fragment
getFragmentManager().beginTransaction().hide(frag[j]).commit();
getFragmentManager().beginTransaction().show(frag[0]).commit();
also know how to add or replace a fragment
getFragmentManager().beginTransaction().add(R.id.container, frag[i])
//.addToBackStack(null)
.commit();
getFragmentManager().beginTransaction().replace(R.id.container, frag[i])
//.addToBackStack(null)
.commit();
Tell me a better approach to send data and why my layout add instead of replacing.
I recommend using EventBus for Activity/Fragment/Service or any other communication.
There is a good one from Square: http://square.github.io/otto/
Or this one (also very good): https://github.com/greenrobot/EventBus
It makes it simplier and your code gets cleaner.
It is pretty easy to send data to fragment with constructor.
In your fragment class, add constructor like this:
public class MyFragment extends Fragment{
String id;
public MyFragment(String id)
this.id = id;
}
Then, when calling a fragment,you can easily send data like this :
getSupportFragmentManager().beginTransaction().replace(android.r.id.container,new MyFragment("123")).commit();
You can't override onBackPressed within fragment, you can only do that in activity
You can set try set the variables in the fragment as public static then you can declare them before you instantiate your fragment to be shown.
in your onDestroy method of the fragment you can set the variables = null, so they dont take space in memory.
could be smth like this
MyFragment.theString = "A String";
getSupportFragmentManager().beginTransaction().replace(android.r.id.container,new MyFragment()).commit();
And in your fragment
public static String theString;
...
public void onDestroy(){
super.onDestroy();
theString = null;
}
I have seen a few versions of this question before, but the reasons for this exception were different than my own it seems.
What I am trying to do:
-Main Activity class has a toolbar at the bottom, clicking the buttons will display a series of fragments, one after another.
- A class EditItemFragmentManager, which is instatiated on a button click, and has methods that display specific fragments based on the toolbar button clicked.
I would like to use this manager class I created because it cleans my code up significantly and will make adding more features later helpful.
Here is my EditItemFragmentManager class, I am not sure if extending Activity is a good idea or not, I think that it will put my MainActivity on pause
public class EditItemFragmentManager extends Activity{
//instance variables
public EditItemFragmentManager(){
// initialization of some variables
}
public void editItem(){
editItemSequence();
}
private void editItemSequence(){
EditNameFragment enf = new EditNameFragment();
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.add(editNameFragment, EDIT_FRAG_TAG);
fragmentTransaction.addToBackStack(EDIT_FRAG_TAG);
fragmentTransaction.commit();
}
}
So it blows up when commit(); is called, giving me
java.lang.IllegalStateException: Activity has been destroyed
This is how I am trying to get this fragment from my MainActivity,
#Override
public void onClick(View view) {
EditIteFragmetManager manager = new EditIteFragmetManager();
manager.editItem();
}
I am still learning about the Acvtivity lifecycle in Android. I think my problem is something due to this class extending Activity, which puts my Main on pause, and the FragmentTransaction has nothing to commit to? If so, I need to get the existing instance of my main activity and call it on that? This is where I'm a bit lost, if anyone who understands the lifecycle of Activities/Fragments explain how I could go about implementing this while still having a helper class such as this?
If you're using the SupportFragmentManager, then you need to extend from FragmentActivity, and not just Activity. Also make sure that you imported the Fragment from the v4 support library, and not android.app.
Other than that, you seem to be instantiating a subclass of Activity with "new", which is terrible. Create activities only using Intents.
I solved this issue by moving my manager class to become a private inner class of my main, since they are so tightly coupled. No fragment issues now.
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();
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();
}
}