Call different method in fragment based on parent activity - android

I in my app i have a fragment that contains some input fields, i show this fragment in two different activities, in both cases, the layout will be the same but I need to perform different actions based on who is the parent activity.
I'll try to explain better my problem and the solution that I'm using with the following code:
ActivityA
public class ActivityA extends AppCompatActivity{
private NavController navController;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.ActivityA);
navController = Navigation.findNavController(this,R.id.graphA);
initView();
}
public void nextFragment(int actionID, Bundle bundle) {
btn.setOnClickListener(v->{
navController.navigate(from_activityA_toFragment, bundle);
});
}
ActivityB
public class ActivityB extends AppCompatActivity{
private NavController navController;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.ActivityA);
navController = Navigation.findNavController(this,R.id.graphB);
initView();
}
public void nextFragment(int actionID, Bundle bundle) {
btn.setOnClickListener(v->{
navController.navigate(from_activityB_toFragment, bundle);
});
}
In my fragment class now i have something like this
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootview = inflater.inflate(R.layout.fragemnt, container, false);
if(getActivity().getClass().equals(ActivityA.class))
//Do things of activityA
else if(getActivity().getClass().equals(ActivityB.class)))
//Do things of activityB
return rootview;
}
Now this code work but doesn't seem the best way to archive my goal so have anyone some suggestion? Thanks

You can check the instance
In Java you can use instaceof
if(getActivity() instanceof ActivityA){
// Do things of activityA
}else if(getActivity() instanceof ActivityB){
// Do things of activityB
}
and for Kotlin
if(getActivity() is ActivityA){
// Do things of activityA
}else if(getActivity() is ActivityB){
// Do things of activityB
}

The correct way to do it is to define an interface in the Fragment, make the Activity implement the interface, and make the Fragment cast the Activity to the interface.
The following guide describes it: https://web.archive.org/web/20180405192526/https://developer.android.com/training/basics/fragments/communicating.html
public class HeadlinesFragment extends Fragment {
OnHeadlineSelectedListener mCallback;
// Container Activity must implement this interface
public interface OnHeadlineSelectedListener {
public void onArticleSelected(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 = (OnHeadlineSelectedListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement OnHeadlineSelectedListener");
}
}
...
}

Related

Sending data to the container activity

I have this issue of sending some data back and forth between a fragment and its container activity, I succeeded in doing it. What puzzles me is sending my data from the fragment to the activity, at first I implemented OnResume(), OnStop() and sent the data through an intent and that created an infinite loop so I removed them. Then I did setRetainInstance(true) and it worked and gave me the wanted behavior.
My Question is How my data are really being sent and where in the fragment lifecycle ?
The Right approach is to use Interfaces. Don't use onStop or setRetainInstance()
See this. It will solve you problem.
Pass data from fragment to actvity
You can also achieve this by using Interface, using an EventBus like LocalBroadcastManager, or starting a new Activity with an Intent and some form of flag passed into its extras Bundle or something else.
Here is an example about using Interface:
1. Add function sendDataToActivity() into the interface (EventListener).
//EventListener.java
public interface EventListener {
public void sendDataToActivity(String data);
}
2. Implement this functions in your MainActivity.
// MainActivity.java
public class MainActivity extends AppCompatActivity implements EventListener {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
#Override
public void sendDataToActivity(String data) {
Log.i("MainActivity", "sendDataToActivity: " + data);
}
}
3. Create the listener in MyFragment and attach it to the Activity.
4. Finally, call function using listener.sendDataToActivity("Hello World!").
// MyFragment.java
public class MyFragment extends Fragment {
private EventListener listener;
#Override
public void onAttach(Activity activity)
{
super.onAttach(activity);
if(activity instanceof EventListener) {
listener = (EventListener)activity;
} else {
// Throw an error!
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_my, container, false);
// Send data
listener.sendDataToActivity("Hello World!");
return view;
}
#Override
public void onDetach() {
super.onDetach();
listener = null;
}
}
Hope this will help~

FragmentTransaction within Fragment

im a new guy on this programming world so i guess this question is simple. So I have one imagebutton on my fragment and I whatI want is that when I click on it, it does a fragment transaction to another fragment; but the thing is that when I run the app and click on the imagebutton, it only "superimposes" the content of the other fragment into the one where the imagebutton is, of course what i want to do is just to go to the other fragment, ive been dealing with this for a while so I hope someone helps me, thanks.
Here is my java code of the fragment
public class inicio extends Fragment {
public inicio() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view=inflater.inflate(R.layout.fragment_inicio,container,false);
ImageButton luces = (ImageButton)view.findViewById(R.id.imageButton);
luces.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
interior interior= new interior();
FragmentManager fragmentManager = getActivity().getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.inicioo, interior).commit();
}
});
}
}
New code added...
public class transaction extends MainActivity
implements com.example.administradora.prueba.inicio.OnSelectedListener{
public void onButtonSelected() {
interior interior= new interior();
getSupportFragmentManager().beginTransaction().replace(R.id.inicioo, interior).commit();
}
}
but i get this error in the logcat:
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.administradora.prueba/com.example.administradora.prueba.MainActivity}: java.lang.ClassCastException: com.example.administradora.prueba.MainActivity#20f8fe9b must implement OnSelectedListener
You shouldn't replace a Fragment from another Fragment. Try to do that through the Activity.
Define some interface for the Fragment to hold
public class MyFragment extends Fragment {
OnSelectedListener mCallback;
// Container Activity must implement this interface
public interface OnSelectedListener {
public void onButtonSelected();
}
#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 = (OnSelectedListener ) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement OnSelectedListener");
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view=inflater.inflate(R.layout.fragment_inicio,container,false);
ImageButton luces = (ImageButton)view.findViewById(R.id.imageButton);
luces.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// Send the event to the host activity
if (mCallback != null) mCallback.onButtonSelected();
}
});
return view;
}
}
And swap the Fragment container in the interface implementation from the Activity.
public class MainActivity extends Activity
implements MyFragment.OnSelectedListener{
...
public void onButtonSelected() {
interior interior= new interior();
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.inicioo, interior)
.commit();
}
}
The first two sections of Communicating with Other Fragments is what you are looking for.

