call activity method from inner viewpager fragments - android

I'm working on an android app and I've got a viewPager with 4 fragments.
In each fragment there are some input views.
Is it possible to declare a method in the activity that read each input view values, called on each input views status change?
Thank you
Alessandro

Yes, it is possible. Follow these steps.
Make an interface and declare a method.
Let the activity implement that interface
Now override the method from interface and write the definition for the function.
Make the object of the interface in the fragment.
Using the object call that method whenever needed.
Example using code : -
Inteface Code :-
//use any name
public interface onInputChangeListener {
/*To change something in activty*/
public void changeSomething(//parameters that will hold the new information);
}
Activity Code:-
public class MyActivity extends AppCompatActivity implements onInputChangeListener {
onCreate();
#override
public void changeSomething(/*Arguments with new information*/){
//do whatever this function need to change in activity
// i.e give your defination to the function
}
}
Fragment Code:-
public class MyFragment extends Fragment {
onInputChangeListener inputChangeCallback;
/*This method onAttach is optional*/
#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 {
inputChangeCallback = (onInputChangeListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement onFragmentChangeListener");
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_my,container,false);
inputChangeCallback.changeSomething(//pass the new information);
return v;
}
}
This would doo.. Cheers!
If you want a quick Fix :-
In your Fragment:-
public class MyFragment extends Fragment {
MyActivity myActivity;
onCreateView(){
...
myActivity = (MyActivity)getActivity;
myActivity.callAnyFunctionYouWant();
...
}
}

Related

Activity crashes when minimized and a dialog is opened

On my activity a show a Custom DialogFragement. But when I minimize the app while the dialog is opened i get this error
java.lang.RuntimeException: Parcelable encountered IOException writing serializable object (name = xxx.xxx.MyActivity$24)
at android.os.Parcel.writeSerializable(Parcel.java:1468)
at android.os.Parcel.writeValue(Parcel.java:1416)
at android.os.Parcel.writeArrayMapInternal(Parcel.java:686)
at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1330)
at android.os.Bundle.writeToParcel(Bundle.java:1079)
at android.os.Parcel.writeBundle(Parcel.java:711)
at android.support.v4.app.FragmentState.writeToParcel(Fragment.java:144)
at android.os.Parcel.writeTypedArray(Parcel.java:1254)
at ....
... 23 more
java.io.NotSerializableException: xxx.xxx.MyActivity
at java.io.ObjectOutputStream.writeNewObject(ObjectOutputStream.java:1344)
xxxxx
For the moment to fix this I have set this in the dialog fragment which seems to works but I don't like these kind of of dirty tricks
#Override
public void onPause() {
dismiss();
super.onPause();
}
Anyone can tell me what causes this exception in the first place ?
The onCreate of the custom DialogFragment:
public static MyCustomDialog newInstance(double lat, double lng, MyListener listener) {
MyCustomDialog dialogFragment = new MyCustomDialog();
Bundle bundle = new Bundle();
bundle.putDouble(ARG_LAT, lat);
bundle.putDouble(ARG_LNG, lng);
bundle.putSerializable(ARG_LISTENER, listener);
dialogFragment.setArguments(bundle);
return dialogFragment;
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.dialog_layout, null, false);
final double lat = getArguments().getDouble(ARG_LAT);
final double lng = getArguments().getDouble(ARG_LNG);
listener = (MyListener) getArguments().getSerializable(ARG_LISTENER);
TextView saveBtn = (TextView) view.findViewById(R.id.save_btn);
saveBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
/* some processing */
listener.onSave();
}
});
return view;
}
MyListener is an interface. I thought at first that this could have been the problem but even if I dont put in the bundle I get the same error, so it must be from something else.
Activity side inside a button on click:
MyCustomDialog myCustomDialog = MmyCustomDialog.newInstance(lat, lon, listener);
myCustomDialog.show(getSupportFragmentManager(), "tag");
I see this line in your code:
listener = (MyListener) getArguments().getSerializable(ARG_LISTENER);
If I guess correctly, MyListener is implemented either by an inner class of your Activity or by the Activity itself. Either way, MyListener holds a reference to the Activity. Please note that if your are creating an anonymous inner class, that is:
MyListener listener = new MyListener() { ... }
it still holds a reference to your Activity.
When the Fragment needs to be destroyed, the arguments Bundle is stored in the fragment state Parcel. Android tries to serialize MyListener, however Activity is not Serializable and it fails.
The usual pattern is to declare your listener interface in your Fragment:
public class MyFragment extends Fragment {
...
public interface Callbacks {
void doSomething();
}
...
Then an Activity that hosts the Fragment implements this interface:
public class MyActivity extends AppCompatActivity implements MyFragment.Callbacks {
...
#Override
public void doSomething() {
// do your stuff
}
...
In onAttach of your Fragment you cast the Fragment's host to your Callbacks and set the field.
#Override
public void onAttach(Context context) {
super.onAttach(context);
if (getHost() instanceof Callbacks) {
mCallbacks = (Callbacks) getHost();
} else {
throw new AssertionError("Host does not implement Callbacks!");
}
}
PS In the documentation you may see a version of onAttach method taking an Activity. The version above is deprecated from API 23. Until then, Fragments could be hosted only by Activities. Now, Fragments can be hosted by arbitrary objects and operate in any Context. That being said, you probably will host your Fragments inside Activities anyway, and your context and host will be the same.
try using this method
dismissAllowingStateLoss();
instead of
dismiss();

Android MVP - How to communicate between activity presenter and fragment presenter

I have an activity with 3 fragments, currently I use ViewPager. I want to implement MVP and communicate between activity presenter and fragment presenters i.e:
Passing data from activity presenter to fragment presenters
Sending event from fragment presenters to activity presenter
...
But I don't know how to do it in official way. I can use BusEvent but I don't think it's a good practice.
Communication between fragments and activity or vice-versa can be done by using
nnn's answer or you could use ViewModel and LiveData witch provides a cleaner way and respect the lifecycle from fragments and activities which can save from writing a few lines of code in attempt to prevent a a non-visible fragment from receiving data on the background.
First you extend the ViewModel class, initialize the Livedata and some helper methods.
public class MyViewModel extends ViewModel {
private MutableLiveData<String> toFragmentA, toFragmentB;
private MutableLiveData<List<String>> toAllFragments;
public MyViewModel() {
toFragmentA = new MutableLiveData<>();
toFragmentB = new MutableLiveData<>();
toAllFragments = new MutableLiveData<>();
}
public void changeFragmentAData(String value){
toFragmentA.postValue(value);
}
public void changeFragmentBData(String value){
toFragmentB.postValue(value);
}
public void changeFragmentAllData(List<String> value){
toAllFragments.postValue(value);
}
public LiveData<String> getToFragmentA() {
return toFragmentA;
}
public LiveData<List<String>> getToAllFragments() {
return toAllFragments;
}
public LiveData<String> getToFragmentB() {
return toFragmentB;
}
}
Then you initialize the ViewModel on your activity.
public class MainActivity extends AppCompatActivity {
private ViewPager viewPager;
private TabLayout tabLayout;
MyViewModel mViewModel;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mViewModel = ViewModelProviders.of(this)
.get(MyViewModel.class);
viewPager.setAdapter(new Adapter(getSupportFragmentManager()));
}
}
reading the data in the fragments:
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mViewModel = ViewModelProviders.of(getActivity()).get(MyViewModel.class);
mViewModel.getToAllFragments().observe(this, new Observer<List<String>>() {
#Override
public void onChanged(List<String> s) {
myList.addAll(s);
//do something like update a RecyclerView
}
});
mViewModel.getToFragmentA().observe(this, new Observer<String>() {
#Override
public void onChanged(String s) {
mytext = s;
//do something like update a TextView
}
});
}
to change the values of any of the live datas you can use one of the methods in any of the fragments or in the activity:
changeFragmentAData();
changeFragmentBData();
changeFragmentAllData();
Whats happing behind the scenes:
when you use mViewModel = ViewModelProviders.of(this).get(MyViewModel.class) you are creating a n instance of ViewModel and binding it to the lifecycle of the given activity of fragment so the view model is destroid only the the activity or fragement is stopped. if you use mViewModel = ViewModelProviders.of(getActivity()).get(MyViewModel.class)you are bindig it to the lifecycle if the parentactivity`
when you use mViewModel.getToFragmentA().observe() or mViewModel.getToFragmentB().observe() or mViewModel.getToAllFragments().observe() you are connecting the LiveData in MyViewModel class to the given fragment or activity an the value of the onChange() method is updated in all the classes that are observing the method.
I recomend for personal expirience a bit of research about Livedata end ViewModel which ou can on youtube or this link
As per my understanding, for your UseCase, suppose ActivityA have a viewPager having 3 Fragments(FragmentA, FragmentB, FragmentC).
ActivityA have ActivityPresenterA
FragmentA have FragmentPresenterA
As per MVP, FragmentPresenterA should be responsible for all the logical and business flows of FragmentA only and should communicate with FragmentA only. Therefore, FragmentPresenterA can not directly communicate with ActivityPresenterA.
For communication from Fragment to Activity, presenter should not be involved and this should be done as we would communicate in non-MVP architecture, i.e. with the help of interface.
Same applies for Activity to Fragment communication.
For communication between Activity and Fragment read here
You can use one presenter for that case.
Used your Activity Presenter to get all the data that your fragments need.
then create an interface class and implement it to your fragments.
For example:
Create a public interface for your PageAFragment (this interface will the bridge of your data from activity to fragment). and use the method of your interface to handle the result from your presenter to view.
This is the example of interface class that I created for received data. for the parameter you can choose what you want it depends on your need, but for me I choose model.
public interface CallbackReceivedData {
void onDataReceived(YourModel model);
}
In MainActivity Class check the instance of fragment that attached into your activity. put your checking instance after you commit the fragment.
public class MainActivity extends AppCompatActivity{
private CallbackReceivedData callbackReceivedData;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//after commit the fragment
if (fragment instanceof PageAFragment){
callbackReceivedData = (CallbackReceivedData)fragment;
}
}
//this is the example method of MainActivity Presenter,
//Imagine it, as your view method.
public void receivedDataFromPresenter(YourModel model){
callbackReceivedData.onDataReceived(model);
}
}
I assumed that the receivedDataFromPresenter is the received method of our view and get data to presenter.
And now we will pass the data from presenter to callbackReceivedData
In PageAFragment implement the CallbackReceivedData and Override the onDataReceived method. Now you can passed the data from activity to your fragment.
public class PageAFragment extends Fragment implements CallbackReceivedData{
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public void onDataReceived(YourModel model) {
}
}
Note: Alternative way, you can use Bundle and pass the data with the use of setArguments.
If you want to send Event from Fragment to Activity you can follow this Idea.
Create an Interface class and implement it to your MainActivity and Override the method from interface to your activity, for My case I do it something like this.
Here's my CallbackSendData Class.
public interface CallbackSendData {
void sendDataEvent(String event);
}
Implement CallbackSendData interface to your MainActivity and Override the sendDataEvent method.
public class MainActivity extends AppCompatActivity implements CallbackSendData{
private CallbackReceivedData callbackReceivedData;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//after commit the fragment
if (fragment instanceof PageAFragment){
callbackReceivedData = (CallbackReceivedData)fragment;
}
}
//this is the example method of MainActivity Presenter,
//Imagine it, as your view method.
public void receivedDataFromPresenter(YourModel model){
callbackReceivedData.onDataReceived(model);
}
#Override
public void sendDataEvent(String event){
//You can now send the data to your presenter here.
}
}
And to your PageAFragment you need to use attach method to cast your interface. The attach method called once the fragment is associated with its activity. If you want to understand the lifecycle of fragment just click this link: https://developer.android.com/reference/android/app/Fragment.html.
public class PageAFragment extends Fragment implements CallbackReceivedData{
private CallbackSendData callbackSendData;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public void onDataReceived(YourModel model) {
//Received the data from Activity to Fragment here.
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup
container, #Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.PagerAFragment, container,
false);
}
#Override
public void onViewCreated(View view, #Nullable Bundle
savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
Button Eventbutton;
Eventbutton = view.findViewById(R.id.event_button);
Eventbutton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
callbackSendData.sendDataEvent("send Data sample");
}
});
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
try{
callbackSendData = (CallbackSendData) context;
}catch (ClassCastException e){
e.printStackTrace();
}
}
}
And now you can use the CallbackSendData to send the data from activity to fragment.
Note: It's much easier if you are using Dependency Injection to your project, you can use Dagger2 library.
Goodluck.
To communicate between a Fragment and an Activity (whether between their presenters or their classes), you need an interface that your activity implements (like ShoppingInteractor).
This way you can call ((ShoppingInteractor)getActivity()).doSomething() in the fragments. If you want your activity's presenter to handle the task, you need to call the presenter in the doSomething inside the activity.
You can do the same with the fragments with another interface and call the fragment's interactor inside the activity.
You can even have a Presenter getPresenter() inside these interfaces to have access to the actual presenter. (((ShoppingInteractor)getActivity()).getPresenter().sendData(data)). Same goes for the fragments.
If you want to use MVP, the first step is to create one presenter for each View, I mean, If you have 3 fragments, then would have 3 presenters. I think that is a bad idea to create one presenter for 4 views (activity and 3 fragments).
Dynamic data:
Here is an example using rxjava2, dagger2 and moxy.
Conditionalities:
Presenters do not depend on the life cycle of the view
One presenter - one view. The views do not share the presenters among themselves and one view has only one presenter.
The solution is similar to the EventBus, but instead uses Subject with a limited lifetime. It is in the component that is created when the activity starts and is destroyed when it exits. Both activity and fragments have an implicit access to it, they can change the value and respond to it in their own way.
Example project: https://github.com/Anrimian/ViewPagerMvpExample
Static data:
Just use arguments in the fragment and that's it.

Receiving data from a DialogFragment if you're calling from an Activity vs a Fragment?

I call my DialogFragment like so:
If I am in an Activity:
MyDialogFragment dialogfragment = new MyDialogFragment();
dialogfragment.show(getFragmentManager(), "");
If I am already in a Fragment:
MyDialogFragment dialogfragment = new MyDialogFragment();
dialogfragment.show(getActivity().getFragmentManager(), "");
In MyDialogFragment, which inflates an XML and allows the user to input some values to EditTexts and so forth, I want to be able to return those values back to wherever I called the dialog from.
For the sake of the question let's say my dialog class wants to return some private variables String mName and int mValue.
Is there a proper way to do this without knowing where the dialog is being called from (either an Activity or a Fragment)? How do I pass the values back / how do I receive them?
If you want to send data to activity from fragment. You can do that by calling public method of activity by:
((MainActivity)getActivity()).sendData(Object object);
You can't do the same for sending data to a fragment.
As doc says:
All Fragment-to-Fragment communication is done through the associated Activity. Two Fragments should never communicate directly.
What you should do is:
Define an Interface in the fragment.
Implement that Interface in the activity
Deliver data to the activity
then activity will deliver data to some other fragment.
BTW, you can also use this practice to send data to activity (upto point 3).
Reference and example
Defining an interface:
public interface DataListener {
public void onDataReceived(Object obj);
}
inside the fragment:
public class MyFragment extends Fragment {
DataListener callback;
#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 = (DataListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement DataListener");
}
}
}
Sending data from fragment;
callback.onDataReceived(object); // some object data
Receiving data in activity:
public static class MainActivity extends AppCompatActivity
implements DataListener{
public void onDataReceived(Object object) {
// Do something here with object data
}
}
Now if you want, you can send this data to any other fragment.
Sending data from activity to some other fragment:
AnotherFragment anotherFrag = (AnotherFragment)
getSupportFragmentManager().findFragmentById(R.id.fragment_container);
if (anotherFrag != null) {
anotherFrag.receiveDataInFrag(object);
}else{
// create a new instance of the fragment and pass data to it.
}
Create a callback interface and have pass it into your dialogfragment
interface DialogValuesCallback {
void callThisFunctionWhenUserClicksOnOkInDialog(String passinmName,int passinmValue);
}
You can have your Activity or Fragment implement this interface.
Have a constructor in your MyDialogFragment which accepts the interface and assigns it to an member variable.
MyDialogFragment(DialogValuesCallback activityOrFragmentWhichImplementsThis){
mInterfaceCallbackObjectRef = activityOrFragmentWhichImplementsThis;
}
#Rohit Arya this is what you should know first that fragments needs to declare an interface that must be implemented by every activity that uses that fragment so you can pass data from the fragment to the activity(s)... and must cast the activity displaying the fragment currently into this interface like this in your onAttach method
//#param Listener is the declared interface in your fragment class
public class MyFragment extends Fragment {
Listener mInterface;
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
mInterface = (Listener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()`
+ " must implement DataListener");
}
}
public interface Listener{
//#param type = data type, cont = variable
public void sendData(type cont, type2 cont2)
;
}
}
make sure the interface declared in your fragment get implemented in your baseActivity then what ever data you pass to the interface using
mInterface.sendData(value, value1);
you can get it in the base activity like
public class baseActivity extends Activity implements MyFragment.Listener{
//#param cont & cont2 are data sent from MyFragment
#Override
public void sendData(type cont, type cont2){
//do what ever with your values here
}
}
got it now?

