I have application where Activity A allows to set some options and starts (after click on button) Activity B. User can do something in B and when he finishes, he has choice (RadioButtons):
repeat - it means the B Activity will run again with the same options (taken from A),
new - it means application finishes B Activity and goes back to A, where user can set options again and start B again,
end - it goes out from application (I suppose it should finish B and then A Activity).
First I have done this way:
Intent intent = getIntent();
finish();
startActivity(intent);
Another way I could use is to clean all parameters in this Activity, but above was more quickly.
Second is just a finish().
Third is the biggest problem and i don't know how to to this. I tried with startActivityForResult(), onActivityResult() and setResult() but i saw it's impossible to set different results depending on selected RadioButton.
Other method I found is
public static void closeAllBelowActivities(Activity current) {
boolean flag = true;
Activity below = current.getParent();
if (below == null)
return;
System.out.println("Below Parent: " + below.getClass());
while (flag) {
Activity temp = below;
try {
below = temp.getParent();
temp.finish();
} catch (Exception e) {
flag = false;
}
}
}
source
But don't know how to put in current Activity.
Could you help me with this?
"i saw it's impossible to set different results depending on selected RadioButton" isn't really clear to me. Anyway, I suggest you what I would do: Activity A starts Activity B with startActivityForResult() and A wait for a bundle coming from B. When you build the exit function from B to A, create a bundle with your result. In A, you will analyze the bundle coming from B and decide what to do with. If you would exit, call finish().
Edit: Ok this could be your case:
Activity A:
public class XY extends Activity {
// you need that as a flag to go back and forth:
protected static final int SUB_ACTIVITY_REQUEST_CODE_04 = 5337; // choose the number you want...
int result_from_B; // I usually use an int, choose as you want....
static final String KEY_FROM_B = "mKey";
#Override
protected void onActivityResult(int result,int resultCode,Intent data) {
super.onActivityResult(result, resultCode, data);
if (result == SUB_ACTIVITY_REQUEST_CODE_04) {
if (data!=null){
Bundle extras = data.getExtras();
result_from_B = extras.getInt(XY.KEY_FROM_B);
if (result_from_B==1) {
// do what you want
} else {
// do somthing else...
}
}
To call the Activity B, use this:
Intent i = new Intent(this,
ActB.class);
this.startActivityForResult(i,SUB_ACTIVITY_REQUEST_CODE_04);
In the Activity B:
public class ActB extends Activity{
protected final int SUCCESS_RETURN_CODE = 1; // you need it to come back
int result; // this is the result to give to activity A
}
// in the function to build the output:
// I suppose you have already put something in result:
Bundle bundle = new Bundle();
bundle.putInt(XY.KEY_FORM_B, result);
Intent mIntent = new Intent();
mIntent.putExtras(bundle);
ActB.this.setResult(SUCCESS_RETURN_CODE,mIntent);
finish();
This is basic. You can manage also booleans back and forth, arrays, all you want (admitted by a bundle). Just put it in a bundle and go :)
Don't forget to declare your classes (activities) in the Manifest otherwise it will throw an exception runtime.
First of all, you need to keep track of the Intent used to call B. Try the putExtra() method of the intent to B. You can also package everything into a Bundle and restore it with getExtras() (if I recall correctly, that's the method name). When on B, read the Intent used to call it and save the parameters. You also need to startActivityForResult(B) for the following to work.
repeat - it means the B Activity will run again with the same options
(taken from A),
You probably want to call, from B, Activity B again using the FLAG_ACTIVITY_SINGLE_TOP flag in this case. I assume you don't want to have two B instances. Otherwise, just don't use the flag.
Put the Bundle you received from the Intent (from A) again, and catch it (if using single top) in the onNewIntent() method (or just normally, onCreate, if not single top). It goes like this: B -> B onPause() -> B onNewIntent() -> B onResume().
new - it means application finishes B Activity and goes back to A,
where user can set options again and start B again,
Depending on the exact behavior you want, you could call A with FLAG_ACTIVITY_REORDER_TO_FRONT. In this case, you end up with A in the foreground and B in the background (pressing back will go back to B).
Or you could call finish() if you just don't want B anymore, and want to go back to A.
end - it goes out from application (I suppose it should finish B and
then A Activity).
Do nothing on B, setResult() to something like "RESULT_FINISH_EVERYTHING", and when taking care of the results in A (override "onActivityResult()", IIRC), finish activity A also.
but i saw it's impossible to set different results depending on
selected RadioButton.
You could setResult() depending on thw button checked in the radio button. You can set listeners to the radio group and read which button is selected. See RadioGroup.OnCheckedChangeListener or View.OnClickListener if you need actions for eadch individual radio button.
Really, not too complicated. It just depends on what you want. I can clarify all this if you want.
Good luck!
Related
I have 2 activities in my assignment: MainActivity and Country_Activity.
I'm trying to pass 2 inputs the user puts in MainActivity:
int counter
String Country
But the app always crashes here: (this is Country_Activity)
private void Update(){
Intent mIntent = getIntent();
int intValue = mIntent.getIntExtra("intCounter", 0);
String country = mIntent.getStringExtra("country");
counterTextView.setText(intValue);
countryTextView.setText(country);
if (country.equals("canada")){
flagView.setImageResource(R.drawable.canada);
}
else if (country.equals("us")){
flagView.setImageResource(R.drawable.us);
}
}
Specifically on the lines "setText" for each variable.
Everything else works. I can't figure out why they wouldn't.
Thanks!
Usually the extras that are passed from one activity to another are read inside on onCreate() where all the needed initialization of variables and views is made.
In your case I see that you get the extras inside another method (maybe it's called inside onCreate()?).
So you forgot to initialize the textviews:
TextView counterTextView = findViewById(R.id.countersomething);
TextView countryTextView = findViewById(R.id.countrysomething);
also another error that you will encounter later is this:
counterTextView.setText(intValue);
change it to:
counterTextView.setText(String.ValueOf(intValue));
Don't pass an integer value inside setText() because it will be treated as the id of a resource.
I got a few fragments and I tried to sort out the transitions between them. I can go from Main -> A -> B -> C. Then once I am done with the stuffs in C, I wanted to go back to B -> A -> Main. This is the desired transition I wanted to achieve.
However, with my current code, there is something weird with the transition. I go from Main -> A -> B -> C, then inside C I am doing some SQL to create data in database, once I am done, I go back to B. However, from there, when I pressed back button, it go back to C -> B -> A -> Main. There is an unnecessary C in the back transition.
Inside my Main, I am calling A like this:
final SettingActivity settingFragment = new SettingActivity();
ft.replace(R.id.frame,settingFragment);
ft.addToBackStack("tag");
ft.commit();
Inside my A, I am calling B like this:
FragmentTransaction it = getFragmentManager().beginTransaction();
it.replace(R.id.frame,categoryFragment);
it.addToBackStack("tag");
it.commit();
Inside my B, I am calling C like this:
FragmentTransaction et = getFragmentManager().beginTransaction();
et.replace(R.id.frame,editFragment);
et.addToBackStack("tag");
et.commit();
Then inside my C, when I am successfully inserted a record in database, I am calling B like this:
// button on click listener
new GetAllCategoriesAsyncTask(
new GetAllCategoriesAsyncTask.OnRoutineFinished() {
public void onFinish() {
Bundle bundle = new Bundle();
FragmentTransaction it = getFragmentManager().beginTransaction();
bundle.putSerializable("categorylist", GetAllCategoriesAsyncTask.categoryList);
categoryFragment.setArguments(bundle);
it.replace(R.id.frame,categoryFragment);
it.addToBackStack("tag");
it.commit();
}
}).execute();
Then inside my B, I am getting the data like this in onCreate():
categoryList = (ArrayList<Category>) getArguments().getSerializable("categorylist");
On button click pop the current fragment from stack which is C , instead of adding B again in the stack. So, replace the button onClick code with below line of code:
getFragmentManager().popBackStack();
Bundle is used when you transferring data to next screen. For transferring data to previous screen you need to use callbacks.
For reference please find below attached link :Communicating with Other Fragments
I've searched on many SO questions , they are to old and can't find better solution on docs.oracle.com , i don't want to convert Each StringBuilder to string to pass an string array so how to achieve it ?
From inside the source Activity:
Intent intent = new Intent(this, DestActivity.class);
intent.putCharSequenceArrayListExtra("strings", myStringBuilders);
startActivity(intent);
You might have to do something like new ArrayList(Arrays.asList(myStringBuilders));
Make it into a Collection and then pass it through an intent, and then convert it back to an Array.
To pass an StringBuilder array you can use the putExtra of Intent, like this:
Intent it = new Intent(this, YourActivity.class);
StringBuilder[] sbAr = new StringBuilder[4];
sbAr[0] = new StringBuilder("Array0");
sbAr[1] = new StringBuilder("Array1");
it.putExtra("sbParam", sbAr);
however I do not know exactly why when you will retrieve the value in activity targets the StringBuilder array is recovered as CharSequence array.
CharSequence[] csA = (CharSequence[]) getIntent().getExtras().get("sbParam");
I hope this helps
The best way I've seen to pass around objects without bundling is to use an EventBus (I recommend greenrobot/EventBus).
An EventBus can be much faster than bundling. See this article.
The only down side I can see is that if your Activity/process is destroyed because of low memory it will lose the data when it's recreated; Whereas, if you store data in a bundle, the bundle will be resupplied to the Activity when it is recreated (could be wrong here).
Here's how it's done:
Create a POJO to store the StringBuilder for the event:
public class SaveStringBuilderEvent {
public StringBuilder sb;
public SaveStringBuilderEvent(StringBuilder sb){ this.sb = sb; }
}
Create first Activity, and post a StickyEvent before starting the next Activity (a 'StickyEvent' will keep the last event in memory until it is manually removed):
public class Activity1 extends Activity {
private StringBuilder sb;
...
// Function for loading the next activity
private void loadNextActivity(){
EventBus.getDefault().postSticky(new SaveStringBuilderEvent(sb));
Intent intent = new Intent(this, Activity2.class);
startActivity(intent);
}
...
}
Create Second Activity and remove the stick event from the EventBus:
public class Activity2 extends Activity {
StringBuilder sb = EventBus.getDefault()
.removeStickyEvent(SaveStringBuilderEvent.class)
.sb;
}
NOTE:
Personally I think it's the most proper way to handle such a situation where you don't want to bundle an object, however there are two other, less ideal, ways to maintain the state of an object between Activity life-cycles -- you could store the StringBuilder in the Application class or in a Singleton object; however, I wouldn't recommend this because:
If you store it in Application class then you liter variables from all Activities into the Application.
In addition, since the Application/Singleton have global scope, you have to make sure to null the variable so it can get garbage collected or it will stick around for the entire lifetime of the application, just wasting memory. However, using the eventBus, the variable is only stored "globally" between the postSticky and removeStickyEvent commands, so it will get garbage collected whenever those Activities do.
I have an application that has this transition:
A -> B -> C -> D-> C
Upon entering C , i have to check a flag. Then I have to pass it as intent (let us say intentX = false) to D. After doing something in D , it will then go back to C after pressing a button.
What i did was just pass again the intentX with value true, then startActivity C again.
So what happen is that it created another Activity C.
What i want to happen is that i will not have to start a new Activity C, but use the previous C by just calling super.onBackPressed(). But I cannot pass the new value of the intentX. Is there other way, to achieve what i want. I might have missed some.
What you want is startActivityForResult(). When you go from C to D, instead of using startActivity() use instead startActivityForResult(). Then when you want to return from D to C you use setResult() which can include an Intent object with extras to pass back to C.
I don't recommend doing this in onBackPressed() if you don't have to because this will not be what the user expects. Instead, you should return with this data with an event such as a Button click.
So, in C you will do something like
Intent i = new Intent(new Intent(C.this, D.class);
startActivityForResult(i, 0);
then in D when you are ready to return
Intent i = new Intent();
i.putExtra(); // insert your extras here
setResult(0, i);
then when you return to C you will enter this method (taken from the Docs)
protected void onActivityResult(int requestCode, int resultCode,
Intent data) {
if (requestCode == PICK_CONTACT_REQUEST) {
if (resultCode == RESULT_OK) {
// A contact was picked. Here we will just display it
// to the user.
startActivity(new Intent(Intent.ACTION_VIEW, data));
/*
can also get the extra sent back through data
using data.getStringExtra("someKey");
assuming the extra was a String
*/
}
There are some cases where startActivityForResult is not really needed or it is not practical to change all startActivity calls for startActivityForResult.
If the simple case of just starting a previous activity 'again' is needed, my recommendation is: Use the FLAG_ACTIVITY_CLEAR_TOP flag.
Quoting a brief description:
If set, and the activity being launched is already running in the
current task, then instead of launching a new instance of that
activity, all of the other activities on top of it will be closed and
this Intent will be delivered to the (now on top) old activity as a
new Intent.
For example, consider a task consisting of the activities: A, B, C, D.
If D calls startActivity() with an Intent that resolves to the
component of activity B, then C and D will be finished and B receive
the given Intent, resulting in the stack now being: A, B.
So this example
// From ActivityD
Intent intent = new Intent(getApplicationContext(), ActivityB.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); // The flag we wanted
intent.putExtra(ActivityB.SOME_EXTRA_THAT_I_NEED_CHANGED, SomeValue); // Example of changing the intent to get something new..
startActivity(intent);
Where you will get that new intent is defined by which launch mode and which flags where used to start it (in this case our ActivityB).
The currently running instance of activity B in the above example will
either receive the new intent you are starting here in its
onNewIntent() method, or be itself finished and restarted with the new
intent. If it has declared its launch mode to be "multiple" (the
default) and you have not set FLAG_ACTIVITY_SINGLE_TOP in the same
intent, then it will be finished and re-created; for all other launch
modes or if FLAG_ACTIVITY_SINGLE_TOP is set then this Intent will be
delivered to the current instance's onNewIntent().
i've a code that will get a image from the gallery and then set a gridSize with it.
the main code that will send android to the SizeSelection class.
PuzzleAcitivity:
if( resultCode == RESULT_OK) {
Intent gridSizeIntent = new Intent();
gridSizeIntent.setClass(this, SizeSelection.class);
startActivityForResult(gridSizeIntent, GRIDSIZE_VALUE_SELECT);
createGameBoard(SizeSelection.getGridSize(this));
}
but this is where the trouble kicks in: SizeSelection, when I hit the buttons that should activate and set a gridSize nothing happends. I think the problem is in getting the data from the SizeSelection to the main PuzzleActivity class.
sizeSelection:
protected static short getGridSize(Context content) {
if ( mIbtn3x3 == view) {
short gridSize = 3;
return gridSize;
}else if (mIbtn4x4 == view ) {
short gridSize = 4;
return gridSize;
}else if (mIbtn5x5 == view ) {
short gridSize = 5;
return gridSize ;
}
return gridSize;
}
how am I supposed to finish the getGridSize method? thanks in advance
When you start an activity for result, your current code does not block. That is, startActivityForResult() returns quickly and you do not have the result yet. In order to receive the result (once it is available), you must provide an onActivityResult(int, int, Intent) method in your calling class. That method will be called automatically once the result is available.
In your child activity, you must do two things to provide the result. First, you call setResult(int) to provide the current expectation of what the result will be. This does not return anything, it just prepares the result -- you may change it if you wish at any time prior to the second step. Second, you call finish() to finish your activity.
See http://developer.android.com/reference/android/app/Activity.html for full documentation.
I think that when you compare mIbtn... == view maybe there is not any match check it :)