Android best practices for fragment-fragment interaction (described here and here) forces the Activity to implement a listener defined by the child fragment. The Activity then manages the communication between fragments.
From my understanding this is to keep the fragments loosely coupled from each other. However,
Is this also the case for nested fragments? I can imagine it might make sense for a nested fragment to report directly to it's parent fragment instead of the Activity.
If a nested fragment has its parent fragment implement it's listener, how would one (or should one) require the parent fragment to do this. In other words, is a similar to the paradigm to the following but for Fragments:
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
mCallback = (OnHeadlineSelectedListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement OnHeadlineSelectedListener");
}
}
If anyone wanted an example of an implementation that ensures parent context implements the callbacks while not caring whether it is an activity or fragment, the following worked for me:
#Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof Callbacks) {
mCallbacks = (Callbacks) context;
} else {
if (getParentFragment() != null && getParentFragment() instanceof Callbacks) {
mCallbacks = (Callbacks) getParentFragment();
} else {
throw new RuntimeException(context.toString()
+ " must implement " + TAG + ".Callbacks");
}
}
}
#Override
public void onDetach() {
super.onDetach();
mCallbacks = null;
}
Enjoy!
As long as you define an interface in the fragment, you can have the parent activity or parent fragment implementing it. There is no rule that says fragment should not implement interface of a child fragment. One example where this make sense is that fragment A has two children Fragments B, C. A implements B's interface, when A gets a call back, it might need to update fragment C. Exactly the same thing with activity, just different level.
You can implement the same pattern for child/parent interactions using getParentFragment(). The parent fragment refers to whichever fragment has this one added through its ChildFragmentManager. If this Fragment is attached directly to an Activity, this method returns null.
Related
noob fragmentmanager question. Is there a better way to close a fragment from a button inside said fragment? I was trying to use getParentFragmentManager from within the fragment in the onclick but this doesnt seem to work(or do anything really). Im guessing because I really need the container in the main activity to be hidden and not the fragment? I do have a working viewModel and was wondering if it would be better to run a co-routine in the main activity that listens for the button click and hides/shows the fragment in the container view. Keep in mind I have multiple container views in the main activity.
parentFragmentManager.beginTransaction().hide(this).commit()
We can use supportFragmentManager instead of parentFragmentManager(if not using fragment inside fragment) guessing that your activity is AppCompat one.
activity.supportFragmentManager.beginTransaction().hide(this).commit()
or if you want to pop your fragment then you can do the following:
activity.supportFragmentManager.popBackStack()
To hide from activity, we can use fragmentlistener callback. It's a callback kind of:
public class MyActivity implements OnFragmentInteractionListener {
#Override
public void onFragmentInteraction(String pageClassName, int operationCode, Bundle operationRequirement) {
//here we can put conditions if any according to pageClassName or operationCode
}
}
public interface OnFragmentInteractionListener {
/**
* #param pageClassName This basically class.getName() string eg.Myfragment.this.getClass().getName()
* #param operationCode This would be an we are using to distinguish each opearation
* #param operationRequirement This would be bundle that would contain result
* */
void onFragmentInteraction(String pageClassName, int operationCode, #Nullable Bundle operationRequirement);
}
Now inside my fragment:
private OnFragmentInteractionListener mListener;
#Override
public void onAttach(#NotNull Context context) {
super.onAttach(context);
try {
mListener = (OnFragmentInteractionListener) mActivity;
} catch (ClassCastException e) {
throw new ClassCastException("activity must implement OnFragmentInteractionListener");
}
}
now inside onClick of fragment:
mListener.onFragmentInteraction(TAG, HIDE_FRAMENT, extras);
Navigation component appears to be the best way (for my use case) to manage fragment transactions. Works with binding also!
https://developer.android.com/guide/navigation
I have a fragment that can either be attached to an Activity or a parent fragment. This fragment has an interface that must be implemented by anyone it is attached to. For activities, this is quite simple:
#Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof Activity){
Activity activity =(Activity) context;
try {
mCallback = (OnMyListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString() +
" must implement OnMyListener");
}
}
}
However, I am unable to set the mCallback listener for other Fragments that are hosting this particular Fragment.
You can't directly communicate between 2 fragments, it has to go thorough the activity hosting it (and I saw you already implemented the first half).
After the activity received the data from sender fragment, you can send it to the receiver fragment by resolving the receiver fragment's reference first, using:
ReceiverFragment fragment = ( ReceiverFragment) getSupportFragmentManager().findFragmentById(R.id.receiver_fragment_id);
if it's null then you need to instantiate it first and pass the data using fragment.setArguments(Bundle), otherwise you can directly call the member function of the receiver fragment.
Check: https://developer.android.com/training/basics/fragments/communicating.html
I am new to Android and I am trying to build an app containing three fragments: let's say A, B, and C. I want a button on A to show me B when clicked, and a button on B to show me C when clicked. I understand that one way is to use FragmentManager like this: in fragment A, I can have a button click listener that does
getSupportFragmentManager().beginTransaction()
.add(R.id.fragment_container, new B())
.commit();
However, I do not want A to know about fragment B, or B to know about C. I was thinking of creating some kind of FragmentController to solve this problem such that the Controller would know about the necessary transitions and maintain a state machine.
Is this a common pattern in Android? I tried googling but I didn't see many code examples for this pattern. How can I decouple the different fragments so that the fragments don't have to worry about the transitions?
Thanks.
Yes, it is a common pattern, you need understand how to work the comunnication between fragments.
Taking on, your fragments are contained in an activity, maybe you can do the next:
The Fragment listener
public interface FragmentNavigationListener {
public void onNavigateTo(int fragment);
}
The Activity which implements callback
public static final int Fragment FRAGMENT_A = 0;
public static final int Fragment FRAGMENT_B = 1;
#Override
public void onNavigateTo(int fragment){
switch fragment{
case FRAGMENT_A :
...
case FRAGMENT_B :
...
}
}
Each Fragment
must encore that parent activity implements the listener
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
mCallback = (FragmentNavigationListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement FragmentNavigationListener");
}
}
In OnclickListener from each button in each fragment you call the listener.
button.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
// Perform action on click
mCallback.onNavigateTo(FRAGMENT_B);
}
});
Write fragmentInteraction interfaces, define methods in this interface, then onAttach of the fragment lifeCycle do this
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
mListener = (OnFragmentInteractionListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement OnFragmentInteractionListener");
}
}
That way every activity to which this fragment gets attached to must implement the OnFragmentInteractionListener. Now in your onClick method do mListener.onClick(view) assuming you have a method called onClick(View v) defined in your interface. And in your activity's implementation of the onClick function do the fragmentTransaction
I have two fragment class:
Mainfragment and subfragment.
I have public List<object> l1 in Mainfragment and i have add items in this list.
Now i want to access this list l1 in subfragment.
i have used - subfragment sf = (subfragment) getActivity().getFragmentManager();
but it shows error.
How can i access this List l1 in subfragment?
It is always better to let the activity apply changes to its fragment than passing values directly between them.
you can achieve this by implementing one interface :
-Implement one interface on MainActivity, say interfaceListener with onQuery(Bundle data) and onResult(Bundle data) methods.
next inside onAttach() of each fragments, implement like this :
public void onAttach(Activity activity) {
super.onAttach(activity);
try{
interfaceListener = (interfaceListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement interfaceListener");
}
}
In this way , you can access list of MainFragment in subFragment.
If I'm inside a Fragment how can I call a parent's activity?
Yes, Its right by calling getActivity and cast it with parent activity to access its methods or variables ((ParentActivityName)getActivity())
Try this one.
ParentActivityName is parent class name
2021 UPDATE
As it's told in the comments, Fragment#onAttach(Activity) is deprecated starting from API 23. Instead:
#Override
public void onAttach(Context ctx) {
super.onAttach(ctx);
// This makes sure that the container activity has implemented
// the callback interface. If not, it throws an exception
try {
// Only attach if the caller context is actually an Activity
if (ctx instanceof Activity) {
mCallback = (OnHeadlineSelectedListener) ctx;
}
} catch (ClassCastException e) {
throw new ClassCastException(ctx.toString()
+ " must implement OnHeadlineSelectedListener");
}
}
ORIGINAL ANSWER
The most proper way is to make your Activity implement an Interface and use listeners. That way the Fragment isn't tied to any specific Activity keeping it reusable. Into the Fragment:
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
// This makes sure that the container activity has implemented
// the callback interface. If not, it throws an exception
try {
mCallback = (OnHeadlineSelectedListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement OnHeadlineSelectedListener");
}
}
That way, you make the Activity listen to the fragment when it's attached to it.
See also:
http://developer.android.com/training/basics/fragments/communicating.html
Simply call your parent activity using getActivity() method.
CardView cardView = (CardView) getActivity().findView(R.id.your_view);