Overview of what I need:
Pass String from FragmentActivity to FragmentList. Said FragmentList is specified in the xml as below:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MultiContactsFragmentList" >
...
<fragment
android:id="#+id/multiContactsList_fragment"
android:name="com.sf.lidgit_android.content.MultiContactsFragmentList"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
The problem in the code lies here (by the way, I'm using Support Library because I target API level 10), in my class that extends from FragmentActivity:
#Override
protected void onCreate(Bundle savedInstance) {
super.onCreate(savedInstance);
...
inviteByEmail();
}
public void inviteByEmail(){
setContentView(R.layout.activity_multi_contacts_email_picker);
MultiContactsFragmentList fragmentList =
(MultiContactsFragmentList)
getSupportFragmentManager().findFragmentById(R.id.multiContactsList_fragment);
Bundle extras = new Bundle();
extras.putString(MultiContactsFragmentList.DATA_TO_PICK, MultiContactsFragmentList.EMAIL_DATA);
fragmentList.setArguments(extras);
}
Here is the thing, fragmentList is null. I figure it's because of the life cycle (I'm not aware to the FragmetnActivity-FragmentList life cycle). The FragmentList is yet to be created. Is that it? I know the other way around would be to have my FragmentActivity "save" the data, and have the FragmentList access it (since FragmentActivity is available to it's Fragments, I believe).
EDIT
My FragmentList class (the fragment I'm trying to pass a Bundle to), implements LoaderManager.LoaderCallbacks<Cursor> . I don't know if that influences or not, but I tried the first answer below and got an error on my FragmentList caused by a null pointer Exception, it appears that the onLoadFinished(..) method was called before the onCreate(..), because the null Pointer Exception happened on the onLoadFinished(..), stating the adapter was null.
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d("invite", "oncreate called");
String[] fromColumns = {ContactsContract.Contacts.DISPLAY_NAME, this.view_ID_Data};
int[] toViews = {R.id.contact_name, R.id.contact_data};
getLoaderManager().initLoader(MULTI_CONTACTS_LIST_LOADER, null, this);
this.adapter = new SimpleCursorAdapter(
getActivity().getApplicationContext(), R.layout.custom_list_contacts_multiple_choice,
null, fromColumns, toViews,
CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);
setListAdapter(adapter);
}
#Override
public void onLoadFinished(Loader<Cursor> arg0, Cursor cursor) {
adapter.swapCursor(cursor);
}
Change your layout to this:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<FrameLayout
android:id="#+id/content"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
And in your activity:
#Override
protected void onCreate(Bundle savedInstance) {
super.onCreate(savedInstance);
...
inviteByEmail();
}
public void inviteByEmail(){
setContentView(R.layout.activity_multi_contacts_email_picker);
MultiContactsFragmentList fragmentList = new MultiContactsFragmentList();
Bundle extras = new Bundle();
extras.putString(MultiContactsFragmentList.DATA_TO_PICK, MultiContactsFragmentList.EMAIL_DATA);
fragmentList.setArguments(extras);
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.add(R.id.content, fragmentList);
ft.commit();
}
The method setArguments(Bundle args) for the fragment can only be called just after creating the fragment, so if you embed you fragment in the XML the argumments setting may no be happening at all even if you call it. This way you have to attach the fragment yourself but you will have the arguments when you access it.
Related
I have a quite complex situation, I'll try to explain:
I have three classes:
FleetListFragment extends android.support.v4.app.Fragment
FragmentFleetListMap extends FleetListFragment
FragmentFleetListReports extends FleetListFragment
now, this app is made of a drawer activity, which of course uses fragments; FragmentFleetListMap is used as a child fragment in one of the "main" fragments of the activity.
I want to use FragmentFleetListReports inside a custom dialog (FleetSelectDialog, which also contains other things) which is shown by one of the main fragments.
I tried this:
public class FleetSelectDialog extends Dialog{
public FleetSelectDialog(Context context, FragmentManager fragmentManager) {
super(context);
this.context = context;
this.fragmentManager = fragmentManager;
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LayoutInflater inflater = (LayoutInflater) context.getSystemService( Context.LAYOUT_INFLATER_SERVICE );
final View v = inflater.inflate(R.layout.fleet_select_dialog, null);
fragmentManager.beginTransaction().add(R.id.list_select_frame, new FragmentFleetListReports(this)).commit();
}
with fragmentManager passed from the main fragment: new FleetSelectDialog(context, getFragmentManager());
this gives this error:
java.lang.IllegalArgumentException: No view found for id 0x7f0e0086 (com.Tierra:id/list_select_frame) for fragment FragmentFleetListReports
The other thing I tried is changing the dialog to be a DialogFragment but:
getChildFragmentManager().beginTransaction().add(R.id.list_select_frame, new FragmentFleetListReports(this)).commit();
does not compile giving the error: cannot resolve method: add(int, myPackage.FragmentFleetListReports)
just like if FragmentFleetListReports is not extending Fragment
dialog layout looks like this:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/fleet_select_dialog"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:focusable="true"
android:focusableInTouchMode="true">
<FrameLayout
android:id="#+id/list_select_frame"
android:layout_width="match_parent"
android:layout_height="fill_parent"
android:layout_above="#+id/footerview"/>
<RelativeLayout
android:id="#+id/footerview"
android:layout_width="fill_parent"
android:layout_height="60dip"
android:layout_alignParentBottom="true">
<!--other views-->
</RelativeLayout>
</RelativeLayout>
I think I am missing something (especially on how DialogFragment works...) but I can't figure out how to solve this. Moving the other views to the fragment is not an option and neither is not using the fragment
if anyone can give any help, it would be great, thanks
I was able to solve the problem using a DialogFragment containing a FrameLayout and adding my fragment to this frame. I think it's not the best way to do this, but it's working at least
DialogFragment:
public FleetSelectDialog() {
}
public static FleetSelectDialog newInstance() {
FleetSelectDialog dialogFragment = new FleetSelectDialog();
return dialogFragment;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
fragmentFleetList = new FragmentFleetListReports();
getChildFragmentManager().beginTransaction().add(R.id.list_select_frame, fragmentFleetList).commit();
}
calling it this way:
fleetSelectDialog = FleetSelectDialog.newInstance();
fleetSelectDialog.show(getFragmentManager(), "");
I have simple app with fragments.
At the beginning list of fragments is empty.
And there are 2 buttons:
ADD FRAGMENT
REMOVE FRAGMENT
And following use-case:
Add fragment
Add fragment
Remove first fragment
Add fragment
As result onCreateView() on 4th step is not executed.
If I add fragment again on 5th step - onCreateView is executed again.
Why is it so? I didn't find right answer in google.
Source code:
public class MyActivity extends FragmentActivity {
private ViewPager pager;
private FragmentAdapter adapter;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
pager = (ViewPager) findViewById(R.id.pager);
final Button addButton = (Button) findViewById(R.id.add_button);
addButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
adapter.addFragment(new MyFragment());
adapter.notifyDataSetChanged();
}
});
final Button removeButton = (Button) findViewById(R.id.remove_button);
removeButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
adapter.removeFragment(0);
adapter.notifyDataSetChanged();
}
});
adapter = new FragmentAdapter(getSupportFragmentManager());
pager.setAdapter(adapter);
pager.setOffscreenPageLimit(10);
}
private class FragmentAdapter extends FragmentStatePagerAdapter {
final List<MyFragment> fragments = new ArrayList<MyFragment>();
public FragmentAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int i) {
return fragments.get(i);
}
#Override
public int getCount() {
return fragments.size();
}
public void addFragment(MyFragment fragment) {
fragments.add(fragment);
}
public void removeFragment(int index) {
fragments.remove(index);
}
}
private class MyFragment extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment, container, false);
}
}
}
main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Add"
android:id="#+id/add_button"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Remove first"
android:id="#+id/remove_button"/>
<android.support.v4.view.ViewPager
android:id="#+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
fragment.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="New Text"
android:id="#+id/textView"
android:layout_gravity="center_horizontal"/>
</LinearLayout>
OnCreateView() is only called when the actual Fragment is being drawn on the screen. Since you have removed a fragment at the beginning the pager no longer looks at the same fragment.
To better understand what is going on put a visible number on each of the fragments and then try adding and removing the fragments.
The issue is 'FragmentStatePagerAdapter' and the way it's implemented. The way to fix it is this:
Go to googlesource and grab the source for 'FragmentStatePagerAdapter'.
refactor (name, package,etc) to make it your own implementation.
Extend from your NEW version of 'FragmentStatePagerAdapter'
Modify your NEW version of 'FragmentStatePagerAdapter' using these steps below:
a) Change the mFragments.set(index, object) command to mFragments.add(index, object), in the the 'instantiateItem()' function.
b) Change the mFragments.set(index, object) command to mFragments.remove(index), in the 'destroyItem' function.
All of this assumes that you are using an ArrayList to hold fragments in your 'extends' class and that they are synced to match the ViewPager.
You will need to add a 'pageIndex' to each fragment, so that you can tell ViewPager when your item has 'moved' (due to deleting an item, that is NOT the LAST item).
This took several days to run down...hope it helps someone :-)
My application uses multiple nested fragments. Some of these fragments are placeholders. When I try to replace placeholders with initial set of fragments I get
java.lang.IllegalStateException: Activity has been destroyed
I am sure that activity is fine and the issue is caused by fragments not being attached to the activity. So how do know when all nested fragments are attached to the activity?
Additional information about my setup:
I have an Activity that has fragment declaration in it's layout file:
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/mainLayout"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment
android:id="#+id/rightPane"
android:layout_width="match_parent"
android:layout_height="match_parent"
class="com.company.PagerFragment"/>
</LinearLayout>
The PagerFragment is the host for view pager.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal">
<ViewPager
android:id="#+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
I put two Fragments into the pager. They serve as placeholders.
I want to replace placeholders with initial fragments but when I initiate the replacement in activity's onCreate method I get the aforementioned exception.
Everything works fine if I use new Handler().postDelayed(..) in activity's onCreate method. I would rather to not use the delay approach for obvious reasons.
I tried to replace placeholders in other activity's methods like onStart(), onResume(), onResumeFragments(), onPostResume() but that didn't help.
EDIT:
public class PagerFragment extends Fragment
{
..
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
return inflater.inflate(R.layout.fragment_right_pane, container, false);
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState)
{
super.onViewCreated(view, savedInstanceState);
if (savedInstanceState == null)
{
m_primary = new PlaceholderFragment();
m_secondary = new PlaceholderFragment();
}
else
{
FragmentManager fm = getChildFragmentManager();
m_primary = (PlaceholderFragment)fm.getFragment(savedInstanceState, FRAGMENT_TAG_PRIMARY);
m_secondary = (PlaceholderFragment)fm.getFragment(savedInstanceState, FRAGMENT_TAG_SECONDARY);
}
Fragment[] fragments = new Fragment[] { m_primary, m_secondary };
PagerAdapter pagerAdapter = new FragmentPagerAdapter(getChildFragmentManager(), fragments);
m_viewPager = (ViewPager)view.findViewById(R.id.pager);
m_viewPager.setAdapter(pagerAdapter);
m_viewPager.setOnPageChangeListener(this);
m_viewPager.setCurrentItem(0, true);
}
..
}
Trying to find a ListFragment in the xml layout is always returning null. I'm using SherlockFragmentActivity with a SherlockListFragment. Here is the code:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.my_custom_layout);
adapter = new My_Custom_Adapter(this, My_Object, My_Object_2);
}
// Create the list fragment and add it as the list content.
if (getSupportFragmentManager().findFragmentById(android.R.id.content) == null) {
mListFragment list = new mListFragment();
getSupportFragmentManager().beginTransaction().add(android.R.id.content, list).commit();
}
}
public static class mListFragment extends SherlockListFragment {
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setListAdapter(adapter);
setListShown(true);
}
}
This is a shortened version of the xml layout:
<RelativeLayout
android:id="#+id/top_control_bar">
<!-- Text Views -->
</RelativeLayout>
<RelativeLayout >
<!-- Button -->
</RelativeLayout>
<LinearLayout
android:id="#+id/start_data"
android:layout_width="match_parent"
android:layout_height="fill_parent"
android:layout_below="#id/top_control_bar"
android:orientation="vertical" >
<fragment
android:name="android.support.v4.app.ListFragment"
android:id="#android:id/content"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
</LinearLayout>
findFragmentById(android.R.id.content)
always returns null. Why can't it find the fragment within my layout?
The problem that you have is that you declared in the xml the Fragment as a ListFragment.
getSupportFragmentManager().findFragmentById(android.R.id.content) uses Support fragment manager, so you will need a SupportMapFragment
Try this:
<fragment
android:name="com.google.android.gms.maps.SupportMapFragment"
android:id="#android:id/content"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
Shouldn't
android:id="#android:id/content"
be
android:id="#+id/content"
?
From the documentation it seems like you are using the wrong syntax.
I'm also not sure your chained methods will work correctly as written. Change
getSupportFragmentManager().beginTransaction().add(android.R.id.content, list).commit();
to
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.add(android.R.id.content, list);
transaction.commit();
even then, I honestly don't know what you'd be trying to do there. You are aware that FragmentTransaction's add method as you're using it is trying to add a fragment to a container which, in your if, already was established as having a null value?
Try this in mListFragment class override onCreate() and call setRetainInstance(true);
public static class mListFragment extends SherlockListFragment {
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setListAdapter(adapter);
setListShown(true);
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
}
I have a layout containing a fragment :
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/root"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<fragment
android:id="#+id/ID"
class="com.teovald.app.MyFragment"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
<include
android:id="#+id/toolbar"
layout="#layout/toolbar" />
</FrameLayout>
I set this use setRetainInstance(true) in this fragment onCreate method :
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setRetainInstance(true);
....}
And finally I recuperate a reference to this fragment in its activity onCreate as well :
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
FragmentManager fragmentManager = getSupportFragmentManager();
mFragment = (MyFragment) fragmentManager.findFragmentById(R.id.ID);
...
}
However, each time I rotate my device, onCreate of the activity is called, then onCreate of the fragment is called as well ! Since I seted up setRetainInstance to true, it should not happen.
Is there a reason for this behavior ?
I had this issue recently and was fighting with it for a couple of hours until I discovered that in the code (that I copied from some third party library) of the Activity which contains the retained non-ui-fragment in onSaveInstanceState there was no call to super.onSaveInstanceState()
It was like that:
#Override
protected void onSaveInstanceState(Bundle outState) {
// Save the mapview state in a separate bundle parameter
final Bundle mapviewState = new Bundle();
mMapFragment.onSaveInstanceState(mapviewState);
outState.putBundle(BUNDLE_STATE_MAPVIEW, mapviewState);
}
So I added the missing call to be like:
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
// Save the mapview state in a separate bundle parameter
final Bundle mapviewState = new Bundle();
mMapFragment.onSaveInstanceState(mapviewState);
outState.putBundle(BUNDLE_STATE_MAPVIEW, mapviewState);
}
And now onCreate() is not called twice in the retained fragment.
I hope this will help somebody :)