How do you implement callbacks in Android/Java? [duplicate]

I have this interface in my activity.
public interface LogoutUser {
void logout();
}
My fragment implements this interface, so in my fragment, I have this:
#Override
public void logout() {
// logout
}
In my activity I call
mLogoutUser.logout();
Where mLogoutUser is of the type LogoutUser interface.
My issue is the mLogoutUser object that is null. How can initialize it?
Thank you!
As I said in my comment, I resolved this issue using onAttach method in my fragment, but in this way you have to have the callback field (mLogoutUser in this case) declared in the fragment, and initialize it this way:
public class MyFragment extends ListFragment {
LogoutUser mLogoutUser;
// Container Activity must implement this interface
public interface LogoutUser {
public void logout();
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
// This makes sure that the container activity has implemented
// the callback interface. If not, it throws an exception
try {
mLogoutUser = (LogoutUser) context;
} catch (ClassCastException e) {
throw new ClassCastException(context.toString()
+ " must implement LogoutUser");
}
}
...
}
More info in Communicating with Other Fragments.
But if your case is the field declared in the activity, you can use the onAttachFragment method from your activity to initialize your listener field this way:
#Override
public void onAttachFragment(Fragment fragment) {
super.onAttachFragment(fragment);
mLogoutUser = (LogoutUser) fragment;
}
Also, you can use an event bus to make this communication between fragments and activities. An option is the Otto library, from Square.
Sample for creating callback from Fragment to Activity
public interface CallBackListener {
void onCallBack();// pass any parameter in your onCallBack which you want to return
}
CallBackFragment.class
public class CallBackFragment extends Fragment {
private CallBackListener callBackListener;
public CallBackFragment() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_call_back, container, false);
}
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
//getActivity() is fully created in onActivityCreated and instanceOf differentiate it between different Activities
if (getActivity() instanceof CallBackListener)
callBackListener = (CallBackListener) getActivity();
}
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
Button btn = (Button) view.findViewById(R.id.btn_click);
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(callBackListener != null)
callBackListener.onCallBack();
}
});
}
}
CallbackHandlingActivity.class
public class CallbackHandlingActivity extends AppCompatActivity implements CallBackListener
{
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_all_user);
}
#Override
public void onCallBack() {
Toast.makeText(mContext,"onCallback Called",Toast.LENGTH_LONG).show();
}
}
Android Fragments - Communicating with Activity
You need to get a reference to your fragment with getFragmentById() or getFragmentByTag()
getFragmentManager().findFragmentById(R.id.example_fragment);
You can use kotlinx Channel to send data or callback between fragments and activity or vice versa
In your Mainactivity:
val loginPromptChannel = Channel<LoginPromptState>()
val loginStateFlow = loginPromptChannel.receiveAsFlow()
//onCreate
lifecycleScope.launchWhenStarted {
loginStateFlow.collect() { state ->
when (state) {
is LoginPromptState.Login -> {
//smooth scroll to login fragment
binding.viewpager.setCurrentItem(2, true)
}
}
}
}
//create sealed a class
sealed class LoginPromptState {
object Login : LoginPromptState()
}
In your fragment send callback like:
lifecycleScope.launch {
val channelLogin = (activity as MainActivity).loginPromptChannel
channelLogin.send(MainActivity.LoginPromptState.Login)
}