Access view of other layout

I have one MainActivity and two fragments namely Input.java and Output.java. I want to access the textview located at output_layout in Input fragment (More precisely, if I click on the button in input_layout, the textview of output_layout should change). How can i do this? I am calling these input_layout and out_put layout dynamically in main_activity.
As I am beginner, pardon my ignorance. Your help will be highly appreciated. Thanks in advance.
Try use interfaces.
For example.
public interface OnOutputFragmentTextChanger {
public void onChangeText(String what);
}
In onAttach method of Input.class do next:
#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 = (OnOutputFragmentTextChanger) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement OnOutputFragmentTextChanger");
}
}
And in yours OnClickListener call mCallback.onChangeText("Foooo");
Next in Output class create method
public void updateInputView(String what)
{
mTextView.setText(what);
}
Next implement OnOutputFragmentTextChanger in Activity and Override method onChangeText(String what):
public class MainActivity extends FragmentActivity implements OnOutputFragmentTextChanger
{
//do somwthing
#Override
public void onChangeText(String what)
{
InputFragment inputfragment = (InputFragment)
getSupportFragmentManager().findFragmentById(R.id.fragment); // or try findFragmentByTag
if (inputfragment != null)
{
inputfragment.updateInputView("some string");
}
}
}
you can declare the TextView in the input layout as public static in the corresponding fragment and directly access it in other fragment where you want to set the value onclick of the button

