Android - how to re-inflate View hierarchy - android

I'm having problems making my Activity respond to a theme change (i.e. Theme_Dark change to Theme_Light).
In the Activity this code below works fine, and the theme is changed when the Activity is created (method getPreferenceTheme() just gets the theme preference value that was set via a PreferenceActivity).
protected void onCreate(Bundle savedInstanceState) {
setTheme(getPreferenceTheme());
super.onCreate(savedInstanceState);
setContentView(R.layout.controls);
}
But how can I dynamically change the theme ? So after I change the theme in a PreferenceActivity and return to the main Activity how can I get it to change?
I know that I can re-start the Activity to do this (calling onCreate() again), but I didn't want to do this and have heard that it is possible to "re-inflate the view hierarchy" in onResume() - how do I do this ?
I tried the following (a stab in the dark) but with no joy.
protected void onResume() {
super.onResume();
LayoutInflater inflater = LayoutInflater.from (this);
View v = inflater.inflate (R.layout.controls, null);
setTheme(getPreferenceTheme());
setContentView(v);
}
Any help much appreciated,
M.

Try the "recreate" Activity method.
http://developer.android.com/reference/android/app/Activity.html#recreate()
Call it after you have set the new theme.

Related

How to save the fields data while changing the Layout on screen rotation

I have read a lot of things about the data save instance and restore but Unable to implement in my case. What is my case in application .
I am using the Activity (MainActivity) and calling the fragment in it let say ParentFragment
ParentFragment Calls the ChildFragment in it though ParentFragment has its own views such as TextViews which takes First name, Last name and age and below this part I am programatically calling the ChildFragment
So in this way I have 2 fragments in the MainActivity, which is being shown to the user at a time on the screen
**What I want **
I want when User has change the orientation the layout should also change but I also want that the text fields should maintain there data in them .
I want to save some more fragment variables and their data also on configuration changes and retrieve them after the new layout is set after screen orientation changed.
**Problems and Confusions **
I have no idea If i set the Fragment.setRetainInstance(true) then would my fragment still be able to receive the onConfiguration Change call back?
When I rotate my device, the fragment gets re-initialize and also my activity has the Asynctask and that runs again , i want my activity to hold the same data . How can I achieve that?
Please help me and give me some hint.
If you want to change layout on orientation change then,you should not handle configuration change by retaining the activity(by setting android:configChanges="orientation" value for corresponding activity defined in manifest) or by using setRetainInstance() in fragment.
For saving the fragment state on configuration change use
#Override
protected void onSaveInstanceState(Bundle outState) {
outState.putInt(FIRST_NAME_VALUE_KEY, firstNameTextView.getText());
outState.putInt(LAST_NAME_VALUE_KEY, lastNameTextView.getText());
super.onSaveInstanceState(outState);
}
and for writing back state to the views in fragment use
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.my_fragment, container, false);
if (savedInstanceState != null) {
String firstName = savedInstanceState.getInt(FIRST_NAME_VALUE_KEY);
String lastName = savedInstanceState.getInt(LAST_NAME_VALUE_KEY);
firstNameTextView.setText(firstName);
lastNameTextView.setText(lastName);
}
return view;
}
You will receive onConfigurationChanged() callback by retaining activity.it is not related to Fragment.setRetainInstance(true).
To avoid running asynctask again,We can store data and retain it using following callbacks inside the activity.
#Override
protected void onSaveInstanceState(Bundle savedInstanceState) {
// Save custom values into the bundle
savedInstanceState.putInt(SOME_VALUE, someIntValue);
savedInstanceState.putString(SOME_OTHER_VALUE, someStringValue);
// Always call the superclass so it can save the view hierarchy state
super.onSaveInstanceState(savedInstanceState);
}
  in onCreate(Bundle savedInstanceState) call back you can check for savedInstanceSate , based on that you can call u asynctask or retain values as below
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState == null) {
asyncTask.execute();
} else {
someIntValue = savedInstanceState.getInt(SOME_VALUE);
someStringValue = savedInstanceState.getString(SOME_OTHER_VALUE);
}
}

