I would like to have an appwide rule to not allow the landscape orientation. I realize I could get this by putting:
android:screenOrientation="portrait"
in every one of my activities but that doesn't seem clean. I can also put
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
in every one of my activity's onCreate() methods.
But I desire a solution that doesn't require this code duplication. Any suggestions?
Solution:
You can do this for your entire application without having to make all your activities extend a common base class.
The trick is first to make sure you include an Application subclass in your project. In its onCreate(), called when your app first starts up, you register an ActivityLifecycleCallbacks object (API level 14+) to receive notifications of activity lifecycle events.
This gives you the opportunity to execute your own code whenever any activity in your app is started (or stopped, or resumed, or whatever). At this point you can call setRequestedOrientation() on the newly created activity.
class MyApp extends Application {
#Override
public void onCreate() {
super.onCreate();
// register to be informed of activities starting up
registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
#Override
public void onActivityCreated(Activity activity,
Bundle savedInstanceState) {
// new activity created; force its orientation to portrait
activity.setRequestedOrientation(
ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}
....
});
}
}
Any doubts, Please leave a comment.
Note: Add in manifest's <application> tag: android:name=".MyApp"
Hope it helps.
Related
I have seen a lot of questions and answers about recreating the current activity after changing the application's Night Mode, but I have seen nothing on how to refresh the back stack Activities.
Say I have the backstack A > B > C. Activity C allows to change the night mode by calling AppCompatDelegate.setDefaultNightMode(). After this call, the current Activity (C), can refresh its theme with delegate.applyDayNight() or recreate().
However, when the user navigates back to B or A, the activities are still using the "old" mode, either day or night.
I tried to add something like that to the Activities:
override fun onResume() {
super.onResume()
delegate.applyDayNight()
}
But it does not seem to work.
I did multiple attempts to fix this:
One idea would be to recreate the backstack completely like suggested here or here, but since the backstack is not static, it's not doable for me.
Another idea would be to have a class that handles the night mode change and provides a LiveData. Each Activity would listen to the LiveData for a mode change and call recreate(). However, we are stuck in an infinite loop because the Activity would recreate directly after starting to listen to the LiveData.
I find it hard to believe that I am the first one trying to refresh the Activities from the backstack after changing the night mode. What did I miss?
Thanks!
If you can detect when the day/night mode has changed, you can simply recreate an activity that is resumed when the back stack is popped.
In the following demo, there are three activities: A, B and C. A creates B and B creates C. Activity C can change the day/night mode. When C is popped, activity B sees the change in the day/night mode and calls reCreate() to recreate the activity. The same happens in activity A when activity B is popped.
The video below shows the effect. The light-colored background is the "day" mode and the dark is "night" mode.
I have created a GitHub project for this demo app. If this works as a solution, I can incorporate more text into the answer from the project.
Refreshing your back stack completely is probably overkill and may add some overhead/lag to the UX; and as you mentioned, most applications will not have access to a full, static back stack.
You are essentially describing a more general issue: global changes to the theme or WindowManager itself affect the subsequent drawing of views. But previous layouts for Activities in the stack may not be redrawn. It might seem odd for you in this situation, but there could also be many good reasons why one would not want to redraw an Activity in the stack if once the user goes back to it. And so this is not an automatic feature.
I can think of a couple of options:
1) Write a custom class inheriting from Activity that invalidates all it's views when it moves to the front of the stack again. E.g. in onResume() or onRestart(), call (if in Fragment)
View view = getActivity().findViewById(R.id.viewid);
view.invalidate();
Use this custom Activity for all your activities that you want to keep consistent with the current day/night mode.
2) Use ActivityLifecycleCallbacks. This helps keep all your logic in one place, and avoids the need for custom inheritance as above. You could invalidate your views here as needed as activities are paused/resumed. You could include a Listener, if it is your app that is changing the theme, and record as SharedPreference, for example.
To use, add the callbacks to your Application class:
registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
#Override
public void
onActivityCreated(Activity activity, Bundle savedInstanceState) {
//can check type of Activity for custom behaviour, if using inheritance
if(activity instanceof MainActivity) {
mMainActivities.put(activity, new MainActivityEntry((MainActivity)activity));
//...
}
}
#Override
public void
onActivityDestroyed(Activity activity) {
}
#Override
public void
onActivityPaused(Activity activity) {
}
#Override
public void
onActivityResumed(Activity activity) {
if(activity instanceof MainActivity) {
//...
}
//can update Entry properties too
final MainActivityEntry activityEntry = mMainActivities.get(activity);
if(activityEntry != null) {
//record state /perform action
}
}
#Override
public void
onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
#Override
public void
onActivityStarted(Activity activity) {
}
#Override
public void
onActivityStopped(Activity activity) {
}
});
Quick answer:
#Override
protected void onRestart() {
super.onRestart();
recreate();
}
You add the above codes to your MainActivity and it will work.
create a static boolean variable in the project and in each activity check if the boolean is true or false, then apply daylight and night based on value.
I'm creating my first library to use in some future projects, on it I'm creating some Activities that are the same on every project.
Right now I'm working with a test project and on my library I have this LoginActivity. It has it's own layout and works fine.
How can I make my test app LoginActivity be the one from the library? At the moment I'm extending my LoginActivity from the library into my activity on the project. Is this the right way of doing it considering that all the code logic happens on the Library activity?
Library LoginActivity
public class LoginActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
// Some useless code...
}
}
Project Login Activity
public class MainActivity extends LoginActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Nothing happens in this class... really...
}
}
Make sure that Activity that is inside of the library project is declared inside of the library's manifest file. Then start the activity how you normally would start any other activity. If you want the activity to be the first activity when the app launches (launcher activity), you need to declare it explicitly inside of your manifest and add an intent-filter tag element as a child, that indicates it should be the launcher activity. View this post.. Another way to roughly achieve the same effect would be to create a blank activity that is declared in the app manifest file as the launcher activity and inside of the onCreate method launch the library activity. Make sure you clear any and all activity transitions so that the switch to the library activity is seamless. The benefit to this approach is that you can perform a check before launching the library activity.
Ex. checking if the login activity needs to be displayed or if the screen can be by-passed and the app can be entered immediately.
Add login activity into AndroidManifest.xml in library or application.
<application>
<activity
android:name="com.mypackage.LoginActivity" />
</application>
I'm looking for a single answer (but I might be asking the wrong question)
Question- does any event only get called once TOTAL until an activity is destroyed?
I ask because when my user rotates the phone to landscape oncreate and onstart are both invoked causing a reload of sorts.
I'm looking for an event that I could put behavior into that would only get run 1x (until the activity is killed)
Thank you in advance
If it is specific to the Activity just check your savedInstanceState parameter in the onCreate event. If it is null, run your code, if not, your code has already been run.
Example:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
if(savedInstanceState == null) {
// Run your code
}
}
savedInstanceState will always be null when onCreate is run for the first time, and it will be populated thereafter.
You don't really specify what you're trying to do with it, so I can't guarantee this is appropriate for your use, but Application.onCreate is only called once.
If you want to eliminate the recreation of your activity on an orientationchange you can listen for configchanges in the manifest.
<activity
android:name=".MyActivity"
android:configChanges="orientation" >
</activity>
And then you can override onConfigurationChanged like so:
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged( newConfig );
LinearLayout main = (LinearLayout) findViewById( R.id.mainLayout );
main.requestLayout();
}
to recreate the layout so that it matches the new orientation, without recreating the entire activity.
Check http://developer.android.com/guide/topics/resources/runtime-changes.html to handle configuration changes and to maintain your huge data between them...if all you need to maintain between the configuration change is just the settings,you can use the onSavedInstanceState() and onRestoreInstanceState() callbacks and the given bundles.
I have a simple activity that loads a bitmap in onCreate. I find that if I rotate the device I can see from the logs that onCreate called again. In fact, because all instance variables are set to default values again I know that the entire Activity has been re-instantiated.
After rotating 2 times I get an FC because not enough memory can be allocated for the bitmap. (Are all instances of the activty still alive somewhere? Or does the GC not clean up fast enough?)
#Override
public void onCreate(Bundle savedInstanceState) {
File externalStorageDir = Environment.getExternalStorageDirectory();
File picturesDir = new File(externalStorageDir, "DCIM/Camera");
File[] files = picturesDir.listFiles(new FilenameFilter(){
public boolean accept(File dir, String name) {
return name.toLowerCase().endsWith(".jpg");
}});
if (files.length > 0) {
Bitmap bm = BitmapFactory.decodeStream(new FileInputStream(files[0]));
ImageView view = (ImageView) findViewById(R.id.photo);
view.setImageBitmap(bm);
}
}
From all that I read, onCreate should be called once during the lifetime of an application. Am I wrong about this? How can re-orienting the device cause the activity to be recreated?
android:configChanges="keyboardHidden|orientation|screenSize"
Caution: Beginning with Android 3.2 (API level 13), the "screen size"
also changes when the device switches between portrait and landscape
orientation. Thus, if you want to prevent runtime restarts due to
orientation change when developing for API level 13 or higher (as
declared by the minSdkVersion and targetSdkVersion attributes), you
must include the "screenSize" value in addition to the "orientation"
value. That is, you must decalare
android:configChanges="orientation|screenSize". However, if your
application targets API level 12 or lower, then your activity always
handles this configuration change itself (this configuration change
does not restart your activity, even when running on an Android 3.2 or
higher device).
From docs: http://developer.android.com/guide/topics/resources/runtime-changes.html
What happen when orientation changed
Life Cycle of orientation
onPause();
onSaveInstanceState();
onStop();
onDestroy();
onCreate();
onStart();
onResume();
---- app recreated and now is running ---
If you do long operation in onCreate() and want prevent re-create your activity add configChanges attribute in your mainfest
<activity android:name=".MyActivity"
android:configChanges="orientation|keyboardHidden|screenSize"
android:label="#string/app_name">
screenSize if you targeting api >= 13
Activity is recreated after each rotation by default. You can override this behaviour with configChanges attribute of the activity tag in AndroidManifest. For further details and different options, see http://developer.android.com/guide/topics/resources/runtime-changes.html
Actvity Lifecycle when you rotate screen
onPause
onSaveInstanceState
onStop
onDestroy
onCreate
onStart
onRestoreInstanceState
onResume
If you want to prevent FC from not enough memory, you need to deallocate resources in onStop() or onPause(). this allows you to use fresh memory in onCreate().
This is an alternate solution to preventing the recreation of the activity by using
android:configChanges="keyboardHidden|orientation"
As sometimes your activity's layout is different in portrait and landscape (layout, layout-land).
preventing recreate on orientation change will prevent your activity from using the other orientation's layout.
Yes, activity's onCreate() is called everytime when the orientation changes but you can avoid the re-creation of Activity by adding configChanges attribute of Activity in your AndroidManifest file in the activity tag.
android:configChanges="keyboardHidden|orientation"
On Create method will call everytime when you do orientation, to avoid this you have to use
//Define Below in you Manifest file.
<activity
android:name="com.ecordia.activities.evidence.MediaAttachmentView"
android:configChanges="keyboardHidden|orientation|screenSize"
</activity>
//Define Below in your activity.
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
//your code
} else if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
//your code
}
}
It will works like a charm!!
Manifest XML activity Tag:
android:configChanges="keyboardHidden|orientation"
#Override
public void onConfigurationChanged(Configuration newConfig) {
// TODO Auto-generated method stub
super.onConfigurationChanged(newConfig);
}
Use the above code to perform changes related to orientation in your Activity Java Code
Cheers!!!
One of the most common and suggested “solutions” to dealing with orientation changes is to not deal with them. You can do this by setting the android:configChanges flag on your Activity in AndroidManifest.xml as shown below:
<activity
android:name=".MyActivity"
android:label="#string/title_my_activity"
android:configChanges="orientation|screenSize|keyboardHidden" />
This is NOT the correct way to deal with orientation changes.
CORRECT way is to implement the onSaveInstanceState method (this could be in your Activity, Fragment or both) and place the values you need to save in the Bundle argument that gets passed to the method.
It is nicely described here: http://code.hootsuite.com/orientation-changes-on-android/
While it may seem a bit tedious to implement, handling orientation changes properly provides you with several benefits: you will be able to easily use alternate layouts in portrait and landscape orientations, and you will be able to handle many exceptional states such as low memory situations and interruptions from incoming phone calls without any extra code.
While the Manifest way may work, there is a better and proper solution for these types of problems. The ViewModel class. You should have a look here: https://developer.android.com/topic/libraries/architecture/viewmodel
Basically, you extend the ViewModel class and define all the data members in it which we want to be unchanged over re creation of the activity (in this case orientation change). And provide relevant methods to access those from the Activity class. So when the Activity is re created, the ViewModel object is still there, and so are our data!
Kindly see my way of doing it:-
http://animeshrivastava.blogspot.in/2017/08/activity-lifecycle-oncreate-beating_3.html
snippet is:-
#Override
protected void onSaveInstanceState(Bundle b) {
super.onSaveInstanceState(b);
String str="Screen Change="+String.valueOf(screenChange)+"....";
Toast.makeText(ctx,str+"You are changing orientation...",Toast.LENGTH_SHORT).show();
screenChange=true;
}
#Override
public void onCreate(Bundle b) {
super.onCreate(b);
ctx=getApplicationContext();
if(!screenChange) {
String str="Screen Change="+String.valueOf(screenChange);
// ...
}
}
I had the same problem, in which my onCreate is called multiple times when the screen orientation is changed. My problem got solved when i add android:configChanges="orientation|keyboardHidden|screenSize" in the activity tag in manifest
I had the same problem and I did some workaround
Define didLoad boolean variable with false value
private boolean didLoad = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity);
if (!this.didLoad){
// Your code...
this.didLoad = true;
}
I want to go to homePage of my application on clicking button where i was in inner page.
Can any one tell me how to do that ?
Thanking you ,
Srinivas
I suggest creating an Application class. In that class have a boolean field that is false by default. Every Activity should check if that field is true in onResume() and call finish() if it is true (except for the main activity that always sets the field to false in onResume()). You can even create a custom Activity that does this and then have all activities extend that activity.
Resources:
http://d.android.com/resources/faq/framework.html#3
The android.app.Application class
The android.app.Application is a base class for those who need to maintain global application state. It can be accessed via getApplication() from any Activity or Service. It has a couple of life-cycle methods and will be instantiated by Android automatically if your register it in AndroidManifest.xml.
Reference: http://d.android.com/reference/android/app/Application.html
Stripped-down example:
MyApp
public class MyApp extends Application { public boolean goBack = false; }
MyActivity
public class MyActivity extends Activity {
protected void onResume() {
if ( ((MyApp) getApplication()).goBack ) finish();
}
}
SomeActivity
public class SomeActivity extends MyActivity {
// nothing special here, it's all been implemented in MyActivity!
}
MainActivity
public class MainActivity extends Activity {
protected void onResume() {
((MyApp) getApplication()).goBack = false;
}
}
AndroidManifest.xml
[...] <application android:name=".MyApp" [...]> [...]
Note: You don't need to declare MyActivity in AndroidManifest.xml because it will never be launched directly (it will only be extended).
On your button click event add the following lines of code
moveTaskToBack(true);
There are probably two ways of doing that (general concepts):
The first one would be to simply launch the home actvity again, if you don't mind having it re-created again, unless that activity is a "singleTask" or "singleInstance" activity.
The second one would be to close the top activity in the stack as long as it is not your home activity. I don't see an easy way to achieve that, maybe by finishing the current activity with a specific result that gets checked by the launching activity, who will in turn close and send the result until the home activity is reached.