May someone please elaborate this!
if we have implemented ViewModel to communicate between two fragments then can those fragments by-pass the corresponding activity and communicate directly to the ViewModel? if yes then is it a good practice or not (why and why not)?
You can use interface like this article https://developer.android.com/training/basics/fragments/communicating
Define an Interface
To allow a Fragment to communicate up to its Activity, you can define an interface in the Fragment class and implement it within the Activity. The Fragment captures the interface implementation during its onAttach() lifecycle method and can then call the Interface methods in order to communicate with the Activity.
Here is an example of Fragment to Activity communication:
public class HeadlinesFragment extends ListFragment {
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");
}
}
...
}
Now the fragment can deliver messages to the activity by calling the onArticleSelected() method (or other methods in the interface) using the mCallback instance of the OnHeadlineSelectedListener interface.
For example, the following method in the fragment is called when the user clicks on a list item. The fragment uses the callback interface to deliver the event to the parent activity.
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
// Send the event to the host activity
mCallback.onArticleSelected(position);
}
Implement the Interface
In order to receive event callbacks from the fragment, the activity that hosts it must implement the interface defined in the fragment class.
For example, the following activity implements the interface from the above example.
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
}
}
Deliver a Message to a Fragment
The host activity can deliver messages to a fragment by capturing the Fragment instance with findFragmentById(), then directly call the fragment's public methods.
For instance, imagine that the activity shown above may contain another fragment that's used to display the item specified by the data returned in the above callback method. In this case, the activity can pass the information received in the callback method to the other fragment that will display the item:
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
ArticleFragment articleFrag = (ArticleFragment)
getSupportFragmentManager().findFragmentById(R.id.article_fragment);
if (articleFrag != null) {
// If article frag is available, we're in two-pane layout...
// Call a method in the ArticleFragment to update its content
articleFrag.updateArticleView(position);
} else {
// Otherwise, we're in the one-pane layout and must swap frags...
// Create fragment and give it an argument for the selected article
ArticleFragment newFragment = new ArticleFragment();
Bundle args = new Bundle();
args.putInt(ArticleFragment.ARG_POSITION, position);
newFragment.setArguments(args);
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
// Replace whatever is in the fragment_container view with this fragment,
// and add the transaction to the back stack so the user can navigate back
transaction.replace(R.id.fragment_container, newFragment);
transaction.addToBackStack(null);
// Commit the transaction
transaction.commit();
}
}
}
Related
I have a Fragment A which has an interface that will return the value of a TextView. The Fragment A is initialized and Attached to FragmentB. Here is the code for Fragment A and B.
Fragment A
public class FragmentA extends Fragment {
...
public interface Listener {
void onValue(int value);
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof Listener) {
listener = (Listener) context;
} else {
throw new RuntimeException(context.toString() + " must implement Listener");
}
}
}
Fragment B
public class FragmentB extends Fragment implements FragmentA.Listener {
...
private void initFragmentA() {
FragmentManager fragmentManager = getChildFragmentManager();
fragmentA = FragmentA.newInstance();
fragmentManager.beginTransaction().add(container, fragmentA, TAG).commit();
}
#Override
public void onValue(int value) {
}
}
When I start the app, a error occurred:
Java.lang.RuntimeException: ####.page.MainActivity#1f7f316c must implement Listener
at ####.widget.FragmentA.onAttach(FragmentA.java:66)
The MainActivity contains the Fragment B, but the Fragment B has already implemented the interface of Fragment A. Why the error occurred? The interface implemented in parent Fragment doesn't work?
From Android Developer website, it says:
Often you will want one Fragment to communicate with another, for example to change the content based on a user event. All Fragment-to-Fragment communication is done through the associated Activity. Two Fragments should never communicate directly.
To allow a Fragment to communicate up to its Activity, you can define an interface in the Fragment class and implement it within the Activity. The Fragment captures the interface implementation during its onAttach() lifecycle method and can then call the Interface methods in order to communicate with the Activity.
Therefore, when you implement that interface in Fragment B, it throws a RuntimeException error.
Summary: you have to implement that interface in your fragment hosting activity.
You can learn more on how to pass data between fragments thru a hosting activity (usually we make use of Bundle) at http://www.vogella.com/tutorials/AndroidFragments/article.html
The error message is pretty clear. When you use onAttach, the context passed through is actually the host Activity - the biggest boss of all, not the parent fragment. So FragmentB which implement the interface but will not receive any update.
You should use getParentFragment to access FragmentB from FragmentA, then cast it to the Listener interface.
I want to call the function Ask() of MainActivity from fragment2.
How can I call a function of MainActivity from fragment2?
I want to import results into a page called from fragment2.
Edit:
I already see that discussion, but don't have the solution of my problem.
Make that function static, after that you can access that function in Fragment e.g. MainActivity.Ask();
From fragment to activty:
((YourActivityClassName)getActivity()).yourPublicMethod();
From activity to fragment:
FragmentManager fm = getSupportFragmentManager();
//if you added fragment via layout xml
YourFragmentClass fragment = (YourFragmentClass)fm.findFragmentById(R.id.your_fragment_id);
fragment.yourPublicMethod();
If you added fragment via code and used a tag string when you added your fragment, use findFragmentByTag instead:
YourFragmentClass fragment = (YourFragmentClass)fm.findFragmentByTag("yourTag");
Cheers!
I would recommend you read this documentation.
call to function Ask() of MainActivity from fragment2.
For this you need a create a interface in your fragment2. The below code is an example from the document. You shouldn't ignore the onAttach method in your fragment as well.
public class Fragment2 extends ListFragment {
OnCallActivityListener mCallback;
// Container Activity must implement this interface
public interface OnCallActivityListener {
public void callAskMethod();
}
#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 = (OnCallActivityListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement OnHeadlineSelectedListener");
}
}
...
}
Now the fragment can deliver messages to the activity by calling the callAskMethod() method (or other methods in the interface) using the mCallback instance of the OnCallActivityListener interface.
For example, the following method in the fragment is called when the user clicks on a list item. The fragment uses the callback interface to deliver the event to the parent activity.
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
// Send the event to the host activity
mCallback.callAskMethod();
}
After that you should implement the interface in your host activity.
public static class MainActivity extends Activity
implements Fragment2.OnCallActivityListener{
...
public void callAskMethod() {
// Do something here
ask();
}
}
So that is it. You have called ask() method from fragment2 fragment.
I want to import results into a page called from fragment2.
The host activity can deliver messages to a fragment by capturing the Fragment instance with findFragmentById(), then directly call the fragment's public methods.
In your `MainActivity you can call send the result to the fragment.
Fragment2 fragment2 = (Fragment2) getSupportFragmentManager().findFragmentById(R.id.article_fragment);
So you have a instance value of the Fragment2 in MainActivity. So you can any public method of the fragment2 from there.
for example
fragment2.doSomething();
That's it.
I have an activity which calls another fragmentA .Now this fragmentA calls another fragmentB .Now I want to transfer data from fragmentB to my activity
check this: Communicating with Other Fragments
Define an Interface (In fragment)
To allow a Fragment to communicate up to its Activity, you can define an interface in the Fragment class and implement it within the Activity. The Fragment captures the interface implementation during its onAttach() lifecycle method and can then call the Interface methods in order to communicate with the Activity.
Here is an example of Fragment to Activity communication:
public class HeadlinesFragment extends ListFragment {
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");
}
}
...
}
Now the fragment can deliver messages to the activity by calling the onArticleSelected() method (or other methods in the interface) using the mCallback instance of the OnHeadlineSelectedListener interface.
For example, the following method in the fragment is called when the user clicks on a list item. The fragment uses the callback interface to deliver the event to the parent activity.
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
// Send the event to the host activity
mCallback.onArticleSelected(position);
}
Implement the Interface (in activity)
In order to receive event callbacks from the fragment, the activity that hosts it must implement the interface defined in the fragment class.
For example, the following activity implements the interface from the above example.
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
}
}
PS1: EventBus works for you, but use it carefully if you need, it may make your code harder to read.
PS2: Don't pass an activity instance in Fragment.newInstance() and communicate using it. The activity instance may be destroyed in background. Get activity instance in Fragment.onAttach() like the example, the framework will handle the destroy & recreate & rebind for you.
You can get the Activity instance by overriding onAttach() method
Method: 1
#Override
public void onAttach(Activity activity) {
// TODO Auto-generated method stub
super.onAttach(activity);
this.activity=(ActivityName) activity;
}
and
activity.setdata(yourdata);
or
Method : 2
((ActivityName)getActivity()).setdata(yourdata);
In these ways you need to create a setter method in your activity
It is easiest way to get callback from any fragment to its parent activity .Very well explained by Dev Doc here
You can get the instance of the activity by calling getActivity() in fragmentB and pass the data using an interface.
You could use EventBus if you think that, you will need a similar functionality in different parts of the app and you don't want to write many interfaces for this porpoise.
You could use https://github.com/greenrobot/EventBus
Example:
Add compile 'de.greenrobot:eventbus:2.4.0',
Register on activities
OnCreate -
EventBus.getDefault().register(this);
and OnDestroy-
EventBus.getDefault().unregister(this);
Than add a method with a receiving object parameter and with onEventMainThread name:
public void onEventMainThread(YourObject name) {...}
Now from any Fragament you can call
EventBus.getDefault().post(yourObjectInstance);
And activity will detect it.
Or you could use RxJava to get similar effect -
http://nerds.weddingpartyapp.com/tech/2014/12/24/implementing-an-event-bus-with-rxjava-rxbus/
I have a Class1 extending ListFragment1.
I have a Class2 extending ListFragment2.
I'm currently passing an Intent like this inside OnItemClickListener.
Intent intent = new Intent(getActivity().getApplicationContext(), ListFragment2.class);
intent.putExtra("id", "1");
getActivity().startActivity(intent);
}
However, i get this exception:
java.lang.ClassCastException: com.abc.xyz.Class2 cannot be cast to android.app.Activity
Please help ! :(
First you should know:
You are calling startActivity(intent) and your Intent is pointing to a Fragment.
You cannot pass intents to your Fragment like that since Fragments are not App Components.
Communcation between Fragments should be between the Activity holding them.
Best Solution:
Pass your data from Class1 to your Activity and then send it to Class2, this link provides you exactly this, no more or less code:
http://developer.android.com/training/basics/fragments/communicating.html
It's a must read when you want to communicate between fragments.
Intent must point towards a class extending android.app.Activity which is not a case with your code.
Instead of extending ListFragment2 externally, you can make an inner class extending ListFragment2 and pass the value.
Hope this helps.
Both the fragments must communicate with the activity for communication between each other.The activity acts as a sort of a mediater between the fragement.The flow will look like this:
Fragment1 passes the intent to Activity1 with certain parameters which helps Activity1 to identify that it needs to pass this intent to Fragment2.
Activity1 on receiving the intent, passes this intent to Fragment 2.
How the fragment communicates with activity can be designed in following way mentioned here
To allow a Fragment to communicate up to its Activity, you can define an interface in the Fragment class and implement it within the Activity. The Fragment captures the interface implementation during its onAttach() lifecycle method and can then call the Interface methods in order to communicate with the Activity.
public class HeadlinesFragment extends ListFragment {
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");
}
}
...
}
Now the fragment can deliver messages to the activity by calling the onArticleSelected() method (or other methods in the interface) using the mCallback instance of the OnHeadlineSelectedListener interface.
For example, the following method in the fragment is called when the user clicks on a list item. The fragment uses the callback interface to deliver the event to the parent activity.
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
// Send the event to the host activity
mCallback.onArticleSelected(position);
}
Implement the Interface
In order to receive event callbacks from the fragment, the activity that hosts it must implement the interface defined in the fragment class.
For example, the following activity implements the interface from the above example.
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
}
}
Deliver a Message to a Fragment
The host activity can deliver messages to a fragment by capturing the Fragment instance with findFragmentById(), then directly call the fragment's public methods.
For instance, imagine that the activity shown above may contain another fragment that's used to display the item specified by the data returned in the above callback method. In this case, the activity can pass the information received in the callback method to the other fragment that will display the item:
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
ArticleFragment articleFrag = (ArticleFragment)
getSupportFragmentManager().findFragmentById(R.id.article_fragment);
if (articleFrag != null) {
// If article frag is available, we're in two-pane layout...
// Call a method in the ArticleFragment to update its content
articleFrag.updateArticleView(position);
} else {
// Otherwise, we're in the one-pane layout and must swap frags...
// Create fragment and give it an argument for the selected article
ArticleFragment newFragment = new ArticleFragment();
Bundle args = new Bundle();
args.putInt(ArticleFragment.ARG_POSITION, position);
newFragment.setArguments(args);
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
// Replace whatever is in the fragment_container view with this fragment,
// and add the transaction to the back stack so the user can navigate back
transaction.replace(R.id.fragment_container, newFragment);
transaction.addToBackStack(null);
// Commit the transaction
transaction.commit();
}
}
}
This is one of the way to achieve it, i use constructor to pass the data
FragmentOne.java
int myData=12;
FragmentManager manager = getActivity().getSupportFragmentManager();
Fragment frgObj=FragmentTwo.newInstance(myData);
FragmentTransaction ft = manager.beginTransaction();
ft.replace(R.id.container, frgObj,"FragmentTwo");
ft.addToBackStack(null);
ft.commit();
FragmentTwo.java
int myData;
public static FragmentTwo newInstance(int _myData){
FragmentTwo fragment = new FragmentTwo();
myData=_myData
return fragment;
}
ALSO REFER -- this -- StackOVERFLOW POST
Basically I want to perform setText() feature of TextView class inside , BroadCaseReceiver . But I am not being able to implement this : I don't know
why ( context.findviewById() is not working ) , because findViewById() is part of Activity class and context.toString() is showing the reference of Activity( base activity ) class?
Link to below answer is here: Communicating with Other Fragments
To allow a Fragment to communicate up to its Activity, you can define an interface in the Fragment class and implement it within the Activity. The Fragment captures the interface implementation during its onAttach() lifecycle method and can then call the Interface methods in order to communicate with the Activity.
Here is an example of Fragment to Activity communication:
public class HeadlinesFragment extends ListFragment {
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");
}
}
...
}
Now the fragment can deliver messages to the activity by calling the onArticleSelected() method (or other methods in the interface) using the mCallback instance of the OnHeadlineSelectedListener interface.
For example, the following method in the fragment is called when the user clicks on a list item. The fragment uses the callback interface to deliver the event to the parent activity.
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
// Send the event to the host activity
mCallback.onArticleSelected(position);
}
Implement the Interface
In order to receive event callbacks from the fragment, the activity that hosts it must implement the interface defined in the fragment class.
For example, the following activity implements the interface from the above example.
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
}
}
Deliver a Message to a Fragment
The host activity can deliver messages to a fragment by capturing the Fragment instance with findFragmentById(), then directly call the fragment's public methods.
For instance, imagine that the activity shown above may contain another fragment that's used to display the item specified by the data returned in the above callback method. In this case, the activity can pass the information received in the callback method to the other fragment that will display the item:
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
ArticleFragment articleFrag = (ArticleFragment)
getSupportFragmentManager().findFragmentById(R.id.article_fragment);
if (articleFrag != null) {
// If article frag is available, we're in two-pane layout...
// Call a method in the ArticleFragment to update its content
articleFrag.updateArticleView(position);
} else {
// Otherwise, we're in the one-pane layout and must swap frags...
// Create fragment and give it an argument for the selected article
ArticleFragment newFragment = new ArticleFragment();
Bundle args = new Bundle();
args.putInt(ArticleFragment.ARG_POSITION, position);
newFragment.setArguments(args);
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
// Replace whatever is in the fragment_container view with this fragment,
// and add the transaction to the back stack so the user can navigate back
transaction.replace(R.id.fragment_container, newFragment);
transaction.addToBackStack(null);
// Commit the transaction
transaction.commit();
}
}
}
Unless if the BroadcastReceiver is started programmatically inside the context of an Activity e.g. Activity.onCreate(). For an ordinary BroadcastReceiver, it is very odd to access a UI element like TextView from it. You can only work with UI stuff from an Activity. Maybe you should reviewing the solution of which you're trying to apply. Even if you manage to do such a thing, it will have a wrong design.
public class MyReceiver extends BroadcastReceiver {
private MyActivity myActivity;
public MyReceiver(MyActivity myActivity) {
this.myActivity= myActivity;
}
#Override
public void onReceive(Context context, Intent intent) {
Log.d("", "Onrecieve ready to call");
if(this.myActivity!=null)
{
this.myActivity.update();
}
// make update method is in your activity.
// call function of your activity and change UI.
}
}