Android: How to set static value before onCreate is called - android

I want to have an "option" set before the Activity is created or at least before it starts. If there is a way to do this via the AndroidManifest? Consider this example where we have a global config class that is used in onCreate to instantiate an object (not fully OO for brevity)
public class Global {
public static boolean visible = false;
}
public class MyActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// here is where we want the most up-to-date value of visible
MyObject obj = new MyObject(Global.visible);
}
}
Obviously in this case "visible" would be "false". If this were some sort of API library, we would like to provide the option for users to set "visible" to "true".
Update 1
The objective is to have the global class in a pre-compiled library and have its value set by a developer utilizing the library. I am looking for easiest way for the developer to do this when they create their application; I think the manifest is the probably the way to go but I don't know how to inject the value for "visible" via the xml. The answers below using preferences are good but only cover the users point-of-view.
Update 2
IMHO using resources works best here.
<bool name="visible">true</bool>
public class MyActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// here is where we want the most up-to-date value of visible
Resource res = getResource();
MyObject obj = new MyObject(res.getBoolean(R.bool.visible));
}
}

I think using SharedPreferences would do what you are looking for, using Global.visible as the default value. Then if the user changes it to true, it will use that value.
boolean makeVisible = PreferenceManager.getDefaultSharedPreferences(this).getBoolean(
"MyVisiblePreference",
Global.visible);
MyObject obj = new MyObject(makeVisible);
To allow the preference to be updatable without re-compiling or setting (through a Preferences activity), you can load the default preference from resources:
<bool name="MyVisiblePreference">true</bool>
And reference it similarly with:
boolean makeVisible = PreferenceManager.getDefaultSharedPreferences(this).getBoolean(
"MyVisiblePreference",
getResources().getBoolean(R.bool.MyVisiblePreference));
If the developer does not set the preference to false, it will default to true (based upon the resources value).

For simple objects you can create them like this: (Based off of your code example)
public static boolean visible = false;
public class MyActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// here is where we want the most up-to-date value of visible
MyObject obj = new MyObject(Global.visible);
}
}
For a more complex object you can initialize it with a static initializer like this:
public static boolean visible;
static {
visible = false;
}
public class MyActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// here is where we want the most up-to-date value of visible
MyObject obj = new MyObject(Global.visible);
}
}

You can subclass android.app.Application, this class has method onCreate that you can override. Your subclass have to be defined in AndroidManifest.xml in <application name="YourApplication">. onCreate of application is called before all other components in your application are created (before any Activity or Service).

Related

Getting resources from a static context

I've read a few questions about this, but I wasn't happy with the answers, so I decided do ask about my particular example.
I'm developing and Android App that has a Settings screen with a few configurable integer parameters. All these parameters have a maximum and minimum value. Therefore, everytime the user sets a new value for those parameters, I want to validate them. If the new value is out of the defined bounds, I want to show a Toast informing the user of what went wrong.
On the other hand, because in some situations in my App the user can "spam" a button that may show a Toast, in order to avoid having Toast showing repetedly for a while, I created an Application class with a static Toast that is shown everytime I want to show a toast:
public class MyApplication extends Application {
private static Toast toast;
public static void showToast(Context context, String string){
//(...)
}
}
Back to the Settings page, here's how I implemented it:
public class SettingsActivity extends PreferenceActivity {
private Context context;
static SharedPreferences sharedPreferences;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
context = this;
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
getFragmentManager().beginTransaction().replace(android.R.id.content, new SettingsFragment()).commit();
}
public static class SettingsFragment extends PreferenceFragment implements SharedPreferences.OnSharedPreferenceChangeListener {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences);
setListeners();
}
public void setListeners() {
setListenerA();
//(other listeners to other settings)
}
private void setListenerA() {
findPreference(KEY_PREF_A).setOnPreferenceChangeListener(
new Preference.OnPreferenceChangeListener() {
#Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
boolean isEmpty = newValue.toString().isEmpty();
//(other validations)
boolean isValid = !isEmpty; //&& (other validations)
if(!isValid){
if(isEmpty){
MyApplication.showToast(context, MyApplication.getResources().getString(R.string.toastPreferenceNullValue));
} else if(isAnotherReasonToFail1){
// another Toast
} // else if(other reasons to fail)
}
return isValid;
}
}
);
}
}
}
And here are my problems: MyApplication.getResources() is a non-static method and cannot be called from the static context of class SettingsFragment. Also context is not static (as it should not be) and can't also be referenced there.
I need to show that Toast because otherwise the user wouldn't have a clue why his settings weren't being applied. On the other hand, I need the error message to be stored in the strings.xml file, not only because that's how you do it, but also for future multi-language purposes.
I am not familiar with how Fragments work, and I made the Settings screen like this after reading a few articles (like this one) and some questions here. There might be a different way to make a Settings screen that allows me to do what I want, I just don't know any.
Can someone suggest an approach that fits my problem?
Thanks
EDIT: emerssso solved the resources part. Now the problem is only how to call the Toast without having a context.
Fragment has a getResources() method that is equivalent to calling Application::getResources(). The only caveat is that you have to make sure that the fragment is attached to an activity (i.e. getActivity() != null) or you risk throwing an exception.
See: https://developer.android.com/reference/android/app/Fragment.html#getResources()
More generally, getActivity() can be used to get a valid context whenever the fragment is attached to the activity, as Activity is an implementation of Context.
If you want to have a context reference even after a fragment has detached, you can store a reference to getActivity().getApplicationContext() safely in the fragment for later use, but this is probably not ideal.

