The application that I'm working on has an activity with three fragments. Each fragment needs to show some data that is received from an IntentService.
Fragment 1 - the icon, the name and the description
Fragment 2 - a list of articles
Fragment 3 - a list of items
public class Activity extends SherlockFragmentActivity implements Actionbar.Tablistener
{
public void onCreate(..) {
....
performSearch();
setupTabs(); // 3 tabs are setup, their clicks and swipes init'ed
...
}
#Override
public void onResume() {
IntentFilter intentFilter = new IntentFilter(SearchRequestReceiver.ACTION);
intentFilter.addCategory(Intent.CATEGORY_DEFAULT);
searchRequestReceiver = new SearchRequestReceiver(this);
registerReceiver(searchRequestReceiver, intentFilter);
}
#Override
public void onPause() {
unregisterReceivers();
}
...
public void performSearch() {
Intent intent = new Intent(this, SearchRequest.class);
intent.putExtra("searchTerm", this.searchTerm); // declared and initialised earlier
startService(intent);
}
...
}
What is the best way for me to push this data from my receiver to my fragments or am I approaching this the wrong way? I didn't use AsyncTasks because I wanted to decouple my services from the context or was that the wrong decision?
An EventBus is one of the neatest solutions in this situation. EventBus and Otto are both very easy to use.
An example using Otto...
Your IntentService
new Handler(Looper.getMainLooper()).post(new Runnable() {
bus.post(new DataLoadCompleteEvent());
});
Note the necessity to post the event on Android's main thread with Otto. In this case, a DataLoadCompleteEvent could contain whatever you wanted.
Your Fragment
#Subscribe public void onLoad(DataLoadCompleteEvent event) {
//Do stuff with event
}
Just make sure your Fragments register on the bus in their onResume(), and unregister in their onPause().
I've managed to get it working by having the different receivers within my fragments but it seems like an awful lot of repetition.
public class FragmentName extends SherlockFragment
{
private SearchRequestReceiver searchRequestReceiver;
..
public class ServiceRequestReceiver extends BroadcastReceiver
{
#Override
public void onReceive(Context context, Intent intent) {
...
}
}
}
Is there a better way?
Related
Lets put we 3 activates that may follow the stack A->B->C
How can I close A and keep B->C, but only if B opens C?
C is not always opened, but if it gets opened only then A should be removed, leaving the stack as B->C. Otherwise will remain as A->B.
I don't see the possibility of using finish() in A, as it should be closed from B when opening C.
By the way A is a single instance with its own affinity.
Write an abstract BaseActivity class and implement a LocalBroadcastManager in it and register a BroadcastReceiver. Note that we have also introduced an abstract onFinishReceived method.
public abstract class BaseActivity extends AppCompatActivity {
private LocalBroadcastManager mLBM;
private final BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
onFinishReceived(intent.getStringExtra("activity"));
}
}
protected void onCreate(#Nullable Bundle savedInstanceState) {
...
mLBM = LocalBroadcastManager.getInstance(this);
IntentFilter filter = new IntentFilter();
filter.addAction(...);
mLBM.registerReceiver(receiver, filter);
}
protected void onDestroy() {
mLBM.unregisterReceiver(receiver);
mLBM = null;
}
abstract void onFinishReceived(String activity);
}
Then, extend all A, B and C Activities from the BaseActivity and override the onFinishReceived() method.
public class A extends BaseActivity {
void onFinishReceived(String activity) {
if (activity.equals("A") { // or "B" or "C"
finish();
}
}
}
}
Now, whenever you want to finish a specific activity use,
LocalBroadcastManager.getInstance(context)
.sendBroadcast(new Intent()
.setAction(...)
.putExtra("activity", "A") // or "B" or "C"
);
LocalBroadcastManager is now deprecated. It's better if you can use something like RxJava. The idea is to use an asynchronous and event-based mechanism rather than going for a simple ugly solution like storing the Activity in a static variable.
For Fragment(put data to activity)
m=(MainActivity)getActivity();
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
Intent in=new Intent(getActivity(),MainActivity.class)
in.putExtra("test",test);
startActivty(in)
}
},10);
For Activity (get data from fragment )
{
String get_data=getIntent.getStringExtra("test");
}
//it will return always null...any body help me?
startActivty(in) will start the same activity.
Instead of this, you can make use of Interface. It's the easiest way to pass the data.
in your fragment, you can have an interface like,
SubmitData mSubmitData;
public interface SubmitData{
void DataListener(String s);
}
mSubmitData.DataListener("data to be sent");
In your activity, implement the SubmitData interface. It will make you override the DataListener method, where you can get the data.
public class MyActivity extends AppCompatActivity implements YourFragment.SubmitData{
#Override
public void DataListener(String s) {
// Data from the fragment
}
This questions has been asked and answered multiple times. You can find a valid reply here https://stackoverflow.com/a/9977370/5828132
Basically, it consists of creating an interface in the Fragment (for example) including a simple method. The Fragment has to declare a field of that type, and the Activity hosting the Fragment has to implement (implements) that interface. Both entities are usually connected using a explicit cast, in the onAttach() callback of the Fragment life-cycle, i.e.:
#Override
public void onAttach(Context context) {
super.onAttach(context);
// fragmentField_interfaceType = (interfaceType) context;
}
Hope it helps!
I searched all over the web, couldn't find a good reference on how to call fragment from another fragment.
Fragment A -> Fragment B (fragment A calls fragment B after 3 seconds)
Well, first of all you need to consider that it's a very bad idea to keep somehow a direct reference from FragmentA to FragmentB. Why:
FragmentB may be recreated and you may keep a reference to an older reference of FragmentB. So you have a memory leak.
FragmentB may be not created, added or visible. So you would have a null/unusable reference.
For this reason you need to consider methods that base on sending messages from FragmentA to FragmentB. I see several options:
Send a broadcast message using a custom action from FragmentA. FragmentB registers itself as a receiver for this kind of message (in onCreate/onResume/onAttach and de-register in onDestroy/onPause/onDetach) and when the message arrives it can handle it. This is very suitable if you have no data to send from FragmentA to FragmentB or if you do these are primitive types or easy-to-implement Parcelables. Here's an example:
Have this in FragmentA:
private void sendMessageToFragmentB(String someData) {
Intent messageIntent = new Intent("com.your_package.A_TO_B_ACTION");
messageIntent.putExtra("DATA_VALUE", someData);
LocalBroadcastManager.getInstance(getActivity()).sendBroadcast(messageIntent);
}
While in FragmentB you could have this:
public class FragmentB extends Fragment {
private BroadcastReceiver messagesFromAReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if ("com.your_package.A_TO_B_ACTION".equals(intent.getAction())) {
String dataFromA = intent.getStringExtra("DATA_VALUE");
dataFromAReceived(dataFromA);
}
}
};
protected void dataFromAReceived(String data) {
// here you have the data
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
IntentFilter messageFromAIntentFilter = new IntentFilter("com.your_package.A_TO_B_ACTION");
LocalBroadcastManager.getInstance(getActivity()).registerReceiver(messagesFromAReceiver,
messageFromAIntentFilter);
}
#Override
public void onDestroy() {
super.onDestroy();
LocalBroadcastManager.getInstance(getActivity()).unregisterReceiver(messagesFromAReceiver);
}
}
Use the hosting activity as a proxy: The host activity implements some kind of interface defined in FragmentA and when requested it can search if it can find FragmentB and if so call some method in there. The advantage is that you can send any data, no matter its weight. The base idea is descrived in Android dev articles. To exemplify, you could have FragmentA as:
public class FragmentA extends Fragment {
public static interface CallerProxy {
public void sendCustomMessage(Object... dataParams);
}
private CallerProxy proxyActivity;
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
if (activity instanceof CallerProxy) {
this.proxyActivity = (CallerProxy) activity;
}
}
#Override
public void onDetach() {
super.onDetach();
this.proxyActivity = null;
}
private void sendMessageToFragmentB(String someData) {
if (proxyActivity != null) {
// send whatever data
proxyActivity.sendCustomMessage(new Integer(1), new Object());
// or don't send anything ...
proxyActivity.sendCustomMessage();
}
}
}
The proxy activity would have at least these methods and signature:
public class MyProxyActivity extends FragmentActivity implements CallerProxy {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// call setContentView and then make sure you've added FragmentA and
// FragmentB.
}
#Override
public void sendCustomMessage(Object... dataParams) {
// FragmentB must be identified somehow, either by tag,
// either by id. Suppose you'll identify by tag. This means you've added
// it previously with this tag
Fragment fragment = getSupportFragmentManager().findFragmentByTag("FragmentB-TAG");
if (fragment != null) {
FragmentB fragB = (FragmentB) fragment;
fragB.dataFromAReceived(dataParams);
}
}
}
While in FragmentB all you need is a method that can be called with above sent parameters:
public void dataFromAReceived(Object ... data) {
// here you have the data
}
Use or implement some sort of event bus. Some general details here. For Android I remember that Otto event bus was very handy and easy to use. Here's a link with this. This is very similar to first option as you need anyway to register and un-register.
In the end it depends on what you need to send as a message, when should it be received and how flexible does it need to be. ... your choice!
Enjoy programming!
Fragments are not supposed to connect to each other directly, that may be your problem in finding a decent guide to do this.
Your approach makes the assumption that a fragment B will always be reachable (and ready) for a fragment A to interact, and that is actually not true, will kill the flexibility of your Fragment and will cause you problems in the future.
A better approach to interaction of Fragments is to talk only through interfaces that talk directly to a activity that can handle who is alive when where and should receive what.
-> http://developer.android.com/training/basics/fragments/index.html
This Android guide above, specifically on the last topic, shows you how to do this.
i hope this code help you..
in your first fragment add this code
onCreateView
LocalBroadcastManager broadcastManager = LocalBroadcastManager.getInstance(getActivity());
IntentFilter intentFilter = new IntentFilter("update");
// Here you can add additional actions which then would be received by the BroadcastReceiver
broadcastManager.registerReceiver(receiver, intentFilter);
#Override
public void onDestroyView() {
LocalBroadcastManager.getInstance(getActivity()).unregisterReceiver(receiver);
super.onDestroyView();
}
private BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action != null && action.equals("update")) {
// perform your update
getOngoingOrderData();
}
}
};
in your second fragment add this code where you send broadcast..
Intent intent = new Intent("update");
LocalBroadcastManager broadcastManager = LocalBroadcastManager.getInstance(getActivity());
broadcastManager.sendBroadcast(intent);
I have several activities which use several audio features. For that, I have a MediaPlayer in a singleton java class, so the activities interact with that class and just exist on the media player.
One of the features is to stop automatically the media player after X minutes. So I created a timer in the singleton class and stops perfectly the radio streaming. the problem is that there is no feedback or callback to the running activity. There is a play/stop button wich has to change the image and I do not know how can I capture that onStop event or whatever....or can be called from a single java class the current activity class running, so I could call a function of the activity in order to change the image?
You probably want to use a broadcast receiver for this.
From your singlton class which does the stopping, when your timer stops the music, call this method:
public void broadcastMusicPaused(View v){
Intent broadcast = new Intent();
broadcast.setAction("MUSIC_STOPPED");
sendBroadcast(broadcast);
}
Then, from your controlling activity, set up your receiver like this:
private BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
Toast.makeText(getApplicationContext(), "Music Paused", Toast.LENGTH_SHORT).show();
displayMusicStopped(); //switches images
}
};
#Override
protected void onResume() {
IntentFilter filter = new IntentFilter();
filter.addAction("MUSIC_STOPPED");
registerReceiver(receiver, filter);
super.onResume();
}
#Override
protected void onPause() {
unregisterReceiver(receiver);
super.onPause();
}
First of all, thanks jameo for his answer, sounds pretty good, but i do not know if i will have time to try, i promise i will if i can this week or next time i have a similar issue.
Finally i did the trick this way:
1 - Create a Interface with Method onStopMediaPlayer(); //For example call MediaPlayerStopInterface
public interface MediaPlayerStopInterface {
/**
* Called when the player timer ends
*/
public void onStopMediaPlayer();
}
2 - My activities classes implements the interface switching images.
public class PortadaActivity extends Activity implements MediaPlayerStopInterface{
public void onStopMediaPlayer(){
//Switch images or whatever
}
}
3 - My singletton class has an object of the type of the interface MediaPlayerStopInterface
public class AudioControllerClass { //The Singletton Java Class
private MediaPlayerStopInterface currentActivity;
public void setCurrentActivity(MediaPlayerStopInterface mpsi){
currentActivity=mpsi;
}
}
4 - My activities classes in onResume() do a Singlettonclass.setStoppedPlayerInterface(this), so i always have a reference of the running activitie.
public class PortadaActivity extends Activity implements MediaPlayerStopInterface{
public void onResume() {
AudioControllerClass.getInstance(getApplicationContext()).setCurrentActivity(this); //In every resume the singletton class knows who was the last one in being active
}
}
5 - when timer execute, as i have the activitie class reference, i just call object_StoppedPlayerInterface.stoppedPlayer();
public class AudioControllerClass { //The Singletton Java Class
class TimerRadio extends TimerTask {
public void run() {
if(whatever==true){
currentActivity.onStopMediaPlayer();
}
}
}
}
Finally, i didn't code it, but the callback to onStopMediaplayer in activities must be done with a Handler, if you do not want a "Only UI thread can touch his views" exception :P
It works perfectly :). But i don't know if it is a really bad practice or is not so horrible xD
Anyway thanks Jameo. Yours sound much more elegant :P
I currently have a tabhost with 5 tabs. Over one of the tabs I have an ImageView that when the tabs are created it pulls data via POST to display a number. I am wondering how from one of the tab activities (say Rate.java) I could call that method to update that ImageView that is over one of the tabs.
I know it's not very specific but I think I wrote it so you know what I am talking about.
Let me know if you require anymore info.
talitore
Based on the information given, two options that immediately come to mind are:
Send a broadcast from the tab activity (e.g. Rate.java) and have the activity hosting the ImageView listen for it.
Create some sort of BaseActivity (extending Activity) that takes a custom Listener interface with an update method. Have your tab activities extend that BaseActivity and the activity with your ImageView implement it. You can then call the update method on the listener from your tab activities (instantiate them as a BaseActivity and pass along the listener) and make the activity with the ImageView act upon it.
//Edit per request:
A good starting point for information about broadcasts and receivers is the documentation for the BroadcastReceiver. In your case it's probably easiest to just create them in code.
A minimal example will contain something like the following:
BroadcastSendingActivity:
public class BroadcastSendingActivity extends Activity {
public static final String UPDATE_IMAGEVIEW = "UPDATE_IMAGEVIEW";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.sender);
Intent i = new Intent();
i.setAction(UPDATE_IMAGEVIEW);
sendBroadcast(i);
}
}
BroadcastReceivingActivity:
public class BroadcastReceivingActivity extends Activity {
private BroadcastReceiver mReceiver;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.receiver);
}
#Override
protected void onPause() {
super.onPause();
unregisterReceiver();
}
#Override
protected void onResume() {
super.onResume();
registerReceiver();
}
private void registerReceiver() {
if (mReceiver == null) {
mReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(BroadcastSendingActivity.UPDATE_IMAGEVIEW)) {
// code to update imageview...
}
}
};
}
getApplicationContext().registerReceiver(mReceiver, new IntentFilter(BroadcastSendingActivity.UPDATE_IMAGEVIEW));
}
private void unregisterReceiver() {
if (mReceiver != null) {
getApplicationContext().unregisterReceiver(mReceiver);
}
}
}
Note that I did not test the code, but I'm sure you'll be able to figure out any mistakes I might've made. :)