Quick question: I have an activitygroup. Within that Activitygroup I have an activity. If I press back while inside this activity. the onBackPressed method of the activity is called - Not the Activitygroups onBackPressed - Why is that ?
EDIT: Got my answer but the problem remains. Here follows code and explanation of my original issue:
I am using ActivityGroups within a TabHost and as such have been "forced" into overriding onBackPressed. I can navigate through my application without issue by pressing back on my phone and by pressing the tabs on my tabhost. But I cannot interact with the interface after pressing Back.
Once I press one of the tabs on the tabhost again I can interact with everything like normal. Why is this happening? Do I need to override onResume?
Relevant code
SettingsActivityGroup :
public class SettingsActivityGroup extends ActivityGroup
{
// Keep this in a static variable to make it accessible for all the nested activities, lets them manipulate the view
public static SettingsActivityGroup group;
// Need to keep track of the history if you want the back-button to work properly, don't use this if your activities requires a lot of memory.
private ArrayList<View> history;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
// Allocate history
this.history = new ArrayList<View>();
// Set group
group = this;
// Start root (first) activity
Intent myIntent = new Intent(this, SettingsActivity.class); // Change to the first activity of your ActivityGroup
myIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
ReplaceView("SettingsActivity", myIntent);
}
/*
* Replace the activity with a new activity and add previous one to history
*/
public void ReplaceView(String pId, Intent pIntent)
{
Window window = getLocalActivityManager().startActivity(pId, pIntent);
View view = (window != null) ? window.getDecorView() : null;
// Add the old activity to the history
history.add(view);
// Set content view to new activity
setContentView(view);
}
/*
* Go back from previous activity or close application if there is no previous activity
*/
public void back()
{
if(history.size() > 1)
{
// Remove previous activity from history
history.remove(history.size()-1);
// Go to activity
View view = history.get(history.size() - 1);
Activity activity = (Activity) view.getContext();
// "Hack" used to determine when going back from a previous activity
// This is not necessary, if you don't need to redraw an activity when going back
activity.onWindowFocusChanged(true);
// Set content view to new activity
setContentView(view);
}
else
{
// Close the application
finish();
}
}
/*
* Overwrite the back button
*/
#Override
public void onBackPressed()
{
// Go one back, if the history is not empty
// If history is empty, close the application
SettingsActivityGroup.group.back();
return;
}
}
Arbitrary child of SettingsActivityGroup(CallForwardActivity)
public class CallForwardActivity extends ListActivity
{
....
#Override
public void onBackPressed()
{
// Go one back, if the history is not empty
// If history is empty, close the application
SettingsActivityGroup.group.back();
return;
}
}
Because I believe calling onBackPressed() of the currently selected activity is the desired behavior.
It's also worth noting that ActivityGroup is deprecated, but I assume you are coding for <3.0 and don't fancy working with the support libraries.
Regarding your edited question:
Another question on this site cites this article as a good ActivityGroup example, and I would agree http://ericharlow.blogspot.com/2010/09/experience-multiple-android-activities.html
This example just calls finish() on the current activity when back is pressed, and lets the os restart the previous activity, which is simpler than what you are doing, and will hopefully work! You can just call getParent() in your child activities too to avoid using that static reference (just seems easier to read to me that way!).
Related
On pressing back button from child activity parent activity displays for a second and refreshes itself.
In child activity I have this code in java file
#Override
public void onBackPressed()
{
Intent moveback =
new Intent(ClassActivityEdit.this, ClassActivity.class);
startActivity(moveback);
finish();
}
ClassActivityEdit is child class. In manifest file code is as follows
<activity android:name=".ClassActivity"
android:label="Class Activity">
<intent-filter>
<action android:name="com.teamtreehouse.oslist.ClassActivity" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity android:name=".ClassActivityEdit"
android:label="Class Activity"
android:noHistory="true">
<intent-filter>
<action android:name="com.teamtreehouse.oslist.ClassActivityEdit" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
On back button I just want the ClassActivity layout to be displayed without it being refreshed.
Edit:
ClassActivityEdit doesnt extend ClassActivity. Its just that some button press in ClassActivity will result in opening ClassActivityEdit.
Edit2
the below code in ClassActivity starts ClassActivityEdit
public void editListener(View v) {
Intent addNewClass =
new Intent(ClassActivity.this, ClassActivityEdit.class);
RelativeLayout buttonTableRow = (RelativeLayout) v.getParent();
TextView getCourseID = (TextView) buttonTableRow.findViewById(R.id.courseNumberActivity);
String courseIDString = getCourseID.getText().toString();
Bundle bundle = new Bundle();
//Add your data to bundle
bundle.putString("CourseIDString", courseIDString);
addNewClass.putExtras(bundle);
startActivity(addNewClass);
}
Edit 3: I also have a Landing (MAIN) activity which flashes for a second. On pressing back button from ClassActivityEdit activity Landing activity flashes again and then the ClassActivity activity loads. Finding it a bit tricky to solve.
public class LoadingPage extends ActionBarActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.loading_page);
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
Intent mainIntent = new Intent(LoadingPage.this, ClassActivity.class);
startActivity(mainIntent);
}
}, 1000);
}
}
Your Problem is so much strange ! just by not overriding OnBackPressed you will get the result you want.
I guess the problem is in your manifest.
android:noHistory="true"
It doesn't let the activity to stay in history stack, try removing it. also try this:
android:launchMode="singleInstance"
By doing this, the activity wont become created again.
The problem is with your override to the back press (which does not need to be overridden) and is likely a symptom of with what your activity does when focus returns to it.
Firstly if you don't override onBackPress the previous activity will load (that's the default behaviour because of the backstack) so you don't need to manually call it with a new intent and tell manually tell it to go to the previous activity.
When your parent activity (ClassActivity) then starts again it will go through the normal lifecycle - it will get resumed and all saved instance states get restored. Since you haven't posted it you need to make sure onResume and onRestart are not doing anything in your parent activity such as loading or setting data.
If you do keep the onBackPress you wrote then it will create a new instance and onCreate will always be called, unless it is singleInstance flagged, in which case onNewIntent will be called, but neither of these things seem to be what you want.
In response to your Edit 3 you need to make sure that LoadingPage has android:noHistory="true" so that it is not available to the backstack and then finish it explicitly to clean it up when you start the main class
That is a simple as calling finish when starting your intent
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
Intent mainIntent = new Intent(LoadingPage.this, ClassActivity.class);
startActivity(mainIntent);
finish(); //end the wrapping activity (LoadingPage)
}
}, 1000);
The last thing you should be aware of is the difference between up and back navigation. In case you are navigating back via the home/up button you should also tell the manifest that these two activities are related. In the manifest entry for ClassActivityEdit you should add android:parentActivityName="com.teamtreehouse.oslist.ClassActivity"
Just let the back button do what it normally does. Don't override it and don't start a new activity. The parent activity is below the child activity in the stack, so it should appear when the child activity finishes.
By default, Android will retain activities in a stack, so when you go from Activity A to Activity B, Activity A will be brought back when you finish Activity B unless you do some other stuff like mess with the launchMode, finish A on returning from B, etc.
From what can be seen in your code, your problem should be solved by not overriding onBackPressed() in your child activity. Also, you should remove your <intent-filter> block for the child activity in the manifest.
I recommend reading up on managing the activity lifecycle and tasks and back stack.
First of all there is no need of overriding onBackPressed(). As Doug mentioned the parent activity is below the child activity in the stack , so it would be visible just after the child activity finishes on back pressed.
There is a suggestion for you as the ActionBarActivity is deprecated so it would be better to extend the class AppCompatActivity in your activity class.
I have tried to replicate the issue but failed. So I have created a classes.
LandingPage.java
public class LandingPage extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.loading_page);
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
startActivity(new Intent(LandingPage.this, ClassActivity.class));
}
}, 1000);
}
}
ClassActivity.java
public class ClassActivity extends AppCompatActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.class_activity);
//Do whatever you want to do
}
public void editListener(View v)
{
Intent addNewClass = new Intent(ClassActivity.this,ClassActivityEdit.class);
RelativeLayout buttonTableRow = (RelativeLayout) v.getParent();
EditText getCourseID = (EditText) buttonTableRow.findViewById(R.id.courseNumberActivity);
String courseIDString = getCourseID.getText().toString();
addNewClass.putExtra("CourseIDString", courseIDString);
startActivity(addNewClass);
}
}
ChildActivityEdit.java
public class ClassActivityEdit extends AppCompatActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.class_activity_edit);
String course_no = getIntent().getStringExtra("CourseIDString");
Toast.makeText(ClassActivityEdit.this,course_no,Toast.LENGTH_SHORT).show();
//Do whatever you want to do
}
}
Your main problem is that you have overridden onBackPressed in the child activity.
What I understand from your code is-
ClassActivity --> Parent Activity
ClassActivityEdit --> Child Activity
In manifest, android:noHistory="true" is specified for child i.e. ClassActivityEdit activity. So it won't be stored in stack. Nothing is mentioned for parent.
In onBackPressed of your child activity you are again creating new Parent activity even though it is already present in the stack. It's just below your child activity.
Even if you don't override onBackPressed, parent activity will still load. In your case, it is creating many instances of parent activity.
One more thing, unless you want your activity to handle or respond to specific actions, you don't need to add intent filters for activity in manifest. According to documentation,
"Intent filter Specifies the types of intents that an activity, service, or broadcast receiver can respond to. An intent filter declares the capabilities of its parent component — what an activity or service can do and what types of broadcasts a receiver can handle. It opens the component to receiving intents of the advertised type, while filtering out those that are not meaningful for the component."
you can using Intent easily move for back Activity. but your back activity refresh all date write in onresum() method then it esyly refresh data
In child activity I have this code in java file
#Override
public void onBackPressed()
{
Intent moveback =
new Intent(ClassActivityEdit.this, ClassActivity.class);
startActivity(moveback);
finish();
}
When you start child activity from parent activity your parent activity move to stack and child activity display in onBackPressed event on child activity you create a new instance of parent activity and in activity life cycle onCreate event called first and views inside it creating by default values, so when you click on back button in child activity without creating a new instance of parent activity android display parent activity with previous savedInstanceState then onResume event called.
for more details see Activity Life Cycle
Change your onBackPressed() in child activity to this or you can also remove the onBackPressed() from child activity
#Override
public void onBackPressed(){
super.onBackPressed(); // call super back pressed method
}
Make sure that you don't finish() parent activity when you move from parent activity to child activity
Try using FLAG_ACTIVITY_REORDER_TO_FRONT when starting the ClassActivity intent.
you said,
On pressing back button from ClassActivityEdit activity Landing activity flashes again and then the ClassActivity activity loads.
you might be calling finish() somewhere in the ClassActivity which will cause the activity to be removed from the stack. If so, remove it.
#Override
public void onBackPressed(){
finish();
super.onBackPressed();
}
this will solve your issue.
finish() will destroy your child activity and super.onBackPressed() will redirect you to parent activity.
You need to replace
startActivity(addNewClass);
with
startActivityForResult(addNewClass,101);
and then, in OnActivityResult you can refresh your data.
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if(requestCode==101){
if(resultCode==RESULT_CANCELED){
//refresh data accordingly
}else if(resultCode==RESULT_OK){
//refresh data accordingly
}
}
}
So I have an Activity A and an Activity B. I want Activity A to be able to navigate to Activity B with the press of a button. That works, but when I use the up navigation(the home button in the action bar) to navigate back to Activity A, onCreate() is called again and the old information that the user typed in is lost.
I've seen: onCreate always called if navigating back with intent, but they used Fragments, and I'm hoping not to have to redesign the entire app to use fragments. Is there any way I can stop onCreate() from being called every time Activity A becomes active again?
This behavior is totally fine and wanted.
The system might decide to stop Activities which are in background to free some memory.
The same thing happens, when e.g. rotating the device.
Normally you save your instance state (like entered text and stuff) to a bundle and fetch these values from the bundle when the Activity is recreated.
Here is some standard code I use:
private EditText mSomeUserInput;
private int mSomeExampleField;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// TODO inflate layout and stuff
mSomeUserInput = (EditText) findViewById(R.id.some_view_id);
if (savedInstanceState == null) {
// TODO instanciate default values
mSomeExampleField = 42;
} else {
// TODO read instance state from savedInstanceState
// and set values to views and private fields
mSomeUserInput.setText(savedInstanceState.getString("mSomeUserInput"));
mSomeExampleField = savedInstanceState.getInt("mSomeExampleField");
}
}
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
// TODO save your instance to outState
outState.putString("mSomeUserInput", mSomeUserInput.getText().toString());
outState.putInt("mSomeExampleField", mSomeExampleField);
}
You can make the up button behave like pressing back, by overriding onSupportNavigateUp()
#Override
public boolean onSupportNavigateUp() {
onBackPressed();
return true;
}
If you want to navigate from child to parent without recreating the parent (calling onCreate method), you may set the android:launchMode="singleTop" attribute for the parent activity in your AndroidManifest.xml
How to switch layouts? First, I have a class Main where is onCreate (setContentView(R.layout.main);) and then I call, another class with command:
setContentView(secondClass);
In this class, I draw with Canvas and this work just fine. I also create button to go back in first "class" (R.layout.main), but I don't know how to do it.
Now my program is basic a graph shower. In first class you type your function and them second class draw it. But how to go back in first class to type another function. This "back" button or arrow witch every Android phone have, send me out of program not back on insert part.
In secondClass I can't create onCreate method, but I also tried the following and they didn't work:
Intent abc = new Intent("bla.bla.bla.FIRSTCLASS");
startActivity(abc);
and
Intent abc = new Intent(SecondClass.this,FirstClass.class);
startActivity(greNaPrvoOkno);
If you want to use a custom view (as I understood, you are extending the View class), you can do it in the following way;
Consider you are showing the second class from your Main activity like this;
setContentView(new SecondClass(getApplicationContext(), MainActivity.this));
And you Second class is this (suppose);
// I am using onClickListener to go back to main view. You do whatever you like.
public class SecondClass extends View implements OnClickListener {
// This is needed to switch back to the parent activity
private Activity mParentActivity = null;
public SecondClass(Context context, Activity parentActivity) {
super(context);
mParentActivity = parentActivity;
setOnClickListener(this);
}
#Override
public void onClick(View v) {
// Set the Main view back here.
mParentActivity.setContentView(R.layout.main);
}
}
Disclaimer: This code will do what you have asked for, but may cause other problems.
As advised by #Mudassir, you should use two different activities for two screens. It will give you better control, and your code will be easy to understand and maintain.
On the Onclick event of the button you have to write finish(); that's it..
Both of your classes are Activities yes? IF so then in your second activity you will simply call finish() and your activity will close revealing your first activity again.
When I have used multiple intents in my android application, I have created a new activity through:
Intent abc = new Intent(this, SecondClass.class);
startActivity(abc);
When the button is pressed in your second class, I would then either call finish(); on the class, or create a new intent like so:
Intent abc = new Intent(this, FirstClass.class);
startActivity(abc);
However, this method has the disadvantage that if a user wanted to use the back button, they may have to scroll through many layers of activities.
You should create another activity for your second class but not just set the main activity to a new view setContentView(secondClass).
For an easier modification, You could try to set the view back to setContentView(R.layout.main) first.
You still need to configure the widgets(e.g. TextView) on it when you set it back.
You don't have to startActivity again to go back.
Just call finish() in your second activity when you want to finish the current activity and go back:
e.g. When user press the back button in your second activity
mButtonBack.setOnClickListener(new Button.OnClickListener()
{
public void onClick(View v)
{
finish();
}
}
I have a fragment in an activity that I am using as a navigation drawer. It contains buttons that when clicked start new activities (startActivity from a fragment simply calls startActivity on the current activity).
For the life of me I can't seem to figure out how I would finish the current activity after starting a new one.
I am looking to achieve something like this in the fragment:
#Override
public void onClick(View view) {
// TODO Auto-generated method stub
if (view == mButtonShows) {
Intent intent = new Intent(view.getContext(), MyNewActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
finish();
}
}
But it seems Fragment.class does not implement finish() (like it implements startActivity(...)).
I would like the activity backstack cleared when they launch the 2nd activity. (so pressing back from the new activity would technically drop them back to the launcher)
When working with fragments, instead of using this or refering to the context, always use getActivity(). You should call
Java
getActivity().finish();
Kotlin
activity.finish()
to finish your activity from fragment.
Well actually...
I wouldn't have the Fragment try to finish the Activity. That places too much authority on the Fragment in my opinion. Instead, I would use the guide here: http://developer.android.com/training/basics/fragments/communicating.html
Have the Fragment define an interface which the Activity must implement. Make a call up to the Activity, then let the Activity decide what to do with the information. If the activity wishes to finish itself, then it can.
As mentioned by Jon F Hancock, this is how a fragment can 'close' the activity by suggesting the activity to close. This makes the fragment portable as is the reason for them. If you use it in a different activity, you might not want to close the activity.
Code below is a snippet from an activity and fragment which has a save and cancel button.
PlayerActivity
public class PlayerActivity extends Activity
implements PlayerInfo.PlayerAddListener {
public void onPlayerCancel() {
// Decide if its suitable to close the activity,
//e.g. is an edit being done in one of the other fragments?
finish();
}
}
PlayerInfoFragment, which contains an interface which the calling activity needs to implement.
public class PlayerInfoFragment extends Fragment {
private PlayerAddListener callback; // implemented in the Activity
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
callback= (PlayerAddListener) activity;
}
public interface PlayerAddListener {
public void onPlayerSave(Player p); // not shown in impl above
public void onPlayerCancel();
}
public void btnCancel(View v) {
callback.onPlayerCancel(); // the activity's implementation
}
}
You should use getActivity() method in order to finish the activity from the fragment.
getActivity().finish();
This does not need assertion, Latest update in fragment in android JetPack
requireActivity().finish();
In Fragment use getActivity.finishAffinity()
getActivity().finishAffinity();
It will remove all the fragment which pushed by the current activity from the Stack with the Activity too...
Every time I use finish to close the fragment, the entire activity closes. According to the docs, fragments should remain as long as the parent activity remains.
Instead, I found that I can change views back the the parent activity by using this statement:
setContentView(R.layout.activity_main);
This returns me back to the parent activity.
I hope that this helps someone else who may be looking for this.
Very simple...
1- just grab activity by getActivity() in the fragment
2- then call finish();
So just getActivity().finish(); will finish the parent activity.
Try this. There shouldn't be any warning...
Activity thisActivity = getActivity();
if (thisActivity != null) {
startActivity(new Intent(thisActivity, yourActivity.class)); // if needed
thisActivity.finish();
}
You have two options for Java and Kotlin. However, logic of both ways are same. You should call activity after call finish() method.
Answer for Kotlin,
If your activity cannot be null, use Answer_1. However, if your activity can be null, use Answer_2.
Answer_1: activity!!.finish()
Answer_2: activity?.finish()
Answer for Java,
getActivity().finish();
To finish activity in a Fragment use:
getActivity().finish();
Simple solution:
activity?.finish()
yes Fragment.class does not implement finish()
When working with fragments, instead of using this or refering to the context, always use getActivity(). You should call
I have a tabhost on my application and I'm using an Activity group which handles 3 activities inside.
Example:
ActivityGroup Handles
A -> B -> C
When i start this activities i'm using the flag Intent.FLAG_ACTIVITY_CLEAR_TOP.
My problem is when the user goes from A->B->C and press back button, my B activity shows up, but it does not resume or reload or refresh. It has the same state as before.
For example if the user goes again to C, C is refreshed, but when from C goes back.... B is not.
On B I have implementend methods such as onResume, onStart, onReestart and debugging it the main thread never goes in there...
And i need to refresh B because C can make changes that change the content displayed on B.
I have googleled this for 3 days and I couldn't found a solution..
I had this problem too.
I was using ActivityGroup code based on this blog post.
When I pressed the back button the pervious View would load fine, but the activity associated with it would not fire the onResume().
I was using an extended activity with on overridden and public onResume().
I found this blog post, so tried casting the view as my extended activity and called onResume().
Bingo.
Edit.... here's some more detail...
public class YFIMenuListActivity extends ListActivity {
....
#Override
public void onResume() {
super.onResume();
}
....
}
onResume() is normally protected, but I override it and make it public so that my ActivityGroup can call it.
I only have extended list activities in this activity group (I was just playing around). If you have different activities, each will have to override onResume() and I guess you'd have to look at the type of context you got back from v.getContext() before casting and calling it.
My ActivityGroup looks something like this:
public class BrowseGroup extends ActivityGroup {
.....
#Override
protected void onResume() {
super.onResume();
// call current activity's onResume()
View v = history.get(history.size()-1);
YFIMenuListActivity currentActivity = (YFIMenuListActivity)v.getContext();
currentActivity.onResume();
}
....
}
I've managed to implement an expanded version of cousin_itt's approach.
In both of my activities being used within the activity group I changed onResume from :
protected void onResume()
to
public void onResume()
I then wrote the following onResume function in my ActivityGroup to manually fire off onResumes:
#Override
protected void onResume() {
super.onResume();
View v = history.get(history.size()-1);
MainPeopleView currentActivity = null;
try {
currentActivity = (MainPeopleView)v.getContext();
currentActivity.onResume();
}
catch ( ClassCastException e ) {
Log.e(TAG, e.toString());
}
ProfileView otherActivity = null;
try {
otherActivity = (ProfileView)v.getContext();
otherActivity.onResume();
}
catch ( ClassCastException e ) {
Log.e(TAG, e.toString());
}
}
I have to say, this feels like the worst android hack I've ever written. I'm never using activitygroup again.
((ReportActivity)getLocalActivityManager().getActivity("ReportActivity")).onResume();
ReportActivity is that name you want to back Activity
ps: v.getContext();
only return the ActivityGroup ,it can't invoke child Activity onResume
I have found that onFocusChanged(boolean hasFocus) is great for situations like ActivityGroup. This will fire, even if onResume() does not. I use it for a few of my apps that have TabHosts and ActivityGroups. Here you can force the refresh and insure that it always gets fired when your Activity regains the focus.
I hope you have write your refresh data code in this method onResume().