MainView gets nullified

Hello I have a Main activity and this mainactivity has a mainview. This mainview is a surfaceview. For rendering the logic to draw on the surface view teh mainview has a mainthread.
So now I have a Problem which I hadn't before: The first App I make in landscape so I set all activities' orientation to landscape.
When the App now gets through onDestroy the Mainview gets null.
And I dont get why!!!
Here the onCretae:
MainView mView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(mView==null)
Log.e("","mainview==nullllll");
if(savedInstanceState==null)
{
mView=new MainView(this);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(mView);
}
}
and here the onDestroy:
#Override
public void onDestroy()
{
super.onDestroy();
if(mView==null)
Log.e("","mainview==nullllll");
}
What can I do to prevent my Mainview to be nullified?
having two mainviews so to make sure it doesnt get garbage collected and refill the nullified one?
I dont want to set up the thread and the view every time.
I wonder why the Activity gets Destroyed on the other hand. I'm just locking the screen and in my other apps(where the Screen orientation is locked to portrait) doesn't behave like that it doesnt get into the destroy method if it is not really destroyed. It should get into the stopped state and wait there to be resumed again and not getting destroyed!
Please help me guys!
I've found the Solution:
I just need to add these Lines into the Manifest(>API13 solution)
android:configChanges="orientation|screenSize"
So the Activity won't be destroyed...

manage screen orientation change android

I have a activity with multiples initializations ( fragments , sharedpreferences , services , ui components(search bar , buttons etc..)...) and i do not want to restart the activity when the screen orientation change
I found a easy ( and working solution ) by using android:configChanges="keyboardHidden|orientation|screenSize" but my technical manager forbade me this way because according to him it's a bad practice
What are the other ways in order to handle properly the screen orientation changes?
Thank you very much
My main activity
#AfterViews
public void afterViews() {
putSharedPrefs();
initializeFragments();
initializeComponents();
initializeSynchronizeDialog();
initDownloadDialog();
}
You can just add layout for landscape mode.
You just need to add folder layout-land in your "res" directory and define layout for what your activity looks like if it is in landscape mode.
NOTE-Keep xml file name same as the name which is in simple layout folder and which you are using in your activity. It will automatically detect your screen orientation and apply layout accordingly.
EDIT1
Now to save your text from the text view =) Lets assume your textview is named as MyTextView in your layout xml file. Your activity will need the following:
private TextView mTextView;
private static final String KEY_TEXT_VALUE = "textValue";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mTextView = (TextView) findViewById(R.id.main);
if (savedInstanceState != null) {
String savedText = savedInstanceState.getString(KEY_TEXT_VALUE);
mTextView.setText(savedText);
}
#Override
protected void onSaveInstanceState (Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString(KEY_TEXT_VALUE, mTextView.getText());
}
Basically, whenever Android destroys and recreates your Activity for orientation change, it calls onSaveInstanceState() before destroying and calls onCreate() after recreating. Whatever you save in the bundle in onSaveInstanceState, you can get back from the onCreate() parameter.
So you want to save the value of the text view in the onSaveInstanceState(), and read it and populate your textview in the onCreate(). If the activity is being created for the first time (not due to rotation change), the savedInstanceState will be null in onCreate(). You also probably don't need the android:freezesText="true"
You can also try saving other variables if you need to, since you'll lose all the variables you stored when the activity is destroyed and recreated.

When to call openDrawer() (or any other animation) to make it visible on startup?

