Let's consider a case where I have Fragment A and Fragment B.
B declares:
public interface MyInterface {
public void onTrigger(int position);
}
A implements this interface.
When pushing Fragment B into stack, how should I pass reference of Fragment A for it in Bundle so A can get the onTrigger callback when needed.
My use case scenario is that A has ListView with items and B has ViewPager with items. Both contain same items and when user goes from B -> A before popping B it should trigger the callback for A to update it's ListView position to match with B pager position.
Thanks.
Passing interface to Fragment
I think you are communicating between two Fragment
In order to do so, you can have a look into Communicating with Other Fragments
public class FragmentB extends Fragment{
MyInterface mCallback;
// Container Activity must implement this interface
public interface MyInterface {
public void onTrigger();
}
#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 = (MyInterface ) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement MyInterface ");
}
}
...
}
For Kotlin 1.0.0-beta-3595
interface SomeCallback {}
class SomeFragment() : Fragment(){
var callback : SomeCallback? = null //some might want late init, but I think this way is safer
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? {
callback = activity as? SomeCallback //returns null if not type 'SomeCallback'
return inflater!!.inflate(R.layout.frag_some_view, container, false);
}
}
It is optimal for two fragments to only communicate through an activity. So you can define an interface in Fragment B that is implemented in the activity. Then in the activity, define in the interface method what you want to happen in fragment A.
In Fragment B,
MyInterface mCallback;
public interface MyInterface {
void onTrigger(int position);
}
#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 = (MyInterface) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement MyInterface");
}
}
Method for determining if user goes from B to A
public void onChangeFragment(int position){
//other logic here
mCallback.onTrigger(position);
}
In Activity,
public void onTrigger(int position) {
//Find listview in fragment A
listView.smoothScrollToPosition(position);
}
Goodluck!
Using #Amit's answer, and adapting to the OPs question, here is all the relevant code:
public class FragmentA extends BaseFragment implements MyInterface {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// THIS IS JUST AN EXAMPLE OF WHERE YOU MIGHT CREATE FragmentB
FragmentB myFragmentB = new FragmentB();
}
void onTrigger(int position){
// My Callback Happens Here!
}
}
...
public class FragmentB extends BaseFragment {
private MyInterface callback;
public interface MyInterface {
void onTrigger(int position);
}
#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 {
callback = (MyInterface ) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString() + " must implement MyInterface");
}
}
}
I think you should use communication, as I've written below. This code comes from this Android Dev page of communication between Fragments:
HeadlinesFragment
public class HeadlinesFragment extends ListFragment {
OnHeadlineSelectedListener mCallback;
public void setOnHeadlineSelectedListener(Activity activity) {
mCallback = activity;
}
// Container Activity must implement this interface
public interface OnHeadlineSelectedListener {
public void onArticleSelected(int position);
}
// ...
}
MainActivity
public static class MainActivity extends Activity
implements HeadlinesFragment.OnHeadlineSelectedListener{
// ...
#Override
public void onAttachFragment(Fragment fragment) {
if (fragment instanceof HeadlinesFragment) {
HeadlinesFragment headlinesFragment = (HeadlinesFragment) fragment;
headlinesFragment.setOnHeadlineSelectedListener(this);
}
}
public static class MainActivity extends Activity
implements HeadlinesFragment.OnHeadlineSelectedListener {
...
public void onArticleSelected(int position) {
// The user selected the headline of an article from the HeadlinesFragment
// Do something here to display that article
}
You may create call back interface by this way.
var screenVisibility=activity as YourActivity
screenVisibility.setScreenVisibility("which screen you want")
Related
I have a button in my fragment which opens a BottomSheetDialogFragment. I want to notify the host fragment if the user selected an item on the BottomSheetDialogFragment. In order to achieve this, I have made an interface in my BottomSheetDialogFragment. However, that interface only communicates with the host activity, not the fragment. How can I send the information from the dialog to the fragment?
This is my interface:
public interface BottomSheetListener {
void onButtonClicked(int index);
}
#Override
public void onAttach(#NonNull Context context) {
super.onAttach(context);
try {
mListener = (BottomSheetListener) context;
} catch (ClassCastException e) {
throw new ClassCastException(context.toString() + " must implement BottomSheetListener");
}
}
getParentFragment will return the parent fragment, if the current fragment is attached to a fragment else it will return null if it is attached directly to an Activity
#Override
public void onAttach(#NonNull Context context) {
super.onAttach(context);
try {
mListener = (BottomSheetListener) getParentFragment();
} catch (ClassCastException e) {
throw new ClassCastException(context.toString() + " must implement BottomSheetListener");
}
}
When you use a lot of fragments, nested fragments or dialogfragments it becomes messy for communicate between them. I am suggesting to use ViewModel with LiveData for passing and updating data.
first add this to build gradle :
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
then create ViewModel class :
public class YourViewModel extends AndroidViewModel {
private MutableLiveData<Integer> yourMutableLiveData=new MutableLiveData<>();
public YourViewModel(#NonNull Application application) {
super(application);
}
public MutableLiveData<Integer> getYourMutableLiveData() {
return yourMutableLiveData;
}
}
This the fragment you want set value :
public class FragmentA extends Fragment{
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
YourViewModel yourViewModel =new ViewModelProvider(getActivity()).get(YourViewModel.class);
yourViewModel.getYourMutableLiveData().setValue(0);
}
}
And this is the fragment you want to get value when updated :
public class FragmentB extends Fragment{
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
YourViewModel yourViewModel =new ViewModelProvider(getActivity()).get(YourViewModel.class);
yourViewModel.getYourMutableLiveData().observe(getViewLifecycleOwner(), new Observer<Integer>() {
#Override
public void onChanged(Integer integer) {
}
});
}
}
It can work on dialog fragment as well as I tested.
Notes :
-Do not pass context or any view into view model.
-Remember that onActivityCreated comes after onCreateView.
-Do not set this key to
YourViewModel yourViewModel =new ViewModelProvider(this).get(YourViewModel.class);
in fragment if you want to pass data fragment to fragment but you can pass in activity.
-You can set more than one observer to the data.
For example I have 2 fragments including 1 integer variable and 1 TextView for each. One of them has a button. I want this button to change all Integers and TextViews including the other fragment. How can I access to Variable and TextView of the other fragment ? Please explain with example code.
Fragment to Fragment Communication basically happens via an activity which generally hosts the Fragments, define an interface in your Fragment A, and let your Activity implement that Interface. Now you can call the interface method in your Fragment, and your Activity will receive the event. Now in your activity, you can call your second Fragment to update the textview(For example) with the received value:
// You Activity implements your interface which is defined in FragmentA
public class YourActivity implements FragmentA.TextClicked{
#Override
public void sendText(String text){
// Get instance of Fragment B using FragmentManager
FraB frag = (FragB)
getSupportFragmentManager().findFragmentById(R.id.fragment_b);
frag.updateText(text);
}
}
// Fragment A defines an Interface, and calls the method when needed
public class FragA extends Fragment{
TextClicked mCallback;
public interface TextClicked{
public void sendText(String text);
}
#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 = (TextClicked) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement TextClicked");
}
}
public void someMethod(){
mCallback.sendText("YOUR TEXT");
}
#Override
public void onDetach() {
mCallback = null; // => avoid leaking
super.onDetach();
}
}
// Fragment B has a public method to do something with the text
public class FragB extends Fragment{
public void updateText(String text){
// Here you have it
}
}
I have two fragment class (#1 and #2) in my fragment activity.The fragment activity is used in view pager.
How can i call a method of fragment#2 in fragment#1?
Please, notice the attached picture.
http://upload7.ir/imgs/2014-09/92109715706282282966.jpg
The preferred way is to let all communication between Fragments go trough their common parent Activity:
public class MyActivity implements WantsACalledListener {
private Fragment fragment2;
#Override
public onWantsACalled() {
fragment2.a();
}
}
And, according to the Android documentation at: http://developer.android.com/guide/components/fragments.html
public static class Fragment1 {
WantsACalledListener mListener;
...
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
mListener = (WantsACalledListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString() + " must implement WantsACalledListener");
}
}
...
private void someMethod() {
mListener.onWantsACalled();
}
}
So, the Activity can easily keep a reference to all its child Fragments and the child Fragments get the reference to their parent Activity in their onAttach method.
Current my fragment A opens a new dialog fragment B. Is there any way to call method from a fragment A method from a dialog fragment B?
Regarding to android web site, you shouldn't call a fragment from another fragment, instead you should let activity to handle switching fragments.
To do that,
You can create an interface and implement it in activity,
public interface MyListener{
public void switchFragment(Fragment f);
}
public class MyActivity extends Activity implements MyListener{
#Override
public void switchFragement(Fragment f){
getSupportFragmentManager().beginTransaction
.replace(R.id.container, f)
.commit();
}
}
and in fragment classes,
public class MyFragment extends Fragment{
private MyListener listener;
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
listener = (MyListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement MyListener");
}
}
#Override
public void onDetach() {
super.onDetach();
listener = null;
}
}
and simply use
listener.switchFragment(fragment);
You will need to one of 2 things:
Wire a reference to each Fragment in your Activity
Use an Enterprise Service Bus (Otto is an open-source option) to communicate directly between Fragments
There is NO way to talk direct Fragment to Fragment in the standard framework. You must manually create this linkage.
I have default Master-Detail flow, which was created automatically when creating new project. My question is. When I add a button to detail side. Is there a way to update my list side by pressing that button ? In other words, can ItemDetailFragment and ItemListFragment communicate ?
Yes just communicate through the activity with a listener.
Your activity:
public class MyActivity extends FragmentActivity implements OnFragmentClickListener {
#Override
public void OnFragmentClick(int action, Object object) {
switch(action) {
}
}
}
The listener class:
public interface OnFragmentClickListener {
public void OnFragmentClick(int action, Object object);
}
Your fragments will then have following somewhere in code in order to implement the interface:
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
mListener = (OnFragmentClickListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString() + " must implement listeners!");
}
}
Then your fragments communicate with each other like this: fragmentA -> activity -> fragmentB. Your activity can call methodes directly on the fragments without worrying about synchronization problems.
Example of a call from fragment a:
mListener.OnFragmentClick(GLOBAL_ACTION_KEY someObject);
Activity then handle:
public class MyActivity extends FragmentActivity implements OnFragmentClickListener {
#Override
public void OnFragmentClick(int action, Object object) {
switch(action) {
case GLOBAL_ACTION_KEY:
// you call fragmentB.someMethod();
break;
}
}
}