How to make a callback between Activity and Fragment?

I have this interface in my activity.
public interface LogoutUser {
void logout();
}
My fragment implements this interface, so in my fragment, I have this:
#Override
public void logout() {
// logout
}
In my activity I call
mLogoutUser.logout();
Where mLogoutUser is of the type LogoutUser interface.
My issue is the mLogoutUser object that is null. How can initialize it?
Thank you!
As I said in my comment, I resolved this issue using onAttach method in my fragment, but in this way you have to have the callback field (mLogoutUser in this case) declared in the fragment, and initialize it this way:
public class MyFragment extends ListFragment {
LogoutUser mLogoutUser;
// Container Activity must implement this interface
public interface LogoutUser {
public void logout();
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
// This makes sure that the container activity has implemented
// the callback interface. If not, it throws an exception
try {
mLogoutUser = (LogoutUser) context;
} catch (ClassCastException e) {
throw new ClassCastException(context.toString()
+ " must implement LogoutUser");
}
}
...
}
More info in Communicating with Other Fragments.
But if your case is the field declared in the activity, you can use the onAttachFragment method from your activity to initialize your listener field this way:
#Override
public void onAttachFragment(Fragment fragment) {
super.onAttachFragment(fragment);
mLogoutUser = (LogoutUser) fragment;
}
Also, you can use an event bus to make this communication between fragments and activities. An option is the Otto library, from Square.
Sample for creating callback from Fragment to Activity
public interface CallBackListener {
void onCallBack();// pass any parameter in your onCallBack which you want to return
}
CallBackFragment.class
public class CallBackFragment extends Fragment {
private CallBackListener callBackListener;
public CallBackFragment() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_call_back, container, false);
}
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
//getActivity() is fully created in onActivityCreated and instanceOf differentiate it between different Activities
if (getActivity() instanceof CallBackListener)
callBackListener = (CallBackListener) getActivity();
}
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
Button btn = (Button) view.findViewById(R.id.btn_click);
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(callBackListener != null)
callBackListener.onCallBack();
}
});
}
}
CallbackHandlingActivity.class
public class CallbackHandlingActivity extends AppCompatActivity implements CallBackListener
{
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_all_user);
}
#Override
public void onCallBack() {
Toast.makeText(mContext,"onCallback Called",Toast.LENGTH_LONG).show();
}
}
Android Fragments - Communicating with Activity
You need to get a reference to your fragment with getFragmentById() or getFragmentByTag()
getFragmentManager().findFragmentById(R.id.example_fragment);
You can use kotlinx Channel to send data or callback between fragments and activity or vice versa
In your Mainactivity:
val loginPromptChannel = Channel<LoginPromptState>()
val loginStateFlow = loginPromptChannel.receiveAsFlow()
//onCreate
lifecycleScope.launchWhenStarted {
loginStateFlow.collect() { state ->
when (state) {
is LoginPromptState.Login -> {
//smooth scroll to login fragment
binding.viewpager.setCurrentItem(2, true)
}
}
}
}
//create sealed a class
sealed class LoginPromptState {
object Login : LoginPromptState()
}
In your fragment send callback like:
lifecycleScope.launch {
val channelLogin = (activity as MainActivity).loginPromptChannel
channelLogin.send(MainActivity.LoginPromptState.Login)
}

Basic communication between two fragments