I want my app to show the side navigation drawer as soon as the main activity is created.
My code works fine - user launches app and gets the open drawer - but I'd like to actually see the side drawer sliding from the left; instead, I find the drawer fully opened.
At what point should I call openDrawer()?
Have tried calling from:
main activity OnCreate;
similar points in the fragment hosted by the drawer.
I could try OnPrepareOptionsMenu, but I think it gets called more than once during the activity lifecycle. I also tried OnStart() and I fear my options are over.
Any idea? I'm sure this is pretty simple but I can't figure out.
Edit: I realize I wasn't so clear with my first exposition of the question (#Biu). I'm talking about a purely graphical issue here. The point is:
I have something to happen at startup; in my case we're speaking about the nav drawer sliding into the main screen, but it could be any animation I think;
In my case, one could just call:
protected void OnCreate(Bundle b) {
...
DrawerLayout.openDrawer()
}
The above solution works well. The issue I'm talking about is graphical; with the above code you launch the app and find the main activity covered with an already-opened drawer. Instead, I'd like the user to have clue of what is happing, to see where the panel came from; in other words, to see the opening animation.
So my question is: when should I call openDrawer()? The main Activity onCreate isn't quite right, because the animation ends before the user gets to see something on screen.
I thought that the wish of having something start when all is loaded would be more common.
#benjosantony suggests that you should open your drawer at onResume, however it's not guaranteed that the activity will be visible at that time:
onResume is not the best indicator that your
activity is visible to the user; Use onWindowFocusChanged(boolean) to know for certain
that your activity is visible to the user
You'd think that you can just use onWindowFocusChanged and be done, but you can't. There's still the transition animation which breaks (at least for me) the drawer's animation..
For API 21+:
There's onEnterAnimationComplete where you can open your drawer and see the animation properly. However 21+ is a requirement that's just too big..
For lower APIs:
The only possible way I can think of is removing the activity's animation with a theme adjustment:
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="android:windowAnimationStyle">#null</item>
</style>
And opening the drawer like so:
private static final String DRAWER_STATE = "mDrawerOpened";
private DrawerLayout mDrawer;
private ListView mDrawerList;
private boolean mDrawerOpened;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mDrawer = (DrawerLayout) findViewById(R.id.drawer_layout);
mDrawerList = (ListView) findViewById(R.id.left_drawer);
}
#Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if (!mDrawerOpened && hasFocus) {
mDrawer.openDrawer(mDrawerList);
mDrawerOpened = true;
}
}
#Override
protected void onSaveInstanceState(#NonNull Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean(DRAWER_STATE, mDrawerOpened);
}
#Override
protected void onRestoreInstanceState(#NonNull Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
mDrawerOpened = savedInstanceState.getBoolean(DRAWER_STATE);
}
This will animate the drawer only when the activity is started.
The boolean value is saved when your activity is destroyed abnormally, e.g. rotation or need for system resources.
If you don't like setting the instanceState you can use SharedPreferences as #Biu suggested, however IMO that wouldn't be the proper solution as android already provides tools for that, there's no need to re-invent the bike.
You could use this hack by using SharedPreferences
boolean firstTime = true;
SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this);
public void onCreate(Bundle bundle)
{
if (pref.getBoolean("firstTime", true) == true)
{
drawer.openDrawer(yourDrawer);
firstTime = false;
pref.editor().putBoolean("firstTime", firstTime).apply();
}
}
Activity
The foreground lifetime of an activity happens between a call to onResume() until a corresponding call to onPause(). During this time the activity is in front of all other activities and interacting with the user.
Thus I think onResume is the best place to open your drawer.

Save state of activity when orientation changes android

