I've setup a new Android project that comes with an activity. Here's the boiler plate code:
if (savedInstanceState == null) {
getFragmentManager().beginTransaction()
.add(R.id.container, new PlaceholderFragment())
.commit();
}
Can someone explain what this is doing exactly? From what I can see, it checks if the activity hasn't been initliazed and then inflates the layout. But what I don't understand is beginTransaction(), ew PlaceholderFragment(), and commit()
Thanks.
You use fragment transactions to add / replace (etc) fragments within a FrameLayout (R.id.container) and new PlaceholderFragment is a new instance of a fragment to be put into the container
//Check whether we're recreating a previously destroyed instance
if (savedInstanceState == null) {
//Execute a transaction, replacing any existing fragment with this one inside the frame.
//Getting FragmentManager object which will control fragment acvitiy
FragmentManager fm = getFragmentManager()
//Starting a FragmentTransaction which will handle transaction to this fragment activity
FragmentTransaction ft = fragmentManager.beginTransaction();
//Add a fragment to the activity state. This fragment may optionally also have its view (if Fragment.onCreateView returns non-null) into a container view of the activity.
ft.add(R.id.container, new PlaceholderFragment());
//Schedules a commit of this transaction.
ft.commit();
}
There is a good explanation to fragment activity here, here and here
FragmentManager is a class which helps in managing the fragments that an activity may need. So here you are basically getting an instance of it and you are beginning a transaction. You need an instance of transaction because it lets the runtime know that some change is going to happen when this is called. Here 'add()' is that change and finally you commit it to save that change.
The arguments to add are the layout where the fragment needs to be added and the PlaceHolderFragment() is the name of the Fragment you need to put in.
As fragments are the way to go, replacing all of many heavy Activites, Eclipse has also adapted to this change which cause the boiler alert. :)
Starting a fragment (which cant be done via Intents) is treated as a transaction just like in database (not a good example i guess).
getFragmentManager() - gets the Activities FragmentManger which is responsible to initiate FragmentTransaction.
beginTransaction() - creates a new Transaction for this particular fragment job.
new PlaceholderFragment() - is an instance of the PlaceholderFragment which you can find if scroll more in the Activity.
commit - a way to commit this trasaction and bring it to effect.
See Android docs. for more details. :)
It is simple my friend.
In simple Coding Language:
if (savedInstanceState == null) {
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.add(R.id.tab5, new PlaceholderFragment());
fragmentTransaction.commit();
}
If you think that it explain everything, then it is a pleasure for me. Otherwise just ping me to add theory information.
Related
Considering I have a navigation view and few fragments associated with it. I wanted to end the fragment when transited to another
For example if Im in fragment A and I click fragment B (or any), The fragment A should get closed or destroyed.
Note: fragmentTransaction.replace(R.id.container, fragment); doesn't work
code for transaction
if(fragment!=null){
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction ft = fragmentManager.beginTransaction();
ft.replace(R.id.fl, fragment);
ft.commit();
}
Based on the comments, you're WebView isn't getting unloaded when your Fragment transitions. To resolve this, call webView.stopLoading(); and webView.destroy(); from either the Fragment's onPause() or onStop() callback. you may also want to wrap both of those calls in a null check just in case webView actually does unload. So your code block will look something like this:
if(webView != null){
webView.stopLoading();
webView.destroy();
}
that's easy and handling automatically from android side , just enough after to replace your fragment don't do .addToBackStack(null) and when you go to next fragment , last fragment gone
I've done some research but I really couldn't find the answer.
I have single activity with side menu, and holder. I have many (non support) fragments, and all of them are using same holder (one at a time).
When user uses menu (it's in main activity), and goes another page, I want to add name of the current fragment to backstack (using .addToBackStack(Fragment1.class.getName())), but I couldn't find how to get current fragment.
I don't want to implement interface etc to keep track of current fragment. There is a super simple way using fragmentManger isn't there?
You can get your current fragment like this:
if (getFragmentManager().getBackStackEntryCount() > 1) {
Fragment f = getFragmentManager().findFragmentById(R.id.content_frame);
if (f instanceof BlankFragment) {
// Do something
}
}
OK,
If you want to get latest entry from backstack(thanks to #AndroidGeek);
fragmentManager.getBackStackEntryAt(fragmentManager.getBackStackEntryCount()-1);
and, if you want to get currently active fragment (thanks to #Salman500 #AndroidGeek);
Fragment f = getFragmentManager().findFragmentById(R.id.fragment_holder);
you can use this to get fragment id for non support fragment
Fragment fragment = getFragmentManager().findFragmentById(R.id.fragment_id);
if(fragment!=null)
{
getFragmentManager()
.beginTransaction()
.addToBackStack(null)
.commit();
}
You can keep track of fragments in the main activity (with variables) and access them there. Example:
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction transaction= manager.beginTransaction();
MyFragment myFragment = new MyFragment();
myFragment.doSomething();
Adding to the back-stack:
FragmentTransaction fragment = getSupportFragmentManager().beginTransaction();
fragment.addToBackStack(fragment);
fragment.commit();
This is answered here: get currently displayed fragment
Just use addToBackStack() before you commit() the fragment trancsaction. See it here
So your code will look like :
...
fragmentTransaction.replace(R.id.holder, newFragmentToShow, newFragmentTag);
fragmentTransaction.addToBackStack();
fragmentTransaction.commit();
...
EDIT : after OP was edited
You do not need the fragment class to call addToBackStack() as you have mentioned in the OP. The String argument is an optional string just to keep the state for the backstack state. You should see the documentation
It is all internally managed and the current active fragment is automatically added to the backStack, you may call it from where ever you want, it will always use current active fragment.
I have MainActivity that starts DetailsActivity. DetailsActivity layout contains only one FrameLayout needed for displaying DetailsFragment.
So, when user clicks button from MainActivity, DetailsActivity is started:
public class DetailsActivity extends ActionBarActivity{
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_info);
displayFragment();
}
public void displayFragment(){
Fragment fragment = DetailsFragment.newInstance();
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.replace(R.id.fragment_container, fragment);
transaction.commit();
}
}
The problem is that DetailsActivity onCreate finishes before DetailsFragment onCreateView, so user sees blank activity layout for multiple milliseconds. Is there any way to avoid it?
This is not possible.
In order to show a Fragment it must be put into a container. In your case the container (R.id.fragment_container) is part of your main layout (R.layout.activity_info).
Consequentially, in order for R.id.fragment_container to be available to accept the Fragment, R.layout.activity_info MUST have been inflated prior to calling the following...
transaction.replace(R.id.fragment_container, fragment);
transaction.commit();
Unfortunately the act of commiting a FragmentTransaction is asynchronous and, most importantly, you've had to call...
setContentView(R.layout.activity_info);
...before commiting the transaction in order to have a valid reference to the FrameLayout (via R.id.fragment_container).
The time it takes for a Fragment to become visible is likely to vary based on a number of things (device capabilities, complexity of Fragment layout and ancillary code etc). It MAY be possible to fiddle the process order but there is no official way of doing it and results would likely be more or less successful on different Android devices.
If you're really worried about the users seeing a blank screen for a short time then all I'd recommend is have the Activity create a ProgressDialog with a simple "Please wait..." message - create the dialog before commiting the transaction and dismiss it once the Fragment has been fully created.
The problem is that DetailsActivity onCreate finishes before
DetailsFragment onCreateView
That is expected, because from here:
commit() Schedules a commit of this transaction. The commit does not
happen immediately; it will be scheduled as work on the main thread to
be done the next time that thread is ready
You can try fragmentManager.executePendingTransactions() soon after the commit.
check executePendingTransactions
According to your problem you have to do like this inside your onCreate:
if (savedInstanceState == null) {
Fragment fragment = DetailsFragment.newInstance();
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.replace(R.id.fragment_container, fragment);
transaction.commit();
}
And if after doing this, the error is occurring then please show us your fragment java class.
It was actually my mistake. The delay I was talking about was due to I added custom slide animation to fragments transition. In case of activity creation the back stack was empty and it caused small delay before first fragment layout is shown. So I added check:
if (fragmentManager.getBackStackEntryCount() != 0)
before calling
transaction.setCustomAnimations(R.anim.in_transition_left, R.anim.out_transition_left);
Working with fragments I've always used replace() for my transactions, but I wish I didn't have to save instance states anymore to restore a fragment's view and prevent reloading when coming back to that fragment. So, I've decided to work with add(). The thing is when I add another fragment, the previous fragment view remains in the background and that's fine (that's the behavior I expected), but the problem is I can actually interact with the views in the background. Example:
Fragment A has a Button
Fragment B has a TextView
When I add Fragment A and later add Fragment B, I'm able to click on Fragment A's Button, even staying on Fragment B's view.
I'm using:
FragmentTransaction fragmentTransaction =
getSupportFragmentManager().beginTransaction().
add(getRootViewContainer(),fragment,fragment.getClass().getSimpleName());
if (shouldGoBack)
fragmentTransaction.addToBackStack(fragment.getClass().getSimpleName());
where getRootViewContainer() returns the id of the FrameLayout I'm using as my activity main container.
Now, is it really the default behavior of add()?
If so, is there a proper way to avoid this or one just has to use replace()?
What you can do here is just hide previous fragment at the time of transaction of current fragment.
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
Fragment newFragment= new MyFragment ();
ft.hide(CurrentFragment.this);
ft.show(newFragment);
ft.commit();
It worked for me just try it.
FragmentTransaction.hide(fragmentBehind); //works for me!
example :
//I have it globally available
FragmentTransaction trans = MainActivity.getManager().beginTransaction();
//not globally
FragmentTransaction trans = getFragmentManager().beginTransaction();
MapFragment newFragment = new newFragment();
trans.add(R.id.fragmentContainer, newFragment, tag);
trans.hide(this);
trans.addToBackStack(tag);
trans.commit();
Yes, this is a default behaviour of add().
If you really don't want to user replace(), you can try to disable views which are inside "old" fragment.
I um uzing this code:
FragmentManager FManager = getFragmentManager();
FragmentTransaction FTransaction = FManager.beginTransaction();
FTransaction.setTransitionStyle(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
fragment.setArguments(fragment.getArguments());
FTransaction.add(ResourceId, fragment, label);
FTransaction.addToBackStack(backStack);
FTransaction.commit();
and I m geting this on my activity:
http://i.stack.imgur.com/K9NKy.png
can someone help me?
I hope that my simple answer will help you. Currently, You are loading an another fragment over your main fragment. you need to "hide" or "remove" or "replace" with old one. As i mentioned them below
In case of Remove or Hide
Fragment fr2 = fragmentManger.findFragmentByTag(Constants.TAG_Detail_Page);
if (fr2 != null)
fragmentTransaction.remove(fr2);
While for "Replace", you will assign new fragment to contentView:
FTransaction .replace(R.id.fragment_container, newFragment);
You are asking the fragment manager to add another fragment and it is doing just that. You never tell it to do anything to the original fragment. If you want to remove the exisitng fragment and show a new one then you should be calling:
FTransaction.replace(...)
instead of calling add.
If the original fragment has been added via the xml layout file, it is more difficult to replace. There are multiple SO questions dealing with that situation.