I have one activity - MainActivity. Within this activity I have two fragments, both of which I created declaratively within the xml.
I am trying to pass the String of text input by the user into Fragment A to the text view in Fragment B. However, this is proving to be very difficult. Does anyone know how I might achieve this?
I am aware that a fragment can get a reference to it's activity using getActivity(). So I'm guessing I would start there?
Have a look at the Android developers page:
http://developer.android.com/training/basics/fragments/communicating.html#DefineInterface
Basically, you 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 with the received value
Your Activity implements your interface (See FragmentA below)
public class YourActivity implements FragmentA.TextClicked{
#Override
public void sendText(String text){
// Get Fragment B
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, thanks #Deepscorn
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
}
}
Some of the other examples (and even the documentation at the time of this writing) use outdated onAttach methods. Here is a full updated example.
Notes
You don't want the Fragments talking directly to each other or to the Activity. That ties them to a particular Activity and makes reuse difficult.
The solution is to make an callback listener interface that the Activity will implement. When the Fragment wants to send a message to another Fragment or its parent activity, it can do it through the interface.
It is ok for the Activity to communicate directly to its child fragment public methods.
Thus the Activity serves as the controller, passing messages from one fragment to another.
Code
MainActivity.java
public class MainActivity extends AppCompatActivity implements GreenFragment.OnGreenFragmentListener {
private static final String BLUE_TAG = "blue";
private static final String GREEN_TAG = "green";
BlueFragment mBlueFragment;
GreenFragment mGreenFragment;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// add fragments
FragmentManager fragmentManager = getSupportFragmentManager();
mBlueFragment = (BlueFragment) fragmentManager.findFragmentByTag(BLUE_TAG);
if (mBlueFragment == null) {
mBlueFragment = new BlueFragment();
fragmentManager.beginTransaction().add(R.id.blue_fragment_container, mBlueFragment, BLUE_TAG).commit();
}
mGreenFragment = (GreenFragment) fragmentManager.findFragmentByTag(GREEN_TAG);
if (mGreenFragment == null) {
mGreenFragment = new GreenFragment();
fragmentManager.beginTransaction().add(R.id.green_fragment_container, mGreenFragment, GREEN_TAG).commit();
}
}
// The Activity handles receiving a message from one Fragment
// and passing it on to the other Fragment
#Override
public void messageFromGreenFragment(String message) {
mBlueFragment.youveGotMail(message);
}
}
GreenFragment.java
public class GreenFragment extends Fragment {
private OnGreenFragmentListener mCallback;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_green, container, false);
Button button = v.findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String message = "Hello, Blue! I'm Green.";
mCallback.messageFromGreenFragment(message);
}
});
return v;
}
// This is the interface that the Activity will implement
// so that this Fragment can communicate with the Activity.
public interface OnGreenFragmentListener {
void messageFromGreenFragment(String text);
}
// This method insures that the Activity has actually implemented our
// listener and that it isn't null.
#Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof OnGreenFragmentListener) {
mCallback = (OnGreenFragmentListener) context;
} else {
throw new RuntimeException(context.toString()
+ " must implement OnGreenFragmentListener");
}
}
#Override
public void onDetach() {
super.onDetach();
mCallback = null;
}
}
BlueFragment.java
public class BlueFragment extends Fragment {
private TextView mTextView;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_blue, container, false);
mTextView = v.findViewById(R.id.textview);
return v;
}
// This is a public method that the Activity can use to communicate
// directly with this Fragment
public void youveGotMail(String message) {
mTextView.setText(message);
}
}
XML
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp">
<!-- Green Fragment container -->
<FrameLayout
android:id="#+id/green_fragment_container"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:layout_marginBottom="16dp" />
<!-- Blue Fragment container -->
<FrameLayout
android:id="#+id/blue_fragment_container"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
</LinearLayout>
fragment_green.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:background="#98e8ba"
android:padding="8dp"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="#+id/button"
android:text="send message to blue"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
fragment_blue.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:background="#30c9fb"
android:padding="16dp"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/textview"
android:text="TextView"
android:textSize="24sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
The nicest and recommended way is to use a shared ViewModel.
https://developer.android.com/topic/libraries/architecture/viewmodel#sharing
From Google doc:
public class SharedViewModel extends ViewModel {
private final MutableLiveData<Item> selected = new MutableLiveData<Item>();
public void select(Item item) {
selected.setValue(item);
}
public LiveData<Item> getSelected() {
return selected;
}
}
public class MasterFragment extends Fragment {
private SharedViewModel model;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
itemSelector.setOnClickListener(item -> {
model.select(item);
});
}
}
public class DetailFragment extends Fragment {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SharedViewModel model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
model.getSelected().observe(this, { item ->
// Update the UI.
});
}
}
ps: two fragments never communicate directly
Consider my 2 fragments A and B, and Suppose I need to pass data from B to A.
Then create an interface in B, and pass the data to the Main Activity. There create another interface and pass data to fragment A.
Sharing a small example:
Fragment A looks like
public class FragmentA extends Fragment implements InterfaceDataCommunicatorFromActivity {
public InterfaceDataCommunicatorFromActivity interfaceDataCommunicatorFromActivity;
String data;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// TODO Auto-generated method stub
return super.onCreateView(inflater, container, savedInstanceState);
}
#Override
public void updateData(String data) {
// TODO Auto-generated method stub
this.data = data;
//data is updated here which is from fragment B
}
#Override
public void onAttach(Activity activity) {
// TODO Auto-generated method stub
super.onAttach(activity);
try {
interfaceDataCommunicatorFromActivity = (InterfaceDataCommunicatorFromActivity) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement TextClicked");
}
}
}
FragmentB looks like
class FragmentB extends Fragment {
public InterfaceDataCommunicator interfaceDataCommunicator;
#Override
public void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
// call this inorder to send Data to interface
interfaceDataCommunicator.updateData("data");
}
public interface InterfaceDataCommunicator {
public void updateData(String data);
}
#Override
public void onAttach(Activity activity) {
// TODO Auto-generated method stub
super.onAttach(activity);
try {
interfaceDataCommunicator = (InterfaceDataCommunicator) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement TextClicked");
}
}
}
Main Activity is
public class MainActivity extends Activity implements InterfaceDataCommunicator {
public InterfaceDataCommunicatorFromActivity interfaceDataCommunicatorFromActivity;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
#Override
public void updateData(String data) {
// TODO Auto-generated method stub
interfaceDataCommunicatorFromActivity.updateData(data);
}
public interface InterfaceDataCommunicatorFromActivity {
public void updateData(String data);
}
}
There are multiple ways to communicate between fragments.
Traditional way of communication via interface Example
Via ViewModel if you are following MVVM pattern Example
BroadcastReceivers: via LocalBraodcastManager Example or EventBus Example etc...
Take a look at https://github.com/greenrobot/EventBus
or http://square.github.io/otto/
or even ... http://nerds.weddingpartyapp.com/tech/2014/12/24/implementing-an-event-bus-with-rxjava-rxbus/
There is a simple way to implement communication between fragments of an activity using architectural components. Data can be passed between fragments of an activity using ViewModel and LiveData.
Fragments involved in communication need to use the same view model objects which is tied to activity life cycle. The view model object contains livedata object to which data is passed by one fragment and the second fragment listens for changes on LiveData and receives the data sent from fragment one.
For complete example see http://www.zoftino.com/passing-data-between-android-fragments-using-viewmodel
Since Fragment 1.3.0 we have available a new way to communicate between fragments.
As of Fragment 1.3.0, each FragmentManager implements FragmentResultOwner.
That means that a FragmentManager can act as a central storage for fragment results. This change allows components to communicate with each other by setting chunk results and listening to those results without those components having direct references to each other.
Fragment listener:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Use the Kotlin extension in the fragment-ktx artifact
setFragmentResultListener("requestKey") { requestKey, bundle ->
// We use a String here, but any type that can be put in a Bundle is supported
val result = bundle.getString("bundleKey")
// Do something with the result
}
}
Fragment emitter:
button.setOnClickListener {
val result = "result"
// Use the Kotlin extension in the fragment-ktx artifact
setFragmentResult("requestKey", bundleOf("bundleKey" to result))
}
Learn " setTargetFragment() "
Where " startActivityForResult() " establishes a relationship between 2 activities, " setTargetFragment() " defines the caller/called relationship between 2 fragments.
I give my activity an interface that all the fragments can then use. If you have have many fragments on the same activity, this saves a lot of code re-writing and is a cleaner solution / more modular than making an individual interface for each fragment with similar functions. I also like how it is modular. The downside, is that some fragments will have access to functions they don't need.
public class MyActivity extends AppCompatActivity
implements MyActivityInterface {
private List<String> mData;
#Override
public List<String> getData(){return mData;}
#Override
public void setData(List<String> data){mData = data;}
}
public interface MyActivityInterface {
List<String> getData();
void setData(List<String> data);
}
public class MyFragment extends Fragment {
private MyActivityInterface mActivity;
private List<String> activityData;
public void onButtonPress(){
activityData = mActivity.getData()
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof MyActivityInterface) {
mActivity = (MyActivityInterface) context;
} else {
throw new RuntimeException(context.toString()
+ " must implement MyActivityInterface");
}
}
#Override
public void onDetach() {
super.onDetach();
mActivity = null;
}
}
You can user 2 approcach to communicate between 2 fragments:
1 )
You can use LiveData to observe data changes of one fragment in another
Create shared ViewModel
public class SharedViewModel extends ViewModel {
private MutableLiveData<String> name;
public void setNameData(String nameData) {
name.setValue(nameData);
}
public MutableLiveData<String> getNameData() {
if (name == null) {
name = new MutableLiveData<>();
}
return name;
}
}
Fragment One
private SharedViewModel sharedViewModel;
public FragmentOne() {
}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
sharedViewModel = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
submitButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
sharedViewModel.setNameData(submitText.getText().toString());
}
});
}
Fragment Two
private SharedViewModel sharedViewModel;
public FragmentTwo() {
}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
sharedViewModel = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
sharedViewModel.getNameData().observe(this, nameObserver);
}
Observer<String> nameObserver = new Observer<String>() {
#Override
public void onChanged(String name) {
receivedText.setText(name);
}
};
For more details on viewmodel you can refer to : mvvm-viewmodel-livedata , communicate fragments
2 )
You can use eventbus to achieve the same
implementation 'org.greenrobot:eventbus:3.2'
Define Event
public static class MessageEvent { /* Additional fields if needed */ }
Register/Unregister Subsciber
#Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
#Override
public void onStop() {
super.onStop();
EventBus.getDefault().unregister(this);
}
Listen To Events
#Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(MessageEvent event) {/* Do something */};
Post Events
EventBus.getDefault().post(new MessageEvent());
Basically, following are the ways for communication between two fragments:
i) ViewModel
ii) Fragment Result API
iii) Interface
I use many fragments on tabs that need to share data between them, such as a ble scan tab that needs up update a device id on a settings tab.
The communication is a mess for something simple like one edittext.
My solution was to save data to sharedpreferences and use the fragment onResume to read and update.
I can extend the fields in Sharedpreferences later if I need to as well.
Update
Ignore this answer. Not that it doesn't work. But there are better methods available. Moreover, Android emphatically discourage direct communication between fragments. See official doc. Thanks user #Wahib Ul Haq for the tip.
Original Answer
Well, you can create a private variable and setter in Fragment B, and set the value from Fragment A itself,
FragmentB.java
private String inputString;
....
....
public void setInputString(String string){
inputString = string;
}
FragmentA.java
//go to fragment B
FragmentB frag = new FragmentB();
frag.setInputString(YOUR_STRING);
//create your fragment transaction object, set animation etc
fragTrans.replace(ITS_ARGUMENTS)
Or you can use Activity as you suggested in question..
I recently created a library that uses annotations to generate those type casting boilerplate code for you.
https://github.com/zeroarst/callbackfragment
Here is an example. Click a TextView on DialogFragment triggers a callback to MainActivity in onTextClicked then grab the MyFagment instance to interact with.
public class MainActivity extends AppCompatActivity implements MyFragment.FragmentCallback, MyDialogFragment.DialogListener {
private static final String MY_FRAGM = "MY_FRAGMENT";
private static final String MY_DIALOG_FRAGM = "MY_DIALOG_FRAGMENT";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getSupportFragmentManager().beginTransaction()
.add(R.id.lo_fragm_container, MyFragmentCallbackable.create(), MY_FRAGM)
.commit();
findViewById(R.id.bt).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
MyDialogFragmentCallbackable.create().show(getSupportFragmentManager(), MY_DIALOG_FRAGM);
}
});
}
Toast mToast;
#Override
public void onClickButton(MyFragment fragment) {
if (mToast != null)
mToast.cancel();
mToast = Toast.makeText(this, "Callback from " + fragment.getTag() + " to " + this.getClass().getSimpleName(), Toast.LENGTH_SHORT);
mToast.show();
}
#Override
public void onTextClicked(MyDialogFragment fragment) {
MyFragment myFragm = (MyFragment) getSupportFragmentManager().findFragmentByTag(MY_FRAGM);
if (myFragm != null) {
myFragm.updateText("Callback from " + fragment.getTag() + " to " + myFragm.getTag());
}
}
}

Categories

Resources