If I declare a fragment in an XML layout, how do I pass it a Bundle?
Another option is to not declare the fragment in the XML. I know it is not exactly what you want to do. However you could declare a simple layout in your view like this:
<LinearLayout
android:id="@+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" />
And then in your Activity
class you programatically inflate the layout with the fragment. This way you can pass through parameters using args.
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
MyFragment fragment = MyFragment.newInstance();
Bundle args = new Bundle();
args.putInt(Global.INTENT_INT_ROLE, 1);
fragment.setArguments(args);
fragmentTransaction.add(R.id.fragment_container, fragment, "MyActivity");
fragmentTransaction.commit();
In the fragment,
if (getArguments() != null) {
int role = getArguments().getInt(Global.INTENT_INT_ROLE); }
This approach is not as clean and simple as declaring it in the xml however I have moved to it as it gives you a lot more control over the fragment.
Now that my Activity is just a wrapper around a Fragment that does the same work, how do I get that bundle to the Fragment if I declare the fragment in XML with the tag?
You can't.
However, you are welcome to call findFragmentById()
on your FragmentManager
to retrieve the fragment post-inflation, then call some method on the fragment to associate data with it. While apparently that cannot be setArguments()
, your fragment could arrange to hold onto the data itself past a configuration change by some other means (onSaveInstanceState()
, setRetainInstance(true)
, etc.).
You can't pass a Bundle (unless you inflate your fragment programmatically rather then via XML) but you CAN pass parameters (or rather attributes) via XML to a fragment.
The process is similar to how you define View custom attributes. Except AndroidStudio (currently) do not assist you in the process.
suppose this is your fragment using arguments (I'll use kotlin but it totally works in Java too):
class MyFragment: Fragment() {
// your fragment parameter, a string
private var screenName: String? = null
override fun onAttach(context: Context?) {
super.onAttach(context)
if (screenName == null) {
screenName = arguments?.getString("screen_name")
}
}
}
And you want to do something like this:
<fragment
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/myFragment"
android:name="com.example.MyFragment"
app:screen_name="@string/screen_a"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
Note the app:screen_name="@string/screen_a"
to make it work just add this in a values file (fragment_attrs.xml
or pick any name you want):
<!-- define your attribute name and type -->
<attr name="screen_name" format="string|reference"/>
<!-- define a bunch of constants you wanna use -->
<string name="screen_a" translatable="false">ScreenA</string>
<string name="screen_b" translatable="false">ScreeenB</string>
<!-- now define which arguments your fragment is gonna have (can be more then one) -->
<!-- the convention is "FragmentClassName_MembersInjector" -->
<declare-styleable name="MyFragment_MembersInjector">
<attr name="screen_name"/>
</declare-styleable>
Almost done, you just need to read it in your fragment, so add the method:
override fun onInflate(context: Context?, attrs: AttributeSet?, savedInstanceState: Bundle?) {
super.onInflate(context, attrs, savedInstanceState)
if (context != null && attrs != null && screenName == null) {
val ta = context.obtainStyledAttributes(attrs, R.styleable.MyFragment_MembersInjector)
if (ta.hasValue(R.styleable.MyFragment_MembersInjector_screen_name)) {
screenName = ta.getString(R.styleable.MyFragment_MembersInjector_screen_name)
}
ta.recycle()
}
}
et voilá, your XML attributes in your fragment :)
Limitations:
- Android Studio (as of now) do not autocomplete such arguments in the layout XML
- You can't pass
Parcelable
but only what can be defined as Android Attributes
It's not an encapsulated way, but I ended up "pulling" the bundle from the parent activity:
Bundle bundle = getActivity().getIntent().getExtras();