Getting string resource from xml file outside onCreate method

I am trying to use a String from the string.xml file as a key in a key-value pair. However when I try to declare the variable before the onCreate() method, the program crashes. So if I use the following code I get an error:
public class MainActivity extends ActionBarActivity {
String MAX_SQUAT = getResources().getString(R.string.max_squat);
protected void onCreate(Bundle savedInstanceState) {
//blah blah blah
}
}
Whereas when I declare MAX_SQUAT inside the onCreate() method, there is no problem. I want to declare it outside of onCreate() method so I don't need to define it in other methods
You need a Context to get resources (as you can see in the Docs getResources() is a method of Context). Since the Context isn't available before onCreate(), you can't do this.
You can declare the variable before onCreate() but you can't initialize it until after onCreate() has been called.
Ex.
public class MainActivity extends ActionBarActivity {
String MAX_SQUAT;
protected void onCreate(Bundle savedInstanceState) {
// super call, set content view
// now you can get the string from strings.xml safely
MAX_SQUAT = getResources().getString(R.string.max_squat);
}
Declaring it as a member variable this way but initializing it in onCreate() will allow you to use it throughout the class and keep it from crashing.

Android : Passing a value between activities

In my Android application I have to use common string value for all activities. "commonValue" is the common string value that I want to use in all activities. Relevant code of the main activity like this :
public class TestActivity extends Activity {
public String commonValue;//THE COMMON STRING FOR ALL ACTIVITIES
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
commonValue = "DemoValue";
}
}
In my next activity I created an object of "TestActivity" class and tried to assign "testValue" string to another string named "str"
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.testlist);
TestActivity obj = new TestActivity();//OBJECT OF MAIN ACTIVITY
String str = obj.commonValue;
}
but the "str" value in second activity does not equal to the value assigned in my first activity. Why is that & How can I do this?
Thanks!
Put your value in string.xml
<string name="common_value">DemoValue</string>
and use in any activity like this..
String common_value = getApplicationContext().getString(R.string.common_value);
Start using SharedPreferences in your app.
In your first activity you would do
SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);
SharedPreferences.Editor editor = settings.edit();
editor.putString("commonValue", "DemoValue");
editor.commit();
In your second activity
SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);
String str = settings.getString("commonValue", null);
Try this -
TestActivity.java
public class TestActivity extends Activity {
public static String commonValue;//THE COMMON STRING FOR ALL ACTIVITIES
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
commonValue = "DemoValue";
}
}
another activity
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.testlist);
String str = TestActivity.commonValue;
}
If the value is always the same you can create a public static final variable and access it via TestActivity.COMMON_VALUE.
If you want to pass around a value between to Activities you should use Intents and add an extra with the value you want to pass.
As Sana has suggested, use SharedPreferences.
Alternatively, use a global constant class. If you want to stick with what you have, then you could try:
String str = TestActivity.this.commonValue;
Your existing code is creating a new instance of the activity, so it's not going to have the value you had set.
To pass data between activities use Bundle. and methods,
intent.putExtra()
and If you want to set data to be global to your app, then create an application class, and save the data there.
We have an Application file for each app you can declare the variable there and as the Application file can get from any activity so using the public getter setter and can get/set that
there are vaious oter metjod you can sue as mention on developer.android http://developer.android.com/resources/faq/framework.html
Singleton class
A public static field/method
A HashMap of WeakReferences to Objects (almost same as my above solution )
Persistent Objects
take a look on them as well
The reason why commonValue doesn't equal what you set in TestActivity onCreate method is because that function hasn't been called yet.
The solution for this is already mentioned by others. Like putting the value in a bundle.

