I have an activity with two fragments, with only 1 of the two fragments being displayed at a given time, as determined by the user. Both fragments subscribe to data via a broadcast receiver.
The acitivty manages which fragment is displayed using the fragment manager's replace() method, e.g.
getFragmentManager().beginTransaction().replace(R.id.north_fragment, fragment1, FRAGMENT_TAG).commit();
The Broadcast Receivers are registered in the Fragment's onCreateView() override: The broadcast receiver receives the appropriate data as expected.
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_spectrum_analyzer_graph, container, false);
setupGraphView(rootView);
// register for Spectrum Frame Response messages
myReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "onReceive()");
// do stuff
}
}
The broadcast receiver is unregistered in onDestroyView(), e.g.:
#Override
public void onDestroyView() {
super.onDestroyView();
getActivity().unregisterReceiver(m_spectrumDataFrameReceiver);
}
Is there a way I can keep the fragment receiving data through the BroadcastReceiver, even when it's not visible (after it's been destroyed)? I want the receiver to continue getting data and storing it in the Fragment, even if the fragment is not visible (but the activity is visible), but I know that I need to unregister the broadcast receiver at some point.
Should unregistration be done when the Activity is destroyed (calling in to its activities to unregister), or is it too late here? I'm fairly new to the intricacies of Fragment lifecyle, and am not quite sure what to do.
Instead of replacing each fragment everytime just use the add method fragment transaction so it is still alive when invisible.
solution:
replace the replace use the add
beginTransaction().add(R.id.north_fragment, fragment1, FRAGMENT_TAG).commit();
You should register your receiver in activity onResume and unregister it in onPause. You receiver will receive broadcast as long as activity is running. Then you can pass received data to respective fragment by any means.
Related
I have 4 Fragments and I am trying to click a button on FragmentA and call a method that changes the visibility of some views on FragmentB and populate it.
I tried an interface, but I can't seem to get it to work between 2 fragments. I can call the interface method from a fragment if I implement it in the activity, but I can't implement it in a fragment and call it in a fragment.
Is there a different way to do this? I don't think I can use the static keyword.
I am suggesting you can use broadcast receiver its, good to perform action anywhere and easy to use.
In your first fragment you can define receiver and from another fragment, you can send broadcast or action.
Example are following.
Write following code in your first fragment in which you want to update view,
private void registerReciver() {
broadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if (intent != null && intent.getAction().equals("UPDATE_FRAG_A")) {
// here you can fire your action which you want also get data from intent
}
}
};
IntentFilter intentFilter = new IntentFilter("UPDATE_FRAG_A");
getActivity().registerReceiver(broadcastReceiver, intentFilter);
}
And In your second fragment write following code for fire action,
Intent intent=new Intent();
// Here you can also put data on intent
intent.setAction("UPDATE_FRAG_A");
getActivity().sendBroadcast(intent);
Assume that all the fragments are in the same activity.
Define a interface in FragmentA, which is a Listener
Expose what you want to do in FragmentB via a public method
Implement FragmentA's interface in the parent activity by calling the public method of FragmentB
For more inforamtion,see Communicating with Other Fragments
I have a Fragment with a ListView and a BroadcastReceiver that updates the ListView when new data arrives. The Fragments life is controlled by a ViewPager.
In regard to where to (un)register the BroadcastReceiver, I found several places suggesting to do it in
onResume():
refreshReceiver = new RefreshReceiver();
LocalBroadcastManager.getInstance(getActivity()).registerReceiver(
refreshReceiver,
refreshIntentFilter);
onPause():
LocalBroadcastManager.getInstance(getActivity()).unregisterReceiver(refreshReceiver);
However, this does not work properly. When I long-press the home button to get to the Recent apps screen, onPause() is called. When new information comes in while I'm on Recent apps, the ListView misses the update and shows old information after I go back there.
Now I was thinking about moving the unregistering to the onStop() method (or even in onDestroy()), but is that guaranteed to be called when the fragment is destroyed? I was worried because if the BroadcastManager holds a reference to the BroadcastReceiver and that in turn holds a reference to the Fragment, it would be quite a serious memory leak.
if you don't need receiver, then unregister it. if you use it only in that fragment, you may consider unregistering it onViewDestroyed().
According this answer , if your fragment is single and not in pager you can un/register LBM in :
#Override
public void onResume() {
super.onResume();
LocalBroadcastManager.getInstance(getActivity()).registerReceiver(
broadcast_manager, new IntentFilter("filter"));
}
#Override
public void onPause() {
super.onPause();
LocalBroadcastManager.getInstance(getActivity()).unregisterReceiver(broadcast_manager);
}
Else you need to find out when fragment is visible then register LBM.
through this:
public class MyFragment extends Fragment {
#Override
public void setMenuVisibility(final boolean visible) {
super.setMenuVisibility(visible);
if (visible) {
// Register LBM
}
}
// ...
}
you can register LBM in fragment
There are 2 Fragments
I'm calling a service from Fragment 1. I have a ResultReceiver in Fragment 1 which listens to the result and onReceiveResult will call method1().
I want a ResultReceiver in Fragment 2 to listen to the same response but onReceiveResult will be calling method2()
How can I achieve this?
You could specify an interface:
interface Receiver {
void onResult();
}
Have your two Fragments implement this interface. Fragment1's implementation simply calls method1(), and Fragment2's implementation simply calls method2():
public class Fragment1 extends Fragment implements Receiver {
// Remember to register and remove the receiver (e.g. in onAttach and onDetach respectively).
private MyReceiver mBroadcast = new MyReceiver(this);
public void onResult() {
this.method1();
}
}
public class Fragment2 extends Fragment implements Receiver {
// Remember to register and remove the receiver (e.g. in onAttach and onDetach respectively).
private MyReceiver mBroadcast = new MyReceiver(this);
public void onResult() {
this.method2();
}
}
Then specify the BroadcastReceiver as a standalone (or inner static) class such that both Fragment1 and Fragment2 will be able to instantiate it:
public class MyReceiver extends BroadcastReceiver {
private Receiver mFragment;
public MyReceiver(Receiver fragment) {
mFragment = fragment;
}
public void onReceive(Context context, Intent intent) {
if(intent.getAction().equals(YOUR_ACTION) {
mFragment.onResult();
}
}
}
I don't think that you can receive results in two different fragments simultaneously.
But there are many ways to achieve this..
1.
I believe the easiest way will be to use object reference..
There are two possibilities.. Either create a static method in Fragment 2 and call it from fragment 1 from onReceiveResult(). Or Create an object of Fragment 2 in fragment 1 and from fragment 2 , assign that it is the same as the instance created by fragment1. Then just call
object_of_frgament2.method2() from the onReceiveResult() of fragment 1.
2.
Using interface.Create a custom interface and make the Fragment 2 implement the interface and create an instance of the interface in Fragment 1.
and within onReceiveResult() of Fragment1 you can call the interface method.
While implementing the interface, you can get the result in fragment 2 in the interface method.
Just call method2() from the function....
3.Using Broadcast Receiver..
Create a custom broadcast receiver and make all the fragments/activities which need the results to listen to it. and within onReceiveResult() of Fragment1 just broadcast the result..
I believe there are still other ways to do it..
just pass into your service two different ResultReceiver's ... If the service is already started calling startService(Intent) again just makes you call onStartCommand(...) and then you can set your resultReciever each time. So you can keep an array of resultreciever's if you want.
saying that, i would never do it this way. Research Java Observer pattern. Java has a default implementation of the Observer pattern. Here is a link
I am trying to modify views inside a fragment1 from other fragment2, parent of fragment1, calling a public method of fragment1, but doesn't works.
In the container fragment a do this:
AddressEditSubviewFragment profesionalEditFragment = new AddressEditSubviewFragment();
notificationsEditFragment = new AddressEditSubviewFragment();
fragmentTransaction.add(R.id.addresses_edit_fragment_notifications_edit_fl, notificationsEditFragment);
fragmentTransaction.commit();
CheckBox notCB = (CheckBox) view.findViewById(R.id.addresses_edit_fragment_notifications_same_cb);
notCB.setOnCheckedChangeListener(new OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
notificationsEditFragment.disableEnableEdit(true);
}
});
The method in the fragment to change the state of the views is this:
public void disableEnableEdit(boolean disable) {
streetET.setKeyListener(null);
streetET.setCursorVisible(false);
streetET.setPressed(false);
streetET.setFocusable(false);
numberET.setFocusable(!disable);
numberET.setEnabled(!disable);
numberET.setText("pruebas");
floorET.setVisibility(View.GONE);
buildingET.setFocusable(!disable);
buildingET.setEnabled(!disable);
pcET.setFocusable(!disable);
pcET.setEnabled(!disable);
}
When I call the method from the container fragment it enters in the method, but not change nothing. Why is this thing happens?
you can't and shouldn't have fragments communicate directly with one another. all communication between them must go through the activity or the parent fragment (if it's hosting both of them).
There are a few ways to do this. here's one method.
I personally prefer working with broadcastreceivers. The method is basically:
Have fragment1 call some method in the activity.
Have that method sent a broadcast on some intent filter when its done.
Implement a broadcast receiver on fragment2, that does stuff when receiving a broadcast.
Have fragment2 register and unregister the receiver on said intent filter as you please (usually done on the onResume and onPause respectfully).
Tell me if my explanation wasn't clear enough, I'll provide a working code example.
Good luck! :)
--- EDIT ---
Ok, after understanding you're trying to call a method in a child fragment from its parent fragment, here's a method with broadcast receivers that should work:
parent fragment sends a broadcast on some intent filter whenever you need to call the method in the child fragment.
Intent intent = new Intent();
intent.setAction(some_string);
getActivity().sendBroadcast(intent);
implement a broadcast receiver in child fragment that calls that method in its onReceive().
BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
doStuff();
}
};
register the receiver to the intent filter in the child fragment's onResume(), and unregister it in the child's onPause().
getActivity().registerReceiver(receiver, new IntentFilter(some_string));
getActivity().unregisterReceiver(receiver);
Finally, I found the problem. The problem was that I had two cheboxes whit the same id in the parent fragment. And when I check a check box doesn't work well. Sorry for the question.
I am programming a Music Player and the Music plays in a background Service. When the user kills the Activity which hosts 3 Fragments, and then restarts the Activity again, I send a Broadcast from the Service that contains information about the current playing song, and the list of songs that the user added to his session.
The problem is, every time I want to set the last information into the Fragments nothing happens because their creation takes too long, and the Broadcast doesn't get handled like they should.
How can I let the Service or the Broadcast wait until the Fragments are created so they are handled appropriately?
Here are the relevant code snippets:
//When the activity binds to the service, refresh the fragments
private ServiceConnection conn = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
LocalBinder binder = (LocalBinder) service;
myService = binder.getService();
myService.setBound(true);
if(myService.startedOnce) {
myService.refreshFragments();
}
myService.startedOnce = true;
}
}
//This is the broadcast receiver of the Fragment
//it receives the broadcast too soon, and I can't set the
//Views so they are always empty.
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(MusicService.REFRESH_ALL)) {
Song current = intent.getParcelableExtra("song");
setCurrentSong(current);
}
}
The easiest thing to do would simply be hold on to the information until the Fragment is ready to display it. Use the Fragment's setArguments() method to attach the information into the Fragment.
#Override
public void onReceive() {
String action = intent.getAction();
if(action.equals(MusicService.REFRESH_ALL)) {
// Creating a new Bundle so the Fragment can control its own object
Bundle args = new Bundle(intent.getExtras());
Fragment fr = getUsingFragment();
fr.setArguments(fr);
}
}
Then, in the Fragment's onCreateView() simply pull the arguments from getArguments() and build the view with the values.
#Override
public void onCreateView (LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Bundle args = getArguments();
if(args != null) {
// code to set values if arguments are set
} else {
// code to set values if arguments are not set
}
}
Another way to do it would be to use setter methods in which the Fragment itself puts values into a Bundle for setArguments(). That way, you can update the views whenever the View has been created on top of setting the arguments for the possible event when the Fragment's View is destroyed and must be recreated.
Note: You can only call setArguments() before the Fragment has been attached to the Activity. You can however update the Bundle that you pass in by setArguments by retrieving a reference to it from getArguments(), then simply putting in the values. So instead of calling setArguments() from your receiver, do something like this:
public void setCurrentSong(Song extra) {
Bundle args = getArguments();
args.putParcable(KEY_MAP, extra);
if(/* Views are created */) {
// update and invalidate the views
}
}
How I fixed this
As I was using a Service for Media Playback, I wanted to bring up last listened songs from the service so I could directly play it. This was old logic, but I actually built my code around it. Until thusfar I bumped into it.
This was happening
FragmentActivity is created
Service gets started and bound to
Meanwhile the Fragments get created asynchronously
As soon as the Service starts, it sends out a Broadcast with latest information
Because the both the Service and the Fragment creations are asynchronous, the broadcast would be sent from the service, but because the BroadcastReceivers in the Fragments weren't even initialized yet, they would not receive the Intent.
What I did to fix it
I somehow had to use a callback that made sure that
the Service was created and bound to
the fragments created and views are set
So I used the ServiceConnection and to be precise, the onServiceConnected() method. There I got the preferences in which the last song was saved, and then send out the Broadcast and the Fragments received it and the Views were appropiately set. This also worked for orientation changes.
The code
//This is the code in the FragmentActivity
private ServiceConnection conn = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
LocalBinder binder = (LocalBinder) service;
myService = binder.getService();
myService.setBound(true);
if (myService.startedOnce) {
myService.refreshFragments();
} else {
sendLastSavedSong();
}
myService.startedOnce = true;
}
public void onServiceDisconnected(ComponentName className) {
myService.setBound(false);
}
};
Can't you do something in the fragment like
Song current=null;
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(MusicService.REFRESH_ALL)) {
current = intent.getParcelableExtra("song");
}
}
#Override
public void onResume()
{
if(current!=null) setCurrentSong(current);
super.onResume();
}
my solution was just creating your own callback interfaces. At the very end of onCreateView method of ur fragment just call your callback method, which tells ur mainactivity that the creation is done.
It worked for me, hope helps u too.