I am trying to send data from activity to fragment and vice versa using interfaces but getting an error of cycling inheritance involving MyFragment.
Implementing interface created in MyFragment:
public class MyActivity implements OnSendFromMyFragListener {
OnSendFromMyActivityListener mCallback;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mCallback.sendFromMyActivity(2);
}
#Override
public void sendFromMyFrag (int a) {
//do something
}
public interface OnSendFromMyActivityListener {
public void sendFromMyActivity(int b);
}
}
Implementing interface created in MyActivity:
public class MyFragment extends Fragment implements OnSendFromMyActivityListener {
OnSendFromMyFragListener mCallback;
public interface OnSendFromMyFragListener {
void sendFromMyFrag(int a);
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
try {
mCallback = (OnSendFromMyFragListener) context;
} catch (ClassCastException e) {
throw new ClassCastException(context.toString());
}
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_my, container, false);
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mCallback.sendFromMyFrag(1);
}
}
});
return view;
}
#Override
public void sendFromMyActivity(int b) {
//do something
}
}
Well the reason you are getting this error is because your Activity depends on your Fragment and Fragment depends on your Activity. Don't Agree?
Let me show you. Imagine you are a compiler in your work you stumbled across:
class A implements B.A {
interface B {
void foo1();
}
#Override
public void foo2()
{
// do something;
}
}
Now you know that class A depends on (implements) B.A, so before you go further into class A you move on to class B:
class B implements A.B {
interface A {
void foo2();
}
#Override
public void foo1()
{
// Do Something;
}
}
Now you see that class B depends on (implements) class A specifically class A.B! What do you (compiler) do? Go back to class A? But that depends on class B. So you see this becomes an unending cycle thus causing a cyclic dependency where both your class' definitions depend on each other.
As an alternative you could either create a member event listener or an anonymous one. Or if you don't like either of those options, you could also create a separate java interface class file for any one of the two interfaces.
Maybe you have to do this for Activity
public class MyActivity extends AppCompatActivity implements MyFragment.OnSendFromMyFragListener {
public interface OnSendFromMyActivityListener {
void sendFromMyActivity(int b);
}
OnSendFromMyActivityListener mCallback;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
MyFragment myFragment = MyFragment.newInstance();
//Do the transaction.....
//And after this
mCallback.sendFromMyActivity(0);
}
public void setOnSendFromMyActivityListener(OnSendFromMyActivityListener mCallback){
this.mCallback = mCallback;
}
#Override
public void sendFromMyFrag(int a) {
//do something
}}
And this for Fragment
public class MyFragment extends Fragment implements OnSendFromMyActivityListener {
OnSendFromMyFragListener mCallback;
public interface OnSendFromMyFragListener {
void sendFromMyFrag(int a);
}
public static MyFragment newInstance() {
Bundle args = new Bundle();
MyFragment fragment = new MyFragment();
fragment.setArguments(args);
return fragment;
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
try {
mCallback = (OnSendFromMyFragListener) getActivity();
((MyActivity) getActivity()).setOnSendFromMyActivityListener(this);
} catch (ClassCastException e) {
throw new ClassCastException(context.toString());
}
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_place_suggest, container, false);
view.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mCallback.sendFromMyFrag(1);
}
});
return view;
}
#Override
public void sendFromMyActivity(int b) {
//do something
}
if you want you can put setters of the interface also to the Fragment for better abstraction.
Something you have to watch out is to look for nulls interfaces
Thanks
Related
I am calling a Fragment Dialogue from a Fragment, and when I try to impliment a callback it isn't calling back to the Fragment, I think it is calling back to the Activity. I believe this is by design from Google, is there anyway around that? How can couple the Dialogue Fragment to the Fragment?
My code is as follows;
Dialogue Fragment
public class CustomDatePicker extends DialogFragment {
private DateListener listener;
...
public interface DateListener {
void onDateComplete();
}
}
Fragment
public class User extends Fragment implements CustomDatePicker.DateListener {
...
#Override
public void onDateComplete() {
Log.i("TEST", "onDateComplete: " + date);
}
}
For those purposes you can use setTargetFragment()and onActivityResult().
Check this: Receive result from DialogFragment or Callback to a Fragment from a DialogFragment. In this case you dont need any interfaces.
Interfaces:
public class CustomDatePicker extends DialogFragment {
private DateListener listener;
public interface DateListener {
void onDateComplete(long date);
}
public void setListener(DateListener listener) {
this.listener = listener;
}
public void sendToFragment(long value) { //value you want to pass
if (listener != null) {
listener.onDateComplete(value);
}
}
}
and
public class User extends Fragment implements CustomDatePicker.DateListener {
public void showDialog(){
CustomDatePicker datePicker = new CustomDatePicker();
datePicker.setListener(this); // since User fragment implements it
...
datePicker.show(getFragmentManager(), "TAG");
}
#Override
public void onDateComplete(long date) {
Log.d("Interface", "onDateComplete: " + date);
}
}
Call sendToFragment() with your value where you need, hope this help.
instead of implementing your listener to your fragment get a new instance of your dialog fragment and new your listener when you want to call it
add newInstance to your dialog fragment like this:
public static CustomDatePicker newInstance(DateListener listener) {
return new CustomDatePicker();
}
and show your dialog fragment in this way:
CustomDatePicker.newInstance(new CustomDatePicker.DateListener() {
#Override
public void onDialogPositiveListener(DialogFragment dialog) {
}).show(getFragmentManager(), "tag");
so you can get result in your code
What I ended up doing, based off of comments and answers was the following.
Dialogue Fragment;
public class CustomDatePicker extends DialogFragment {
private DateListener listener;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
...
Button btnSave = (Button) v.findViewById(R.id.save);
btnSave.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
getDate();
dismiss();
listener.onDateComplete();
}
});
...
}
...
public interface DateListener {
void onDateComplete();
}
}
Fragment;
public class User extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
...
CustomDatePicker customDatePicker = new CustomDatePicker();
customDatePicker.setListener(new CustomDatePicker.DateListener() {
#Override
public void onDateComplete() {
adapter.notifyDataSetChanged();
}
});
...
}
...
}
So question. Which of the two is the proper way to do a callback?(Unless both of these are incorrect) Or does it depend on the case.
For example. Let's say i wanted to return something from a Fragment to its Activity.
1)I implement an interface in the Fragment and then on its onAttach I set the listener, and then implement the interface in the activity.
public class UrgentCareFragment{
public interface TestListener {
void finishedTest();
}
TestListener mEventListener;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_urgent_care_viewpager_fragment, container, false);
mEventListener.finishedTest();
return v;
}
#TargetApi(23)
#Override
public void onAttach(Context context) {
super.onAttach(context);
try {
mEventListener = (TestListener) context;
} catch (ClassCastException e) {
throw new ClassCastException(context.toString() + " must implement urgentCareListener");
}
}
/*
* Deprecated on API 23
* Use onAttachToContext instead
*/
#SuppressWarnings("deprecation")
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
if (Build.VERSION.SDK_INT < 23) {
try {
mEventListener = (TestListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString() + " must implement urgentCareListener");
}
}
}
}
Activity Class.
public class testActivity extends AppCompatActivity implements TestListener{
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_urgent_care);
}
#Override
public void finishedTest(){
//do what you need to do
}
}
2) I create an interface class. In the Fragment I set the listener to the activities context by using getActivity. And then implement the interface in the activity.
Interface Class
public interface TestListener {
void finishedTest();
}
Fragment
public class UrgentCareFragment{
TestListener mEventListener;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mEventListener = (TestListener) getActivity();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_urgent_care_viewpager_fragment, container, false);
mEventListener.finishedTest();
return v;
}
}
Activity Class.
public class testActivity extends AppCompatActivity implements TestListener{
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_urgent_care);
}
#Override
public void finishedTest(){
//do what you need to do
}
}
Which is the correct way? My main concern is the way the listener is set. Could either of these two cause issues. Or is there one i should throw out. Both seem to work properly, I am just trying to make sure what the correct way is, or if both are fine.
Thanks
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)
}
I'm going insane!!
I don't manage to pass data from a fragment(A) to an another fragment(B).
I read about using a public interface... and it seems to work, but i don't understand how to use this method.
Fragment(A)
package it.anddev.pagertabs;
public class Page1Fragment extends Fragment {
String Str;
OnDataPass dataPasser;
Class Senddata extends AsyncTask<String, String, String> {
#Override
protected void onPreExecute() {
// do something [...]
}
#Override
protected String doInBackground(String... args) {
// do something [...]
dataPasser.onDataPass(result_array.toString());
}
return null;
}
protected void onPostExecute(String file_url) {
// do something [...]
}};
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
dataPasser = (OnDataPass) activity;
}
public interface OnDataPass {
public void onDataPass(String data);
}
#Override
public View onCreateView(LayoutInflater inflater,ViewGroup container,Bundle savedInstanceState) {
// do something [...]
Button Avanti = (Button) view.findViewById(R.id.sendbutton);
Avanti.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View view) {
//mViewPager.setCurrentItem(1, true);
new Senddata().execute();
}
});
FragmentB
public View onCreateView(LayoutInflater inflater,ViewGroup container,Bundle savedInstanceState) {
if (container == null) {
return null;
}
View view = (LinearLayout)inflater.inflate(R.layout.page2,container,false);
Button mostra = (Button) view.findViewById(R.id.mostrabutton);
// String str = i need to get string from "public void onDataPass"
mostra.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View view) {
//mViewPager.setCurrentItem(1, true);
Log.d("IT WORKS", str);
}
});
return view;
}
#Override
public void onDataPass(final String data) {
// TODO Auto-generated method stub
Log.d("okkkkkk", "" + data);
}
So finally, how i can get the string from the public void in the fragmentB?
Thanks
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) {
return super.onCreateView(inflater, container, savedInstanceState);
}
#Override
public void updateData(String data) {
this.data = data;
//data is updated here which is from fragment B
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
interfaceDataCommunicatorFromActivity = (InterfaceDataCommunicatorFromActivity) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement InterfaceDataCommunicatorFromActivity");
}
}
}
FragmentB looks like
class FragmentB extends Fragment {
public InterfaceDataCommunicator interfaceDataCommunicator;
#Override
public void onCreate(Bundle savedInstanceState) {
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) {
super.onAttach(activity);
try {
interfaceDataCommunicator = (InterfaceDataCommunicator) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement InterfaceDataCommunicator");
}
}
}
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) {
interfaceDataCommunicatorFromActivity.updateData(data);
}
public interface InterfaceDataCommunicatorFromActivity {
public void updateData(String data);
}
}
The Most easiest way is create a static variable in first fragment and use this variable in second fragment.
Use a public interface to pass the String through the Activity that is containing the fragments. Here is simular question and solution with more detail.
Consider 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 Activity. From there you can call in A getActivity() to get the information.
Example:
Activity is:
public class MainActivity extends Activity implements InterfaceDataCommunicator {
public static String data;
#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) {
this.data = data;
}
}
Fragment A:
public class FragmentA extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
getActivity().data;//receive data
return super.onCreateView(inflater, container, savedInstanceState);
}
}
Fragment B:
class FragmentB extends Fragment {
public InterfaceDataCommunicator interfaceDataCommunicator;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
interfaceDataCommunicator.updateData(/*the String to send*/); //send data
}
public interface InterfaceDataCommunicator {
public void updateData(String data);
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
interfaceDataCommunicator = (InterfaceDataCommunicator) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement InterfaceDataCommunicator");
}
}
}
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)
}