How can I select the default header in my PreferenceActivity on tablets?

I am working with a PreferenceActivity that will be fully compatible with tablets.
For this, I will work as advised by Google in this page.
#Override
public void onBuildHeaders(List<Header> target) {
loadHeadersFromResource(R.xml.preference_headers, target);
}
My problem is that I would like to be able to select the default header when the activity is launched.
For instance, I have several headers;
General Settings
UI Settings
Network settings
And depending on which activity I come from, I would like to display the correct settings.
Is there a way to achieve that?
When creating the Intent to invoke the PreferenceActivity, you can add the extra string 'EXTRA_SHOW_FRAGMENT' to specify which fragment should be initially displayed. You pass the name of the fragment you would like to select.
For instance, if you would like to select the General Settings header (and its contents) you can use the following code:
final Intent intent = new Intent(this, ExtendedPreferenceActivity.class); // Assume we call it from an other activty
intent.putExtra(EXTRA_SHOW_FRAGMENT, GeneralSettingsFragment.class.getName());
startActivity(intent);
More information on this can be found here: http://developer.android.com/reference/android/preference/PreferenceActivity.html
In an issue report to Google it is reported that for Android version 3.0 the correct header will not be automatically selected as well. For the issue report and its workaround look here: issue report.
You can create PreferenceHeaders dynamically using PreferenceActivity.Header class
http://developer.android.com/reference/android/preference/PreferenceActivity.Header.html
You can use a fragment by default:
Here is what I've done:
public class PreferencesActivity extends SherlockPreferenceActivity {
/** Variables **/
/** Constants **/
private static final String CLASSTAG = PreferencesActivity.class.getSimpleName();
/** Class Methods **/
#Override
public void onCreate(Bundle savedInstanceState) {
Log.v(CLASSTAG, "onCreate");
super.onCreate(savedInstanceState);
initializeUI();
}
#Override
public Intent getIntent() {
Log.v(CLASSTAG, "getIntent");
final Intent modIntent = new Intent(super.getIntent());
modIntent.putExtra(EXTRA_SHOW_FRAGMENT, SettingsFragment.class.getName());
modIntent.putExtra(EXTRA_NO_HEADERS, true);
return modIntent;
}
/** Private Functions **/
private void initializeUI() {
getSupportActionBar().hide();
}
/** Classes **/
public static class SettingsFragment extends PreferenceFragment {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Load the preferences from an XML resource
addPreferencesFromResource(R.xml.settings_preference);
initializeUI();
}
private void initializeUI() { }
}
}
and the default xml (as prior HoneyComb versions...):
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory
android:title="#string/preferences_category_1">
<com.taptime.ui.preferences.ClickPreference
android:key="#string/preferences_conditions_key"
android:title="#string/preferences_conditions_title"/>
</PreferenceCategory>
<PreferenceCategory
android:title="#string/preferences_category_2">
<com.newin.android.ui.widget.ClickPreference
android:key="#string/preferences_logout_key"
android:title="#string/preferences_logout_title"
android:summary="#string/preferences_logout_summary"/>
</PreferenceCategory>
</PreferenceScreen>

best way to handle resources

I was kind of stuck trying to pass the resources to a subclass used on my Activity. I solved it in two ways, but not sure if one or both will lead to possible memory leaks. So here is what I have so far:
-myactivity (the activity class)
-global (global class to the package, I'm using to to save global accesible variables)
-subclass (the subclass where I want to use a drawable resource)
a)
public class global{
public static Resources appRes;
....
}
public class myactivity extends Activity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
global.resApp = this.getResources();
...
}
private void somewhere(){
subclass tmp = new subclass();
tmp.subclasmethod();
}
}
public class subclass{
public subclass(){...}
public void subclassmethod(){
Bitmap bmp = BitmapFactory.decodeResource(Global.appRes, R.drawable.myres);
...
}
}
b)
public class myactivity extends Activity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
}
private void somewhere(){
subclass tmp = new subclass(this.getContext());
tmp.subclasmethod();
}
}
public class subclass{
Context context;
public subclass(Context context){
this.context = context
...
}
public void subclassmethod(){
Bitmap bmp = BitmapFactory.decodeResource(context.getResources(), R.drawable.myres);
...
}
}
Thanks in advance for you feedback.
If you want a global class to store application-wide values, you should at least not use your option a. Instead, take a look at the Application class, which is meant to help you with exactly this:
Base class for those who need to
maintain global application state.
Otherwise, the alternative you suggest in option b is an OK way to do it. At least if all you need is to pass along a reference to your application context so that you can access the resources.

Categories

Resources