How Send message from fragment to activity and received and use in activity?

Please please don't minus my question i confused when googling.
I used Android Tab Layout with Swipeable Views in my code for when user pressed setting button on an activity.
now I need send message from TopRatedFragment.java that extends from fragment to the activity that call the mainActivity of "Android Tab Layout with Swipeable Views".
You can do this by implementing a call back
create an interface first
public interface CommunicationInterface {
public void onSuccess();
public void onFailed();
}
then in your activity implement the interface
public class YourActivity extends ActionBarActivity implements CommunicationInterface {
//default functions
#Override
public void onSuccess() {
//stuff you want to do in the acivity
}
#Override
public void onFailed() {
//stuff you want to do in the acivity
}
}
Now in the fragment
public class yourfragment extends Fragment {
CommunicationInterface callback;
//stuffs that usually come in yor fragment and like OncreateView etc
#Override
public void onActivityCreated(#Nullable Bundle outState) {
super.onActivityCreated(outState);
//after all the stuff you want to do in your fragment then implement //call back function to communicate with the activity
callback= (CommunicationInterface) getActivity();
callback.onSuccess();//according to your purpose use where ever you like
callback.onFailed();//according to your purpose use where ever you like
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
callback= (CommunicationInterface) activity;
}
}
Take a close look on this reference:
Creating event callbacks to the activity
The android docs recommend using this pattern of having the parent activity implement an interface of the fragment (Basically calling methods on it)
class MyFragment extends Fragment {
interface Listener {
public void onSomeEvent();
}
private void somethingHappeninInTheFragment() {
// let the activity know
((Listener) getActivity()).onSomeEVent();
}
}
class MyActivity extends Activity implements MyFragment.Listener {
// etc
#Override
public void onSomeEvent() {
// handle the message from the fragment
}
}
Explained with a more concrete example here: http://developer.android.com/guide/components/fragments.html#EventCallbacks
Here's the solution:
Step 1 : From your fragment.
Intent i = new Intent(getActivity(), YourActivity.class);
i.putExtra("key", "Your value1");
i.putExtra("key2", "Your value2");
i.putExtra("key3", "Your value3");
getActivity().startActivity(i);
Step 2 : In your Activity where you want the result
Intent getResults = getIntent();
String firstValue = getResults.getStringExtra("key1");
String secondValue = getResults.getStringExtra("key2");
String thirdValue = getResults.getStringExtra("key3");
Use those values your needs are.
Hope this helps.. :)

Categories

Resources