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.
Related
Edit: Changing the language results in SettingsActivity.onCreate being called twice: (1) Due to recreate() and (2) due to context change.
However SettingsFragment.onCreate is being called only once, which kind of explains below question. But why isnt't SettingsFragment.onCreate being called after the second execution of the SettingsActivity.onCreate?
I am quite new to Android development and want to understand the reason which makes my application crash:
Within a settings activity I have placed a PreferenceFragment which allows the user to change the UI language. In order to take effect and not to spoil the back stack I call the recreate() method of the fragment's activity.
The first implementation looked like below:
(A)
public class SettingsActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SettingsFragment fragment = new SettingsFragment();
getFragmentManager().beginTransaction().replace(android.R.id.content, fragment).commit();
getFragmentManager().executePendingTransactions();
fragment.setActivity(this);
}
public static class SettingsFragment extends PreferenceFragment implements SharedPreferences.OnSharedPreferenceChangeListener
{
Activity m_activity;
public void setActivity(Activity activity){
m_activity = activity;
}
#Override
public void onCreate(final Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences);
SharedPreferences preferences = getPreferenceScreen().getSharedPreferences();
preferences.registerOnSharedPreferenceChangeListener(this);
}
#Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
...
m_activity.recreate();
}
}
}
Sometimes above solution works, sometimes m_activity is Null and the application crahes. As a possible alternative solution I removed the 'setActivity' setter and called 'getActivity' within onSharedPreferenceChanged:
(B)
#Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
...
getActivity().recreate();
}
But this also results in sporadic NullPointerExceptions.
In scenario (B) I found that the fragment's 'onAttach' is not being called for any reason sometimes and hence 'getActivity' returns null. However I would expect scenario (A) not to crash since this isn't Null in onCreate?
Problem solved!
For those who have the same problem: I had to unregister the onSharedPreferenceChanged listener in SettingsFragment.onDestroy.
I have an Activity in whose onCreate() method i call a Utility function.
This utility functions requires a callback class instance as a parameter, in which it returns the info that i need. this is:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Utility.functionA(new functionACallBack() {
/**
*
*/
private static final long serialVersionUID = -7896922737679366614L;
#Override
public void onResponse(String error) {
((MyActivity) AppClass.getAppContext()).finish();
}
});
}
Once I have obtained that info, I want to close the activity. so i called finish() from inside the anonymous class that i created for the callback.
But the activity is not getting finished. I thought maybe i need to call finish() from UI thread so i did runOnUiThread(), in inside it also i tried calling finish(). But it just doesn't work.
Could someone please help me with this issue?
UPDATE:
I am storing APP context and then trying to use that but to no avail.
public class AppClass extends Application {
private static Context mContext;
#Override
public void onCreate() {
super.onCreate();
AppClass.mContext = getApplicationContext();
}
public static Context getAppContext(){
return AppClass.mContext;
}
}
Simply call something like this:
#Override
public void onResponse(String error) {
((Activity) context).finish();
}
As this is a static function, you'll have to be able to access your Context in a static way. You can save that as a Class variable, but you'll have to be aware about its handling as it might lead to memory leaks.
To avoid them, you can declare a class that extends Application and save here your context, so this way you won't ever have a memory leak.
Try using this code:
((Activity) ActivityClass.this).finish();
Remember, use the Activity class, not the Application one.
I need some help with an android project I am working on. I am trying to use switch preferences to send a certain text. Basically, it the user switches the switch from off to on, I want the phone to send a text saying "on". Then when the user turns the switch from on to off, it sends a text saying "off". All I need is to be able to see what the current state of the switch is and then if it's off, call a "turn on" method and vice-versa.
I've never asked a question like this, so I don't really know what part of my code to post.(If asked, I can post most of my code.) I think it has something to do with the onPreferenceChangeListener, but I'm not sure how to implement it. Any ideas?
Edit: Here is the main activity class:
public class MainActivity extends PreferenceActivity implements OnSharedPreferenceChangeListener {
public static final String KEY_ROOM1_SWITCH = "switch_room_1";
private SwitchPreference mSwitchPreference1;
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
//This is a sample of one of 4 switches that are being used. They are all basically the same, but with different variables
if (key.equals(KEY_ROOM1_SWITCH)) {
boolean checkedornot1;
SharedPreferences myPreference=PreferenceManager.getDefaultSharedPreferences(this);
checkedornot1 = myPreference.getBoolean("switch_room_1", false);
if (checkedornot1 = true)
mSwitchPreference1.setChecked(true);
else
mSwitchPreference1.setChecked(false);
}
}
}
Do I need to grab the value that is stored in the shared preferences and make my choice based on that? or is there something else I am missing?
Edit your class that extends PreferenceActivity and add the private variable: private OnSharedPreferenceChangeListener listener;
Create and register your listener within the onResume method:
public void onResume() {
super.onResume();
listener = new OnSharedPreferenceChangeListener() {
public void onSharedPreferenceChanged(SharedPreferences sp, String key) {
if (key.contains("your switchpreference key name")
if (sp.getBoolean("your switchpreference key name",false) {
sendOnSMS();
} else {
sendOffSMS();
}
}
PreferenceManager.getDefaultSharedPreferences(this).registerOnSharedPreferenceChangeListener(listener);
}
Unregister your listener within the onPause method:
public void onPause() {
super.onPause();
PreferenceManager.getDefaultSharedPreferences(this).unregisterOnSharedPreferenceChangeListener(listener);
}
Implement the sendOnSMS and sendOffSMS methods.
I have a PreferenceActivity class that handles the getting and setting of preferences within my application. But I have a "main" Activity class that makes a call to a web service right at start-up, and based on the return value from the web service, needs to update one of the preferences.
I can't seem to get this to work without throwing a NullPointerException error.
Here is the code from the main Activity:
protected void onPostCreate(Bundle savedInstanceState)
{
if(getWebServiceResult.equals("FALSE"))
{
//do stuff
}
else
{
myPreferences prefs = new myPreferences();
prefs.updateMyChoice("TRUE");
}
}
And here is the code from the PreferenceActivity class that is throwing the error:
public void updateMyChoice(String newText)
{
if(subscriber_opt_in == null) //this is coming up null
{
subscriber_opt_in = (EditTextPreference) findPreference("opt_in"); //error here
}
subscriber_opt_in.setText(newText);
subscriber_opt_in.setSummary(newText);
}
I need to know how to properly update this preference. If there's a way to do it within the main Activity class, that's even better, but if I have to do it through the PreferenceActivity class, I just need to understand how to do it.
Thanks!
You should use SharedPreference class:
http://developer.android.com/reference/android/content/Context.html#getSharedPreferences%28java.lang.String,%20int%29
Code examples and use here:
http://developer.android.com/guide/topics/data/data-storage.html#pref
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).