From what I have researched, Android does not have a practical way to change overall application themes.
My application is somewhat simple, Activity wise, and I think this method of handling theme changes is safe.
I want to know if the method below is a safe, or is this a hack job and there are better ways to implement application wide themes?
Notes:
MainActivtiy.java is the entry point and only Activity besides the SettingsActivty.java
SettingsActivity.java extends the PreferenceActivty to display a typical preference screen. The theme setting is stored in the default shared preference identified by R.string.colorThemeListPrefStr where the android:entryValues are {"0", "1"}
Settings.java is just a class for static variables that are safe while the application is in memory, anything that needs to be saved between session is saved to shared preferences during onPause().
MainActivity.java:
public class MainActivity extends ListActivity implements OnClickListener{
public void onCreate(Bundle savedInstanceState) {
sp = PreferenceManager.getDefaultSharedPreferences(this);
// get the int representing the theme selected from shared preferences
switch (Integer.valueOf(sp.getString(getString(R.string.colorThemeListPrefStr), Settings.DEFAULT_COLOR_THEME_INDEX))) {
case 0:
super.setTheme(android.R.style.Theme_Light);
break;
case 1:
super.setTheme(android.R.style.Theme_Black);
break;
}
super.onCreate(savedInstanceState);
// ...
}
public void onPause() {
super.onPause();
// ...
// store the current theme int
Settings.currentTheme = Integer.valueOf(sp.getString(getString(R.string.colorThemeListPrefStr), Settings.DEFAULT_COLOR_THEME_INDEX));
// ...
}
public void onClick(View v) {
switch (v.getId()) {
case R.id.inputSettingsButton:
startActivityForResult(new Intent(this, SettingsActivity.class), Settings.PREFERENCES_REQUEST_CODE);
break;
// ...
}
}
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case Settings.PREFERENCES_REQUEST_CODE:
// check if the new and old themes are different
if (Settings.currentTheme != Integer.valueOf(sp.getString(getString(R.string.colorThemeListPrefStr), Settings.DEFAULT_COLOR_THEME_INDEX))) {
this.finish();
startActivity(new Intent(this, MainActivity.class));
}
break;
// ...
}
}
}
You should restart your Application using this simple one line of code that comes with the support library.
startActivity(IntentCompat.makeRestartActivityTask(getActivity().getComponentName()));
Related
Ok, so the thing is that i'm trying to get my android application to have a settings activity for the user to be able to change some of the application's features (language, theme, ...). My problem comes when trying to get the app to react to the change on the value of one of those preferences. For instance the theme one; my idea would be to have a "Switch preference". When it would be on, the app's theme would be Material.Light and when off, Material. For this I though to have some "onValueChanged" method that would react when the switch changed its position. The problem here is that I'm unable to properly get an instance of the SwitchPreference in my SettingsActivity, both because the "findPreference(key)" method is deprecated and I don't really know how to make it take the value of the needed key.
There is any way to do this, or should I change the way of thinking for this problem?
Instead of setting the theme in onValue change listener, you can also do like below:
1) Create ThemeUtils class and do switch case for theme selection and set a theme for activity. I created own style, you can select your stlye name here
public class ThemeUtils {
public final static int THEME_Professional = 1;
public final static int THEME_Default = 2;
public static void onActivityCreateSetTheme(Activity activity, int sTheme) {
switch (sTheme)
{
default:
case THEME_Professional:
activity.setTheme(R.style.ProfessionalTheme);
break;
case THEME_Default:
activity.setTheme(R.style.DefaultTheme);
break;
}
}
}
2)In onCreate method of Activity, call setTheme method
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setTheme();
}
3)Gets user selected theme
private void setTheme() {
int themeInt = getThemeValue();
ThemeUtils.onActivityCreateSetTheme(this, themeInt);
}
4) After getting the style id, you can call ThemeUtils class method onActivityCreateSetTheme
private int getThemeValue() {
SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(getContext());
Boolean isGrid = pref.getBoolean(getString(R.string.grid_switch), false);
if (isGrid) {
return 2;
} else {
return 1;
}
}
Hope this option helps you !!!
So, I'm kind of a noob in Android but I searched a lot for this and I was not able to find a solution:
In my navigation drawer, each row opens a new intent. How can I check if a certain intent is open/active so that I'll use that instead of creating a new one?
I tried using this solution:
Link
But my issue is that the drawer opens the same class everytime, but each class has different "extras." For example:
public void itemClicked(View view, int position) {
Intent intent=null;
switch (position) {
case 1:
intent = new Intent(getActivity(), DisplayActivity.class);
intent.putExtra("ARGUMENT","SECTION 1");
break;
case 2:
intent = new Intent(getActivity(), DisplayActivity.class);
intent.putExtra("ARGUMENT","SECTION 2");
break;
case 3:
intent = new Intent(getActivity(), DisplayActivity.class);
intent.putExtra("ARGUMENT","SECTION 3");
break;
}
startActivity(intent);
}
How can I check if an intent with that class and with those extras is already open?
Thanks!
You can use shared preferences or extent from Application class where you store last/current activity visible
If you extend class for Application or you are targetting devices API Level 14 or above you can implement ActivityLifecycleCallbacks.
Sample Code
public class MyApplication extends Application implements ActivityLifecycleCallbacks {
private static boolean isMySomeActivityVisible;
#Override
public void onCreate() {
super.onCreate();
// Register to be notified of activity state changes
registerActivityLifecycleCallbacks(this);
....
}
#Override
public void onActivityResumed(Activity activity) {
if (activity instanceof YOURACTIVITY) {
isMySomeActivityVisible = true;
}
}
#Override
public void onActivityStopped(Activity activity) {
if (activity instanceof YOURACTIVITY) {
isMySomeActivityVisible = false;
}
}
// Other state change callback stubs
....
}
You can also check about the launch modes in android which you make use of.
I think it will serve your requirement well.
For your reference have a look at this
http://www.intridea.com/blog/2011/6/16/android-understanding-activity-launchmode
In my Android application, when the user exits the preference screen, the ui needs to be updated according to the updated preferences. Is there any simple way to do this?
EDIT:
Preferences activity:
public class Settings extends PreferenceActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.prefs);
}
}
and in the app, a TextView needs to be changed to one of the preferences strings.
Wherever the UI needs to be updated, those values should be obtained from the SharedPreferences. When you change the preferences simply update your shared preferences values and when you return back to the view (onResume()) update your views (by getting the values again from the shared preferences).
Implement OnSharedPreferenceChangeListener in the activity to be updated and include the code you want to execute in the method onSharedPreferenceChanged(...)
I think its so easy
From what ever activity you are Starting this activity you shall start it using
startActivityForResult(<Your intent>, MY_PREFERENCE);
and now you can override onActivityResult
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
Log.d("VideoListActivity.onActivityResult ", "Opening");
switch(requestCode)
{
case MY_PREFERENCE:
switch(resultCode)
{
case RESULT_OK:
// do your stuff
break;
case RESULT_CANCELED:
// do your stuff
break;
default:
break;
}
break;
default:
break;
}
Log.d("VideoListActivity.onActivityResult ", "Closing");
super.onActivityResult(requestCode, resultCode, data);
}
and Remember when your job done in your child activity you must set result using this
Intent intent = new Intent();
intent.putExtra("isUpdated", true);
setResult(RESULT_OK, intent);
I have one activity representing a screen that is used in two variations. The only difference is that in one case it's used to handle numbers in the other for colors. This is how it is declared:
> public class MainScreen extends Activity {
/** Called when the activity is first created. */
private Integer activityCode;
private static final int ACTIVITY_NUMBER = 0;
private static final int ACTIVITY_COLOR = 1;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
activityCode = savedInstanceState != null ? savedInstanceState
.getInt("Task") : null;
if (activityCode == null) {
Bundle extras = getIntent().getExtras();
activityCode = extras != null ? extras.getInt("Task") : null;
}
do stuff depending on which activity is actually chosen
}
And this is how it is called
FROM WITHIN ITSELF
:
> #Override
public boolean onMenuItemSelected(int featureId, MenuItem item) {
Intent i;
switch (item.getItemId()) {
case OTHER_PAGE_ID:
i = new Intent(this, MainScreen.class);
if (activityCode == ACTIVITY_NUMBER) {
i.putExtra("Task", ACTIVITY_COLOR);
startActivityForResult(i, ACTIVITY_COLOR);
finish();
} else {
i.putExtra("Task", ACTIVITY_NUMBER);
startActivityForResult(i, ACTIVITY_NUMBER);
finish();
}
return true;
....
}
Is this way of re-using the same class actually OK? I use the same class for very similar screens and want to switch back and forth depending on the user selection.
BUT the class calls itself everytime a different screen is selected between NUMBERS <-> COLOR.
The problem is, that when I go from NUMBERS to COLORS and then press back-arrow the app quits. However, when I go from NUMBERS to another screen and press back, it goes back to NUMBERS again.
Why doesn't going back to where I come from work in the case where the class calls itself? I would assume it just puts each call on the stack and comes back to it.
Isn't this just a mini recursion where NUMBER calls itself as COLOR and when finished appears again?
I hope I could make myself clear. Thanks for your help
That makes my head spin. Why not just use two Activities? If it's for code reuse, just have one base class with all your common code, then extend it for your Colour and Number classes:
public class Base extends Activity {
// common code here
}
public class Colour extends Base {
// colour specific code here
}
public class Number extends Base {
// number specific code here
}
This question already has answers here:
Implementing user choice of theme
(4 answers)
Closed 5 years ago.
I've created a PreferenceActivity that allows the user to choose the theme he wants to apply to the entire application.
When the user selects a theme, this code is executed:
if (...) {
getApplication().setTheme(R.style.BlackTheme);
} else {
getApplication().setTheme(R.style.LightTheme);
}
But, even though I've checked with the debugger that the code is being executed, I can't see any change in the user interface.
Themes are defined in res/values/styles.xml, and Eclipse does not show any error.
<resources>
<style name="LightTheme" parent="#android:style/Theme.Light">
</style>
<style name="BlackTheme" parent="#android:style/Theme.Black">
</style>
</resources>
Any idea about what could be happening and how to fix it?
Should I call setTheme at any special point in the code? My application consists of several Activities if that helps.
I would like to see the method too, where you set once for all your activities. But as far I know you have to set in each activity before showing any views.
For reference check this:
http://www.anddev.org/applying_a_theme_to_your_application-t817.html
Edit (copied from that forum):
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Call setTheme before creation of any(!) View.
setTheme(android.R.style.Theme_Dark);
// ...
setContentView(R.layout.main);
}
Edit
If you call setTheme after super.onCreate(savedInstanceState); your activity recreated but if you call setTheme before super.onCreate(savedInstanceState); your theme will set and activity
does not recreate anymore
protected void onCreate(Bundle savedInstanceState) {
setTheme(android.R.style.Theme_Dark);
super.onCreate(savedInstanceState);
// ...
setContentView(R.layout.main);
}
If you want to change theme of an already existing activity, call recreate() after setTheme().
Note: don't call recreate if you change theme in onCreate(), to avoid infinite loop.
recreate() (as mentioned by TPReal) will only restart current activity, but the previous activities will still be in back stack and theme will not be applied to them.
So, another solution for this problem is to recreate the task stack completely, like this:
TaskStackBuilder.create(getActivity())
.addNextIntent(new Intent(getActivity(), MainActivity.class))
.addNextIntent(getActivity().getIntent())
.startActivities();
EDIT:
Just put the code above after you perform changing of theme on the UI or somewhere else. All your activities should have method setTheme() called before onCreate(), probably in some parent activity. It is also a normal approach to store the theme chosen in SharedPreferences, read it and then set using setTheme() method.
i got the same problem but i found the solution.
public class EditTextSmartPhoneActivity extends Activity implements DialogInterface.OnClickListener
{
public final static int CREATE_DIALOG = -1;
public final static int THEME_HOLO_LIGHT = 0;
public final static int THEME_BLACK = 1;
int position;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState)
{
position = getIntent().getIntExtra("position", -1);
switch(position)
{
case CREATE_DIALOG:
createDialog();
break;
case THEME_HOLO_LIGHT:
setTheme(android.R.style.Theme_Holo_Light);
break;
case THEME_BLACK:
setTheme(android.R.style.Theme_Black);
break;
default:
}
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
private void createDialog()
{
/** Options for user to select*/
String choose[] = {"Theme_Holo_Light","Theme_Black"};
AlertDialog.Builder b = new AlertDialog.Builder(this);
/** Setting a title for the window */
b.setTitle("Choose your Application Theme");
/** Setting items to the alert dialog */
b.setSingleChoiceItems(choose, 0, null);
/** Setting a positive button and its listener */
b.setPositiveButton("OK",this);
/** Setting a positive button and its listener */
b.setNegativeButton("Cancel", null);
/** Creating the alert dialog window using the builder class */
AlertDialog d = b.create();
/** show dialog*/
d.show();
}
#Override
public void onClick(DialogInterface dialog, int which) {
// TODO Auto-generated method stub
AlertDialog alert = (AlertDialog)dialog;
int position = alert.getListView().getCheckedItemPosition();
finish();
Intent intent = new Intent(this, EditTextSmartPhoneActivity.class);
intent.putExtra("position", position);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
}
We have to set theme before calling 'super.onCreate()' and 'setContentView()' method.
Check out this link for applying new theme to whole application at runtime.
I had a similar problem and I solved in this way..
#Override
public void onCreate(Bundle savedInstanceState) {
if (getIntent().hasExtra("bundle") && savedInstanceState==null){
savedInstanceState = getIntent().getExtras().getBundle("bundle");
}
//add code for theme
switch(theme)
{
case LIGHT:
setTheme(R.style.LightTheme);
break;
case BLACK:
setTheme(R.style.BlackTheme);
break;
default:
}
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//code
}
this code is for recreate the Activity saving Bundle and changing the theme. You have to write your own onSaveInstanceState(Bundle outState); From API-11 you can use the method recreate() instead
Bundle temp_bundle = new Bundle();
onSaveInstanceState(temp_bundle);
Intent intent = new Intent(this, MainActivity.class);
intent.putExtra("bundle", temp_bundle);
startActivity(intent);
finish();
Instead of
getApplication().setTheme(R.style.BlackTheme);
use
setTheme(R.style.BlackTheme);
My code: in onCreate() method:
super.onCreate(savedInstanceState);
if(someExpression) {
setTheme(R.style.OneTheme);
} else {
setTheme(R.style.AnotherTheme);
}
setContentView(R.layout.activity_some_layout);
Somewhere (for example, on a button click):
YourActivity.this.recreate();
You have to recreate activity, otherwise - change won't happen
This is what i have created for Material Design. May it will helpful you.
Have a look for MultipleThemeMaterialDesign
I know that i am late but i would like to post a solution here:
Check the full source code here.
This is the code i used when changing theme using preferences..
SharedPreferences pref = PreferenceManager
.getDefaultSharedPreferences(this);
String themeName = pref.getString("prefSyncFrequency3", "Theme1");
if (themeName.equals("Africa")) {
setTheme(R.style.AppTheme);
} else if (themeName.equals("Colorful Beach")) {
//Toast.makeText(this, "set theme", Toast.LENGTH_SHORT).show();
setTheme(R.style.beach);
} else if (themeName.equals("Abstract")) {
//Toast.makeText(this, "set theme", Toast.LENGTH_SHORT).show();
setTheme(R.style.abstract2);
} else if (themeName.equals("Default")) {
setTheme(R.style.defaulttheme);
}
This way work for me:
#Override
protected void onCreate(Bundle savedInstanceState) {
setTheme(GApplication.getInstance().getTheme());
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
Then you want to change a new theme:
GApplication.getInstance().setTheme(R.style.LightTheme);
recreate();
You can finish the Acivity and recreate it afterwards in this way your activity will be created again and all the views will be created with the new theme.
Call SetContentView(Resource.Layout.Main) after setTheme().
This had no effect for me:
public void changeTheme(int newTheme) {
setTheme(newTheme);
recreate();
}
But this worked:
int theme = R.style.default;
protected void onCreate(Bundle savedInstanceState) {
setTheme(this.theme);
super.onCreate(savedInstanceState);
}
public void changeTheme(int newTheme) {
this.theme = newTheme;
recreate();
}