I want to dynamically create fragment. So when click the navigation fragment item, it will trigger the callback function in the activity to communicate with detail fragment. Here is the callback faction in activity:
public void getChatRoomId(long chatroom_id) {
FragmentManager fm = getFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
MsgChatRoom msgChatRoom = new MsgChatRoom();
ft.replace(R.id.activity_chat_MsgChatroom_container, msgChatRoom, "messages");
ft.addToBackStack(null);
ft.commit();
msgChatRoom.startQuery(chatroom_id);
}
I could call the startQuery method, but in that method I need some arguments should be initialize in onCreateActivity(). However, at the time when I call startQuery, the Fragment does not have called OncreateActivity. So there will be a error:
.... on a null object reference
How to solve this problem. Thanks in advance.
You can pass arguments to the fragment by using Fragment's setArguments(Bundle) function.
When you create the fragment pass your arguments by setArguments() and then in your fragment retrieve them by calling getArguments() in this way you don't wait for specific fragment's life-cycle to be able to use your data.
For more information you can visit http://developer.android.com/reference/android/app/Fragment.html which also contains couple of examples of using these functions
Related
I am refactoring an Android App to use fragments. I have a fragment which I add to the layout with transaction.replace method but whose onCreateView method is not called. Code looks as follows:
FragmentManager fragmentManager = m_Activity.getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
m_StartNewGameFragment = new StartNewGameToggleButtonsFragment();
fragmentTransaction.replace(R.id.bottom_pane, m_StartNewGameFragment);
fragmentTransaction.commit();
String winnerText = null;
if (isComputerWinner)
winnerText = m_Activity.getResources().getText(R.string.computer_winner).toString();
else
winnerText = m_Activity.getResources().getText(R.string.player_winner).toString();
m_StartNewGameFragment.updateStats(computerWins, playerWins, winnerText);
The method updateStats of fragment is as follows:
public void updateStats(int computer_wins, int player_wins, String winnerText) {
System.out.println("Update stats " + m_ComputerWins);
m_ComputerWins.setText(Integer.toString(computer_wins));
m_PlayerWins.setText(Integer.toString(player_wins));
m_WinnerTextView.setText(winnerText);
}
When updateStats is called m_ComputerWins is null and the program crashes. m_ComputerWins is initialized inside the onCreateView method of the fragment which seems not to be called.
Can anyone please help ?
Take global variables in your fragment class for computer_wins, player_wins, winnerText and init them inside updateStats method.
then inside onViewCreated() method, set values like
m_ComputerWins.setText(Integer.toString(computer_wins));
m_PlayerWins.setText(Integer.toString(player_wins));
m_WinnerTextView.setText(winnerText);
You never know when the fragments is created (which calls onCreate()), because fragment's life cycle differs from your activity's. In your code, you are trying to update fragment from activity. But in the way you have written your code, it is almost guaranteed that your fragment is not created yet.
Search for how to pass parameters to fragment or update fragment to find out the correct way of updating fragment from activity. You can find several related question, e.g. this one.
FragmentTransaction only runs on the next event loop. If you want to immediately run a fragment transaction, use fragmentTransaction.commitNowAllowingStateLoss();.
Please note that this won't actually work correctly after process death, which is why you should use the fragment.setArguments(Bundle method to pass initial argument values to a fragment.
I want to call a fragment method from its parent activity. For that i want object of fragment.
parent activity have fragment in framelayout like this:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/bottom_buttons"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
This is the code for getting fragment object.
FragmentBottomButtons fragment = new FragmentBottomButtons();
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.replace(R.id.bottom_buttons, fragment,"FragmentTag");
//ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
//ft.addToBackStack("");
ft.commit();
/*
getSupportFragmentManager()
.beginTransaction()
.add(R.id.bottom_buttons, new FragmentBottomButtons())
.commit();
*/
frag = (FragmentBottomButtons) getSupportFragmentManager().findFragmentByTag("FragmentTag");
//fragmentBottomButtons = (FrameLayout)findViewById(R.id.bottom_buttons);
if (frag == null){
Utility.displayToast("fragmnt is null");
}
But it is returning null.
can anyone help me on this? what is wrong here?
When you using commit() method nobody gives to you guarantee that your fragment will be attached to FragmentManager on the fly.
This happens due to inner FragmentManager logic : when you add\replace\remove any fragments you create a FragmentTransaction and puts it into execution queue inside FragmentManager. Actually all Transactions waiting until FragmentManager enqueue tasks.
To avoid this situation you can force enqueue each task under Fragment via
getSupportFragmentManager().executePendingTranscation();
This method starts pending Transactions and make more hard sure for use findFragmentById() method.
So, finally you need :
CustomFragment fragment = new CustomFragment();
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
//add or replace or remove fragment
ft.commit();
getSupportFragmentManager().executePendingTranscation();
CustomFragment customFragment = (CustomFragment) getSupportFragmentManager().findFragmentByTag("FragmentTag");
Your Fragment is not attached yet. You need to call findFragmentByTag() later on. You can for exemple add a listener to you Fragment and call your Activity on the onAttach() method to tell your Activity that your Fragment is created.
I have this Activity which at first shows a Fragment with a list of elements. This works perfectly with this code:
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.list_act);
if(null == savedInstanceState)
{
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
ListFragment glfragment = new ListFragment();
fragmentTransaction.add(R.id.listfrag1, glfragment);
fragmentTransaction.commit();
}
}
Well I have a ListFragment and a DetailFragment. But I don't know how to do the transition when I click an element of the list. I know the fragmentTransaction.replace(), but I don't know WHEN to call it.
I thought I should use the OnListItemClick() inside the ListFragment, but I don't know how to use the FragmentManager inside the Fragment and not in the main Activity... Also I want to "export" some data to the DetailFragment as if it was a Intent, but it's not.
To use the fragment manager inside your Fragment, simply call
getActivity().getFragmentManager() instead of getFragmentManager(). Implementing this in your OnItemClickListener should suffice.
What I would do is:
Define an interface with one method listItemSelected() with as an argument the id of the selected item
Let your activity implement this interface
In the onAttach of your list fragment, take the activity and keep it as a member variable, cast to the interface type. Make sure that in the onDetach you dereference it.
In your onListItemClick, call this method on your activity
In the activity, you can now do a new fragmenttransaction, this time you need to replace instead of add the fragment
To create your detail fragment with the correct argument (the id), use the method described here.
This should normally work fine.
My application has a Fragment inside its Activity. I would like to programmatically replace the fragment by another one from the current fragment itself.
For example, if I click on a button inside the fragment, the fragment should be replaced with another one, but the activity should remain the same.
Is it possible? If so, how to do it?
It's actually easy to call the activity to replace the fragment.
You need to cast getActivity():
((MyActivity) getActivity())
Then you can call methods from MyActivity, for example:
((MyActivity) getActivity()).replaceFragments(Object... params);
Of course, this assumes you have a replaceFragments() method in your activity that handles the fragment replace process.
Edit: #ismailarilik added the possible code of replaceFragments in this code with the first comment below which was written by #silva96:
The code of replaceFragments could be:
public void replaceFragments(Class fragmentClass) {
Fragment fragment = null;
try {
fragment = (Fragment) fragmentClass.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
// Insert the fragment by replacing any existing fragment
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction().replace(R.id.flContent, fragment)
.commit();
}
from the official docs:
// Create new fragment and transaction
Fragment newFragment = new ExampleFragment();
FragmentTransaction transaction = getFragmentManager().beginTransaction();
// Replace whatever is in the fragment_container view with this fragment,
// and add the transaction to the back stack
transaction.replace(R.id.fragment_container, newFragment);
transaction.addToBackStack(null);
// Commit the transaction
transaction.commit();
In this example, newFragment replaces whatever fragment (if any) is currently in the layout container identified by the R.id.fragment_container ID. By calling addToBackStack(), the replaced fragment is saved to the back stack so the user can reverse the transaction and bring back the previous fragment by pressing the Back button.
The behavior you have described is exactly what fragments are designed to do. Please go through the official guide for a thorough understanding of fragments which will clear up all your questions.
http://developer.android.com/guide/components/fragments.html
Please note that fragment should NOT directly replace itself or any other fragments. Fragments should be separate entities. What fragment should do is to notify its parent activity that some event has happened. But it is, again, NOT a fragment job to decide what to do with that! It should be activity to decide to i.e. replace the fragment on phone, but to i.e. add another to existing one on tablets. So you are basically doing something wrong by design.
And, as others already mentioned, your activity should use FragmentManager ("native" or from compatibility library) to do the job (like replace() or add() or remove()):
http://developer.android.com/guide/components/fragments.html
Just as Marcin said, you shouldn't have a fragment start another fragment or activity. A better way to handle this situation is by creating a callback implementation for the main activity to handle requests such as start a new fragment. Here is a great example in the android developer guide.
There is a way which works; Just (in the fragment) do the following:
getFragmentManager().beginTransaction()
.add(R.id. container_of_this_frag, new MyNewFragment())
.remove(this)
.commit();
When using nested fragments, we don't want every inner fragment replacement goes to the outer most activity. A mechanism allowing a fragment to notify its parent that it wants to change to another fragment can be useful.
Here is my code in Kotlin, I think it is easy to translate into java.
interface FragmentNavigator {
fun navigateTo(fragment: Fragment)
}
class NavigableFragment: Fragment() {
var navigator: FragmentNavigator? = null
override fun onDetach() {
super.onDetach()
navigator = null
}
}
Inner fragments need to extend NavigableFragment, and use following code to change itself to another fragment.
navigator?.navigateTo(anotherFragment)
Outer activities or fragments need to implement FragmentNavigator, and override navigateTo.
override fun navigateTo(fragment: Fragment) {
supportFragmentManager.beginTransaction().replace(view_id, fragment).commit()
}
//Use childFragmentManager instead of supportFragmentManager a fragment
Finally in outer activities or fragments, override onAttachFragment
override fun onAttachFragment(fragment: Fragment?) {
super.onAttachFragment(fragment)
if(fragment is NavigableFragment) {
fragment.navigator = this
}
}
This worked for me:
getActivity().getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container,
new MenuFragment()).commit();
For Kotlin.
(activity as YourActivityLauncherFragment)
.supportFragmentManager.beginTransaction()
.replace(R.id.yourFragmentContainer, YourFragmentName()).setReorderingAllowed(true)
.commit()
Here is what I do to create a new fragment (called AlbumsTrackFragment) and replace the existing one :
AlbumsTrackFragment albumstrackfragment = new AlbumsTrackFragment();
FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.layout_ipod, albumstrackfragment, "albumsTrackfragment");
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
albumstrackfragment.DoTask(data);
The problem is that when I can't call the method DoTask() and pass the new fragment some data because the app crashes, indeed I get : Content view not yet created
How can I wait that the commit was performed to call DoTask() method of my fragment ?
Thanks
You might consider passing in data to your fragment through DoTask and then wait until OnActivityCreated() is called to perform whatever action happens in DoTask. This way you are passing in your data but not doing anything until your view is loaded and won't crash.