I am working on a budget app and am having trouble getting my expenses to the class that will create a graph. The user will be able to input an expense along with a check box indicating what they spent money on. When I send the value to MainActivity, it reads the expense and shows the updated budget, however, when I try to read it in my class that will make a graph, the graph shows up empty and is not reading the values. Here is my Expense Activity (Screen 2):
public class ExpensesActivity extends AppCompatActivity {
EditText editTextExpense;
Expenses expenses;
String selectedCheckBox;
CheckBox personalCheckBox;
CheckBox commuteCheckBox;
CheckBox billsCheckBox;
CheckBox funCheckBox;
CheckBox workCheckBox;
CheckBox foodCheckBox;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_expenses);
editTextExpense = (EditText)findViewById(R.id.moneySpent);
personalCheckBox = (CheckBox)findViewById(R.id.personalCheckBox);
commuteCheckBox = (CheckBox)findViewById(R.id.commuteCheckBox);
billsCheckBox = (CheckBox)findViewById(R.id.billsCheckBox);
funCheckBox = (CheckBox)findViewById(R.id.funCheckBox);
workCheckBox = (CheckBox)findViewById(R.id.workCheckBox);
foodCheckBox = (CheckBox)findViewById(R.id.foodCheckBox);
backToMainMenu();
}
public void backToMainMenu() {
final Button expenseButton = (Button)findViewById(R.id.addExpenseSecondScreen);
expenseButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (personalCheckBox.isChecked()) {
selectedCheckBox = personalCheckBox.getText().toString();
}
if (commuteCheckBox.isChecked()) {
selectedCheckBox = commuteCheckBox.getText().toString();
}
if (billsCheckBox.isChecked()) {
selectedCheckBox = billsCheckBox.getText().toString();
}
if (funCheckBox.isChecked()) {
selectedCheckBox = funCheckBox.getText().toString();
}
if (workCheckBox.isChecked()) {
selectedCheckBox = workCheckBox.getText().toString();
}
if (foodCheckBox.isChecked()) {
selectedCheckBox = foodCheckBox.getText().toString();
}
float expense = Float.parseFloat(editTextExpense.getText().toString());
Intent intent = getIntent();
intent.putExtra("expense", expense);
intent.putExtra("checkBoxText", selectedCheckBox);
setResult(RESULT_OK, intent);
finish();
}
});
}
}
I am not sure what you are expecting here but onActivityResult is used to get the result from starting an Activity using startActivityForResult and will only be called in the activity which called startActivityForResult.
Since activities are only active one at a time (unless you are targeting the N preview) you would have to pass the result data using the start intent to the other activity.
Use BroadcastReceiver register the BroadcastReceiver to both activity and fire the BroadcastReceiver when you want to send the data. Use LocalBroadcastManager
sample code to create BroadcastReceiver
public class MyReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "Intent Detected.", Toast.LENGTH_LONG).show();
}
}
registerReceiver(receiver, filter);
and unregister in onDestroy()
#Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(receiver);
}
I don't know exactly what you want,are you tried "static" for expense and
selectedCheckBox variables.Then you easily get the values from any where or use
shared preference.
Related
I want to define callback for a Notebook program that way after note saved in EditActivity,in Main Activity Update list of notes,But This does not happen.
EditActivity:
public interface OnClickDoneListener{
void onClickDone();
}
public void setOnClickDoneListener(OnClickDoneListener onClickDoneListener){
this.onClickDoneListener=onClickDoneListener;
}
btnDone.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (extras != null) {
myDatabase.updateRow(id, txtTitle.getText().toString(), txtDesc.getText().toString());
NoteModel noteModel = new NoteModel();
noteModel.setTitle(txtTitle.getText().toString());
noteModel.setDesc(txtDesc.getText().toString());
Intent intent = new Intent(EditActivity.this, MainActivity.class);
startActivity(intent);
onClickDoneListener.onClickDone();
} else {
Done();
}
}
});
Main Activity:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
setupViews();
getDataFromDB();
recyclerView.setAdapter(new NoteAdapter(MainActivity.this,dataList));
noteAdapter = new NoteAdapter(MainActivity.this,dataList);
recyclerView.setAdapter(noteAdapter);
fabAdd = (FloatingActionButton)findViewById(R.id.fab_main_add) ;
EditActivity editActivity = new EditActivity();
editActivity.setOnClickDoneListener(new EditActivity.OnClickDoneListener() {
#Override
public void onClickDone() {
noteAdapter.notifyDataSetChanged();
}
});
please help me.
My English is poor,sorry for it.
You want your data to be updated when the MainActivity is shown, right ?
You just have to call your getDataFromDB part in onResume() instead of onCreate()
EditActivity editActivity = new EditActivity();
editActivity.setOnClickDoneListener(new EditActivity.OnClickDoneListener() {
#Override
public void onClickDone() {
noteAdapter.notifyDataSetChanged();
}
});
What you are doing here is setting the value of OnClickDoneListener in a new instance of EditActivity that you will never use. Because when you use new Intent(MainActivity.this, EditActivity.class) to start the EditActivity it will create a new instance of EditActivity and your interface would be null.
I suggest you use android's Broadcast Receiver instead of a callback in this case. All you have to do is:
In MainActivity: You will need to instantiate a broadcast receiver. You need to register it onCreate or onStart and unregister it onStop or onDestory.
In EditAcitivity: You send a broadcast whenever you want to notify your MainActivity to update the list.
Check out this example:
https://riptutorial.com/android/example/18305/communicate-two-activities-through-custom-broadcast-receiver
I have Act_01 (where I put value) and Act_02 (where I get value) but have declared these methods in a Extras class, getting value from Act_02 returns null value:
Act_01: (Where I want to pass the value Name to Act_02)
public class Act_01 extends Activity {
Extras cc_Extras;
Button btn1;
Intent intent;
String str_Name;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.act_01);
cc_Extras = new Extras();
str_Name = "Buck";
btn1 = (Button) findViewById(R.id.btn1);
btn1.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
cc_Extras.putExtras();
startActivity(intent);
}
});
}
}
Act_02: (Where I want ot receive value Name from Act_01 but the app crashes with null value)
public class Act_02 extends Activity {
Extras cc_Extras;
String str_Name;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.act_02);
cc_Extras = new Extras();
if(getIntent() != null && getIntent().getExtras() != null)
{
cc_Extras.getExtras();
}
Toast.makeText(getApplicationContext(), "Name: "+str_Name, Toast.LENGTH_SHORT).show();
}
}
Extras: (Where I define the methods to put and get Extras)
public class Extras extends Activity {
String str_Name;
Intent intent;
public void putExtras() {
// TODO Auto-generated method stub
intent.putExtra("KEY_Name", str_Name);
}
public void getExtras() {
// TODO Auto-generated method stub
str_Name = getIntent().getExtras().getString("KEY_Name");
}
}
EDIT: I do not want to pass and get data directly between activities, I want to use the 3rd class (Extras.java) because I have too many activities having too many values between each other and want to sort of define them globally in Extras so that all my other activities can just call one method instead of getting and putting too many values in my activities.
Your app crashes not with a null value, but a null pointer reference because you created a new Activity manually
cc_Extras = new Extras();
Then called a lifecycle method on it
cc_Extras.getExtras()
Which calls getIntent(), but the Intent was never setup by the Android framework, and cc_Extras.getExtras() wouldn't have any of the data you wanted anyway in the second Activity because it was just created there, not from the first Activity.
Briefly, you should never make a new Activity, and your Extras class does not need to be an Activity in the first place (nor does it provide much benefit).
Just use the Intent object provided by the first Activity to start the second Activity, and get extras like normal. Don't overcomplicate your code. Regarding the title of the question, Intent and Bundle are already "another class" designed by Android for you to transfer data.
On both activities you are creating a new instances of Extras class means they dont hold the same value you can do this to transfer data from A to B
public class Act_01 extends Activity {
Button btn1;
Intent intent;
String str_Name;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.act_01);
str_Name = "Buck";
btn1 = (Button) findViewById(R.id.btn1);
btn1.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
intent = new Intent(Act_01.this, Act_02.class);
intent.putExtra("data", str_Name)
startActivity(intent);
}
});
}
}
And receieve data like this
public class Act_02 extends Activity {
String str_Name;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.act_02);
// cc_Extras = new Extras();
if(getIntent() != null)
{
if (getIntent().getStringExtra("data") != null) {
Toast.makeText(Act_02.this, "Name: "+getIntent.getStringExtra("data"), Toast.LENGTH_SHORT).show();
}
}
}
}
Also you should consider using Activity Context instead of the application context
Ok! so here are the few things I might wanna suggest you to correct.
Changes needs to be done in the code.
You are not assigning anything to "intent" object , and you have passed a intent without assigning anything to it.
Your instance cc_Extra isn't doing anything in the activity1. You might wanna pass the "intent" object in your constructor of class like cc_Extras= new Extras(intent); and in the Extras class do the following- Intent intent;
Extras(Intent i)
{
this.intent=i;
}
In the activity2 you are creating the new Instance of Extras(). So according to your code it is going to be NULL by default. If you have done the changes from the previous step, you can create new instance by doing cc_Extras(getIntent());
Corrections in the code
1) In Extras class getExtras() method instead of str=getIntent() use str=intent.getExtras.getString().
2) In the activity2 you are not assigning anything to your String str_Name, so you need to return the string you got in getExtras() method. You can do it by changing the return type to String. Below is the sample code.
public String getExtras()
{
str_Name=intent.getExtras().getString("KEY_Name");
//OR
//str_Name=intent.getStringExtra("KEY_Name");
return str_Name;
}
3) By the doing this you need to catch this string in the activity2 by doing `
if(getIntent() != null && getIntent().getExtras() != null)
{
str_Name=cc_Extras.getExtras();
}`
4) Another thing is you must create intent like this-
Intent intent=new Intent(currentActivityName.this,anotherActivity2.class);
//then use the intent object
EDIT- Your code must look like this in the end...
Act1
public class Act_01 extends Activity {
Extras cc_Extras=null;
Button btn1;
String str_Name;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.act_01);
str_Name = "Buck";
btn1 = (Button) findViewById(R.id.btn1);
btn1.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
//changes to do
Intent intent= new Intent(Act01.this,Act02.class);
cc_Extras= new Extras(intent);
cc_Extras.putExtras(str_Name);
//end
startActivity(intent);
}
});
}
}
Act02
public class Act_02 extends Activity {
Extras cc_Extras;
String str_Name;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.act_02);
cc_Extras = new Extras(getIntent());
if(getIntent() != null && getIntent().getExtras() != null)
{
str_Name=cc_Extras.getExtras();
}
Toast.makeText(getApplicationContext(), "Name: "+str_Name, Toast.LENGTH_SHORT).show();
}
}
Extras class
public class Extras { //remove "extends Activity" because it is a class not a activity
String str_Name;
Intent intent;
Extras(Intent i)
{
this.intent=i;
}
public void putExtras(String str) {
// TODO Auto-generated method stub
str_Name=str;
intent.putExtra("KEY_Name", str_Name);
}
public String getExtras() {
// TODO Auto-generated method stub
str_Name = intent.getExtras().getString("KEY_Name");
return str_Name;
}
}
Above code will work just on String. You can extend the functionality if you want.
I hope this must work to get your code working!
I'm trying to start a robospice request in Activity A and then receive the results of the request in Activity B.
In Activity A
FetchSpiceRequest fetchSpiceRequest = new FetchSpiceRequest();
spiceManager.execute(fetchSpiceRequest, new ActivityB().postListener);
The PostListener implements PendingRequestListener and is sitting within Activity B.
In Activity B
We addListener for the pending request that was started in Activity A below.
#Override
protected void onStart() {
spiceManager.start( this );
spiceManager.addListenerIfPending(Post.class, 0, new PostListener());
We then implement the PostListener in Activity B below.
public final class PostListener implements PendingRequestListener<Post> {
#Override
public void onRequestFailure(SpiceException spiceException) {
Toast.makeText(MainActivity.this, "Exception: " + spiceException.toString(), Toast.LENGTH_SHORT).show();
}
#Override
public void onRequestSuccess(Post post) {
Toast.makeText(MainActivity.this, "Added", Toast.LENGTH_SHORT).show();
}
#Override
public void onRequestNotFound() {
Toast.makeText(MainActivity.this, "Request not found", Toast.LENGTH_SHORT).show();
}
}
Finally, we create a variable within Activity B called so that A can pick it up (eg. when I call new ActivityB().postListener):
public PostListener PostListener;
This doesn't work and it always calls onRequestNotFound in the PostListener.
What am I doing wrong?
I have also looked at this post: Robospice - keep spice service continue running when changing activity but #Snicolas doesn't seem to mention anything about how the spiceManager.execute statement should look like in the first activity.
Eventually, I had to use LocalBroadcastManager to achieve what I want to do:
Activity A
FetchSpiceRequest fetchSpiceRequest = new FetchSpiceRequest();
spiceManager.execute(fetchSpiceRequest, postListener);
Please note that postListener is situated within Activity A. It actually doesn't do much as all the work will now be performed inside the localbroadcastreceiver:
public final class AddPostListener implements RequestListener<Post> {
#Override
public void onRequestFailure(SpiceException spiceException) {
Toast.makeText(AddPostActivity.this, "Exception: " + spiceException.toString(), Toast.LENGTH_SHORT).show();
}
#Override
public void onRequestSuccess(Post post) {
Toast.makeText(AddPostActivity.this, "Added", Toast.LENGTH_SHORT).show();
Log.e("AddPostListener", "Background service finished"); }
Inside the fetchSpiceRequest, we perform the work asynchronously and once it is finished, we call the localbroadcast manager to send our results back to Activity B:
public class FetchSpiceRequest extends SpringAndroidSpiceRequest<Post> {
private Post mPost;
private Context mContext;
public AddPostSpiceRequest(Context context, Post post) {
super(Post.class);
mPost = post;
mContext = context;
}
#Override
public Post loadDataFromNetwork() throws Exception {
//Do some network activity to get a post and then after the network activity ends, send the post through the broadcast activity to Activity B
Intent intent = new Intent("my-event");
intent.putExtra("post", post);
LocalBroadcastManager.getInstance(mContext).sendBroadcast(intent);
}
Activity B
Register your localBroadcast receiver:
#Override
public void onResume() {
super.onResume();
LocalBroadcastManager.getInstance(this).registerReceiver(mMessageReceiver,
new IntentFilter("my-event")); }
Do something with the received message:
private BroadcastReceiver mMessageReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
// Get extra data included in the Intent
Post post = intent.getParcelableExtra("post");
Log.e("receiver", "Got message: " + post.getPostheader());
}
};
I need the button to be static so I can enable it/ disable it form my services in case the activity is shown. Still I setOnClickListener and anyway static views are considered dangerous. Do I leak ? Can I avoid it ?
public class MonitorActivity extends FragmentActivity implements
OnClickListener {
private static Button updateButton; // static??
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_monitor);
// button
updateButton = (Button) findViewById(R.id.update_data_button);
updateButton.setOnClickListener(this); // oops ?
}
public static void onDataUpdated(Context ctx) {
if (updateButton != null) { //that's why I need it static
updateButton.setEnabled(true); // + set the text etc
}
}
public static void onUpdating() {
if (updateButton != null) {
updateButton.setEnabled(false);
}
}
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.update_data_button:
serviceIntent.putExtra(MANUAL_UPDATE_INTENT_KEY, true);
this.startService(serviceIntent);
}
}
#Override
protected void onResume() {
super.onResume();
Boolean isUpdating = AccessPreferences.get(this, updateInProgressKey,
false);
// set the button right
updateButton.setText((isUpdating) ? defaultUpdatingText
: getResources().getString(R.string.update_button_text));
updateButton.setEnabled(!isUpdating);
}
}
I think You can create BroadcastReceiver in MonitorActivity. And send extras message from Service to enable/disable button.
I suggest you use LocalBroadcastManager
In your Activity define a BroadcastReceiver and register the Broadcast in onStart()onResume() and unregister it in onStop()onPause().
From your Service send the Broadcast to the Activity if the Activity is active it will receive the Broadcast and update the UI, if not nothing will happen.
Define another BroadcastReceiver in your Service, Register the Broadcast in onCreate() and Unregister it in onDestroy().
When your Activity is started send a Broadcast to the Service and let the Service reply to the Activity using the first Broadcast to update the UI.
UPDATE
After doing some investigation I found you're correct "sticky broadcasts are discouraged", but if you check the date of that post it's on 2008 - before Google implemented the LocalBroadcastManager.
And I have checked the source code of LocalBroadcastManager, it's not a real Broadcast it's an interface, Singleton with a list of BroadcastReceivers (not global and no IPC communication).
I really hate public static and I always avoid them. every body should.
So yes - the static button would leak my activity. I came up with callback below but it is ugly. I finally solved it by making the Activity extend OnSharedPreferenceChangeListener
public final class MonitorActivity extends FragmentActivity implements
OnClickListener, OnSharedPreferenceChangeListener {
private Button updateButton;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
updateButton = (Button) findViewById(R.id.update_data_button);
updateButton.setOnClickListener(this); //no need to unregister methinks
}
#Override
public synchronized void onSharedPreferenceChanged(
SharedPreferences sharedPreferences, String key) {
if (updateInProgressKey.equals(key)) {
final Boolean isUpdating = AccessPreferences.get(this,
updateInProgressKey, false);
// set the button right
updateButton.setText((isUpdating) ? sDefaultUpdatingText
: sUpdateButtonTxt);
updateButton.setEnabled(!isUpdating);
}
}
#Override
protected void onStart() {
super.onStart();
AccessPreferences.registerListener(this, this);
AccessPreferences.callListener(this, this, updateInProgressKey);
}
#Override
protected void onStop() {
// may not be called (as onStop() is killable), but no leak,
// see: http://stackoverflow.com/a/20493608/281545
AccessPreferences.unregisterListener(this, this);
super.onStop();
}
}
Callback
onPause() is guaranteed to run - so I null the static fields there and populate them on onResume(). I only do a read from default shared preferences so it should not take long in the synchronized blocks (I have to synchronize cause the service might call onUpdating() or onDataUpdated() any odd time). Not sure about unregistering the listener though
public class MonitorActivity extends FragmentActivity implements
OnClickListener {
private static TextView dataTextView; //null this onPause() to avoid a leak
private static Button updateButton; // null this onPause() to avoid a leak
// ...
public static synchronized void onDataUpdated(Context ctx) {
if (updateButton != null) {
updateButton.setEnabled(true); // + set the text etc
}
}
public static synchronized void onUpdating() {
if (updateButton != null) {
updateButton.setEnabled(false);
}
}
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.update_data_button:
serviceIntent.putExtra(MANUAL_UPDATE_INTENT_KEY, true);
this.startService(serviceIntent);
}
}
#Override
protected void onResume() {
super.onResume();
synchronized (MonitorActivity.class) {
Boolean isUpdating = AccessPreferences.get(this,
updateInProgressKey, false);
updateButton = (Button) findViewById(R.id.update_data_button);
updateButton.setOnClickListener(this);
// set the button right
updateButton.setText((isUpdating) ? defaultUpdatingText
: getResources().getString(R.string.update_button_text));
updateButton.setEnabled(!isUpdating);
}
}
#Override
protected void onPause() {
synchronized (MonitorActivity.class) {
updateButton.setOnClickListener(null); // TODO : needed ??
dataTextView = updateButton = null; // to avoid leaking my activity
}
super.onPause();
}
}
There are 3 solutions for you:
Set button = null when context is destroyed(onStop);
Use WeakReference for button field, Example:
private static WeakReference<Button> updateButton;
Not creating static button. It's always hold the context.
Let's say that I've the following main activity:
public class MwConsoleActivity extends Activity {
private classChild child = null;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
child = new classChild();
}
}
Then consider the implementation of the class "classChild":
public class MwBondingAgent extends SapereAgent {
MwBondingAgent(){}
public void AddEventListener(childAddedEvent event) {
//Send the data of event back to the main activity
}
}
I've tried to use IntentServices but was not able to receive the values back to the main activity. What would be the approach I've to take?
Cheers
Ali
You can use and intentFilter to listen for broadcasts.
Add this to the activity:
IntentFilter intentFilter = new IntentFilter(
"com.unique.name");
mReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
//extract our message from intent
String msg_for_me = intent.getStringExtra("some_msg");
}
};
//registering our receiver
this.registerReceiver(mReceiver, intentFilter);
In your class add this to the part you want to notify the activity:
Intent i = new Intent("com.unique.name").putExtra("some_msg", "I have been updated!");
this.sendBroadcast(i);
You should use the observer / listener pattern.
http://www.vogella.com/articles/DesignPatternObserver/article.html
It is one of the most used design patterns when using MVC architecture pattern.
Your question is quite unclear but I think what you are wanting is to implement a callback to your activity. You can do this using an interface.
public class MwConsoleActivity extends Activity implements MwBondingAgent{
private classChild child = null;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
child = new classChild();
}
#Override
public void gotEventData(EventData myEventData) {
//to whatever you want with myEventData
}
}
And in your other class.
public class MwBondingAgent extends SapereAgent {
private MwBondingAgentCallback activityCallback;
MwBondingAgent(Activity callback){
activityCallback = callback;
}
public void AddEventListener(childAddedEvent event) {
//Send the data of event back to the main activity
EventData myEventData = //got some event data;
//Send it back to activity
activityCallback.gotEventData(myEventData);
}
public interface MwBondingAgentCallback {
public void gotEventData(EventData myEventData);
}
}