I have an aacplayer app and I want to save the state of my activity when orientation changes from portrait to landscape. The TextViews do not appear to be empty, I tried to freeze my textview using this:
android:freezesText="true"
my manifest:
android:configChanges="orientation"
I also tried this:
#Override
public void onConfigurationChanged(Configuration newConfig){
super.onConfigurationChanged(newConfig);
setContentView(R.layout.main2);
So when orientation changes to landscape I can see my layout-land main2.xml, that works but my textview goes out and appears empty. Streaming music works great. I can listen to it when orientation changes, but the text inside textviews are gone each time I change the orientation of my device.
What should I do to fix this so I can save the state?
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
....
....
Thank you very much.
When your orientation changes, you don't have to manually change to the landscape layout file. Android does this automatically for you. When orientation changes, Android destroys your current activity and creates a new activity again, this is why you are losing the text.
There are 2 parts you need to do, assuming you want a separate layout for portrait and landscape.
Assuming you have 2 XML layout files for portrait and landscape, put your main.xml layout file in the following folders:
res/layout/main.xml <-- this will be your portrait layout
res/layout-land/main.xml <-- this will be your landscape layout
That's all you need to do, you don't have to touch the manifest file to modify android:configChanges="orientation" or override the onConfigurationChanged(). Actually, it's recommended you do not touch this for what you are trying to achieve.
Now to save your text from the text view =) Lets assume your textview is named as MyTextView in your layout xml file. Your activity will need the following:
private TextView mTextView;
private static final String KEY_TEXT_VALUE = "textValue";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mTextView = (TextView) findViewById(R.id.main);
if (savedInstanceState != null) {
CharSequence savedText = savedInstanceState.getCharSequence(KEY_TEXT_VALUE);
mTextView.setText(savedText);
}
}
#Override
protected void onSaveInstanceState (Bundle outState) {
super.onSaveInstanceState(outState);
outState.putCharSequence(KEY_TEXT_VALUE, mTextView.getText());
}
Basically, whenever Android destroys and recreates your Activity for orientation change, it calls onSaveInstanceState() before destroying and calls onCreate() after recreating. Whatever you save in the bundle in onSaveInstanceState, you can get back from the onCreate() parameter.
So you want to save the value of the text view in the onSaveInstanceState(), and read it and populate your textview in the onCreate(). If the activity is being created for the first time (not due to rotation change), the savedInstanceState will be null in onCreate(). You also probably don't need the android:freezesText="true"
You can also try saving other variables if you need to, since you'll lose all the variables you stored when the activity is destroyed and recreated.
There are two ways of doing this, the first one is in the AndroidManifest.xml file. You can add this to your activity's tag
android:configChanges="keyboardHidden|orientation|screenSize|screenLayout"
Or you can override two methods that will take care of this. This method requires some more effort, but arguably is much better. onSaveInstanceState saves the state of the activity before it's killed, and onRestoreInstanceState restores that information after onStart() Refer to the official documentation for a more in depth look.
In my sample code below, I am saving 2 int values, the current selection from the spinner as well as a radio button.
#Override
public void onSaveInstanceState(#NonNull Bundle savedInstanceState) {
spinPosition = options.getSelectedItemPosition();
savedInstanceState.putInt(Constants.KEY, spinPosition);
savedInstanceState.putInt(Constants.KEY_RADIO, radioPosition);
super.onSaveInstanceState(savedInstanceState);
}
// And I am restoring those values with `getInt`, then I can pass those stored values into the spinner and radio button group to select the same values that we saved earlier.
#Override
public void onRestoreInstanceState(#NotNull Bundle savedInstanceState) {
spinPosition = savedInstanceState.getInt(Constants.KEY);
radioPosition = savedInstanceState.getInt(Constants.KEY_RADIO);
options.setSelection(spinPosition, true);
type.check(radioPosition);
super.onRestoreInstanceState(savedInstanceState);
}
static CharSequence savedText;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if(savedText != null) {
TextView mTextView = (TextView) findViewById(R.id.main);
mTextView.setText(savedText);
}
}
// Another function in activity, when you change text
public void actionButton(View view) {
// Change and save text in textView
savedText = "Change text";
mTextView.setText(savedText);
}
Its work for me.
But I think its not good code style and architecture for android.
I use in KOTLIN static var / val :
class MyFragment : Fragment()
{
//all my code
//access to static vars -> MyStaticClass.hello
}
class MyStaticClass
{
companion object {
var hello: String = "Static text"
var number_static: Int = 0
}
}

Categories

Resources