Access default value from custom preference - android

I'm trying to create my own Preference in which I want to give the user the choice to select the new value or to reset to default.
Therefore I need to "store" two values in one preference.
I mean, I want to access the stored value and the default value (defined in XML) at the same time.
<my.custom.preference
myCustomAttribute="R.color.someColor"
android:defaultValue="#color/someColor"
android:key="myPref"
/>
In my code, I read the value like this:
String value = attrs.getAttributeValue(null, "myCustomAttribute");
The return value is "R.color.someColor".
So, I tried to get the R-reference of this string, but this is the point where I'm failing.
int neededValue = ???
At the moment, I use a really bad workaround.
I search the selected Preference by key and set neededValue programmatically like this:
switch(getKey()) {
case "firstCustomPreference":
neededColor = R.color.firstColor;
break;
case "secondCustomPreference":
neededColor = R.color.secondColor;
break;
}
This does work, but I really hope there is a cleaner way of doing this.
So my question is: Is there a way to get the int value from the string "R.color.someColor"? Alternatively, is it possible to access the default value?

"Is there a way to get the int value from the String "R.color.someColor"?"
int resourceId = getResources().getIdentifier("someColor", "color", getPackageName());
int color = getResources().getColor(resourceId);

Related

Can I modify a Strings.xml file programmatically in Android? [duplicate]

I have declared a string in my strings.xml file , and using it in my activity as R.string.compose_title. (setting it as title i.e. setTitle(R.id.compose_title)). Now in some case I want to edit the string and then use it to set the title . How can I do this ?
P.S. I need to change value of a single string only , So declaring a new strings.xml for each case(which are variable depending upon the user) using localization seems to be a lil inefficient .
One thing what you have to understand here is that, when you provide a data as a Resource, it can't be modified during run time. For example, the drawables what you have in your drawable folder can't be modified at run time. To be precise, the "res" folder can't be modified programatically.
This applies to Strings.xml also, i.e "Values" folder. If at all you want a String which has to be modified at runtime, create a separate class and have your strings placed in this Class and access during run time. This is the best solution what I have found.
example howto:
how? by changing one variable reference to other reference
usage:
setRColor(pl.mylib.R.class,"endColor",pl.myapp.R.color.startColor);
// override app_name in lib R class
setRString(pl.mylib.R.class,"app_name",pl.myapp.R.string.app_name);
base methods:
public static void setRColor(Class rClass, String rFieldName, Object newValue) {
setR(rClass, "color", rFieldName, newValue);
}
public static void setRString(Class rClass, String rFieldName, Object newValue) {
setR(rClass, "string", rFieldName, newValue);
}
// AsciiStrings.STRING_DOLAR = "$";
public static void setR(Class rClass, String innerClassName, String rFieldName, Object newValue) {
setStatic(rClass.getName() + AsciiStrings.STRING_DOLAR + innerClassName, rFieldName, newValue);
}
helper methods :
public static boolean setStatic(String aClassName, String staticFieldName, Object toSet) {
try {
return setStatic(Class.forName(aClassName), staticFieldName, toSet);
} catch (ClassNotFoundException e) {
e.printStackTrace();
return false;
}
}
public static boolean setStatic(Class<?> aClass, String staticFieldName, Object toSet) {
try {
Field declaredField = aClass.getDeclaredField(staticFieldName);
declaredField.setAccessible(true);
declaredField.set(null, toSet);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
#bradenV2 My app is supporting many languages , so I wanted to take a
string from my strings.xml that's currently in use and change that ,
and then use that one – atuljangra Mar 12 '12 at 22:04
ps the above solution is good for example when u want to inject some data in already compiled lib/jar. But if u want localize strings just make folder under res per LANG CODE like values-CC where cc is lang code (values-de,values-cs) etc
then u have 2 choices:
"build in" system dependent language selection - based on device selected lang
via create resources for configuration - you decide which lang show
like this:
configuration = new Configuration(resources.getConfiguration());
configuration.setLocale(targetLocale);
String localized = Context.createConfigurationContext(configuration)
.getResources()
.getString(resourceId);
I don't think you can programmatically customize the R class as it is built by ADT automatically.
I had a situation like this, where one of my strings.xml values had some dynamic piece of it. I set up the strings.xml with a "replacement text" (something like %%REPLACEMENT_EMAIL%%), and when I wanted to use that string programatically, I retrieved the string value of the resource, and replaced instances of that replacement text with the dynamic value (e.g. input by the user).
To be honest, my app has not been localized yet, but I'm still attempting to follow best practices w.r.t. not hardcoding any strings.
Use SharedPreferences instead of a Java class. It will give you more versatility if you decide to take values from the outside (web). Filling Java class in runtime can be useless offline. In case of SharedPreferences you have to ensure they are loaded only once, during app's first start, and then updated only by manual request, as previous import will be used.
myActivity.getSharedPreferences("com.example.imported",0)
.edit()
.putString("The news",getTheNews())
.apply();
Maybe you want to "modify" the string.xml so when it is required by the activity again it uses the new value, for example to keep a new dynamic title after screen rotation.
First, you can't modify the resource. It's already compiled. You can't modify the R class (what for?) all it's atributes are "final".
So, for the example above you can use onSaveInstanceState() and onRestoreInstanceState() for those properties you wanna keep on display.
According to my knowledge, you can't change resource value(R class value) while app running. why don't try to store on shared preference? I recommend you to use shared preference
I used below method to get the key-value pairs from the API and storing it in HashMap globally. If the key value is not found in HashMap then I will search that key in strings.xml file. It will achieve the purpose of dynamically changing the value of key.
public String getAppropriateLangText(String key) {
String value = "";
try {
HashMap<String, String> HashMapLanguageData HashMapLanguageData = gv.getHashMapLanguageData();
value = HashMapLanguageData.get(key);//Fetching the value of key from API
if (value == null || value.length() == 0) { //If Key value not found, search in strings.xml file
String packageName = getPackageName();
int resId = getResources().getIdentifier(key, "string", packageName);
value = getString(resId);
}
} catch (Exception e) {
value = "";
}
return value;
}

NumberPicker.Formatter does not seem to be invoking in Android

I am trying to use a NumberPicker to display an array of Strings, but when I try and get the value back of the current String, the value is the integer index value of the String within the array which is the default response.
I have been trying to use NumberPicker.Formatter to get the actual String as a value back as opposed to the integer index value. I have implemented this, but it doesn't seem to be invoked. Could someone please tell me why this is and what I can do to fix it. Thanks in advance.
Here is the code:
final NumberPicker npUnits = (NumberPicker) numberPickerView.findViewById(R.id.numberPicker2);
npUnits.setMinValue(1);
npUnits.setDisplayedValues(tableUnitsArray);
npUnits.setMaxValue(tableUnitsArray.length);
npUnits.setFormatter(new NumberPicker.Formatter()
{
#Override
public String format(int value)
{
ArrayList<String> stringArrayList = (ArrayList<String>) Arrays.asList(tableUnitsArray);
String defaultUnits = stringArrayList.get(value);
System.out.println("Value formatted result: " + defaultUnits);
return defaultUnits;
}
});
npUnits.setDescendantFocusability(NumberPicker.FOCUS_BLOCK_DESCENDANTS);
NumberPicker allows you to pick your values based on the min and max values you specify. It does not need you to call setDisplayedValues(). NumberPicker uses the String array you specify as a set of alternate values which causes your formatter to be ignored. Try removing the call to setDisplayedValues().
If your values for your NumberPicker are not so straight forward (i.e not from min to max incrementing by 1), then you can transform your values in your formatter to get the desired number.
I have found out the answer to my question. NumberPicker.Formatter was the wrong thing that I was doing as I didn't actually need to format anything. I just needed to get the current value from the String[] displayed values.
This is the code that worked for me:
List<String> stringArrayList = (List<String>) Arrays.asList(npUnits.getDisplayedValues());
String defaultUnits = stringArrayList.get(npUnits.getValue() - 1);
I found a solution for a few bugs in NumberPicker that works in APIs 18-26 without using reflection and without using setDisplayedValues() here.

Can you call a string resource with a string?

I have a method that returns one of about 20 possible strings from an EditText. Each of these strings has a corresponding response to be printed in a TextView from strings.xml. Is there a way to call a string from strings.xml using something like context.getResources().getString(R.strings."stringFromMethod")? Is there another way to call a string from a large list like that?
The only methods I can think of is converting each string to an int, and use that to find a string in a string array, or a switch statement. Both of which involve a huge amount if-else if statements to convert the string to an int, and would take just enough steps to change if any strings were added or taken away that I'd be more likely to miss one and have fun bug hunting. Any ideas to do this cleanly?
Edit: Forgot to add, another method I tried was using was to get the resourceID from
int ID = context.getResources().getIdentifier("stringFromMethod", "String", context.getPackageName())
and taking that integer and putting it in
context.getResources().getString(ID)
That doesn't appear to be working either.
No, you can't. The getString() requires the resource id in integer format, so you can't append a string to it.
You can, however, try this:
String packageName = context.getPackageName();
int resId = context.getResources().getIdentifier("stringFromMethod", "string", packageName);
if (resId == 0) {
throw new IllegalException("Unknown string resource!"; // can't find the string resource!
}
string stringVal = context.getString(resId);
The above statements will return string value of resource R.string.stringFromMethod.
You need to use reflection (pretty ugly but only solution) load the R class, and get the relevant field by you string and get the value of it.
this is what I used to do in these kind of situations, I will made a Array like
int[] stringIds = { R.string.firstCase,
R.string.secondCase, R.string.thridCase,
R.string.fourthCase,... };
int caseFromServer=getCaseofServerResponse();
here caseFromServer varies from 0 to wahtever
and then simply
context.getResources().getString(stringIds[caseFromServer]);

how to get an int value according to its name

I would like to ask how can I get the value of predefined value to its name
The following is my code
public class Calculation_Activity extends Activity{
int a=1;
int b=2;
int c=50;
int result;
String array1[]=new String[]{"a","b","c"};
}
I would like to ask how can I get the value of the string by using array1[i]?
for instance, I would like to use array1[3]to call the value of c[ie.50]
May you give me some advice on this matter?
You might solve your issue by using a Map and its standard implementation HashMap:
Map<String, Integer> values = new HashMap<String, Integer>();
values.put("a",1);
values.put("b",2);
values.put("c",50);
String array1[] = new String[] {"a","b","c"};
int result = values.get(array1[2]); //result = 50
// or
int result = values.get("c"); //result = 50
You can use a HashMap (http://docs.oracle.com/javase/6/docs/api/java/util/HashMap.html), is a dictionary like data structure where you can store key-pair values
What you're trying to achieve would be better suited to a dynamic/scripting language. Have you considered using a Map instead of multiple varaibles?
In Java, this is not a common approach, such as it would be in scripting languages. You could try to use a Map (ie HashMap), which would enable you to achieve what you want, sort of.
In fact I think it is possible to do exactly what you want using reflection in Java, but I would not go there!

Using Shared Preferences with arrays

EDIT:
OK It turns out this code was working (more or less) I'd left in a line that reset the booleans I was trying to change. Thanks everyone for the help though.
Having trouble using SharedPreferences to read in saved array data when my app starts.
My _dPad Boolean and my _FreePlay Integer loads, saves and passes to and from my _renderer without any problems.
The trouble starts when I try and use some arrays
easteregg[] only has 2 entries right now so obviously I could just just turn them into separate variables but I wish to add more arras of longer length so this makes a convenient test example.
I've noted on the code what appears to happen (the easteregg[] settings just doesn't appear to have changed)
to read data:
// Read saved preferences
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
_renderer._dPad = prefs.getBoolean("_dPad", false); // * works ok *
_renderer._FreePlay = prefs.getInt("_FreePlay", 1); // * works ok *
_renderer.easteregg[0] = prefs.getBoolean("easteregg[0]", false ); // * not working
_renderer.easteregg[1] = true; // * even this is not working
setRenderer(_renderer);
to write data:
public void onDetachedFromWindow() {
super.onDetachedFromWindow();
// As good a time as any to save current config
save = false ; // don't commit if nothing changed.
SharedPreferences prefs =
PreferenceManager.getDefaultSharedPreferences(getContext());
SharedPreferences.Editor editor = prefs.edit();
if (_renderer._dPad != prefs.getBoolean("_dPad",false)){ save = true ;
editor.putBoolean("_dPad", _renderer._dPad);}
if (_renderer._FreePlay != prefs.getInt("_FreePlay",1)){ save = true ;
editor.putInt("_FreePlay", _renderer._FreePlay);}
editor.putBoolean("easteregg[0]", _renderer.easteregg[0]);
editor.putBoolean("easteregg[1]", _renderer.easteregg[1]);
if (save == true){editor.commit();}
}
And in the .renderer class
// START SAVE DATA
public boolean _dPad ; // false no Virtual Pad *Works Fine*
public int _FreePlay ; // 1 = no free play *Works Fine*
public boolean[] easteregg = new boolean[2]; *Values don't load or save*
//public boolean easteregg[]; // tried this first *CAUSES CRASH*
// END SAVE DATA
Do I have to convert the arrays to strings? I don't get how to change them.
I put your code into a quick activity, creating just the shell of the renderer class as you have above and found that your save boolean is false, so it never commits the preferences.
I forced the save to true, and played around with it and everything worked fine from there.
I'd recommend adding checks to the easter eggs the same as you have for any other preference; test to see if the current value is the same as the saved value, and if not, set the save flag.
I would suggest saving the array as a string in a single variable. It appears you have an array of booleans. So loop through it to make it a series of either ints (0, 1) or the string "true" or "false" then save it to an int or string.
I suspect the probelm might be that your setting name contains square brackets. I think that in key value names, the key name must be a valid variable name. And square brackets are not allowed in variable names.
However i would also expect this to throw an error. Does the code work if you name you settings "easteregg_01" and "easteregg_02"?
The best solutions would be to convert your array into JSON string and store it as preference value. If you have small amount of data, you can as well stick with org.json classes provided by android. If you have more data, GSON pull parser would be better, as it utlizes pull parser. And if you are really lazy, you grab my small databinding library and do:
String jsonState = preferences.getString(GAME_STATE, null);
StateStorage storage = JSONUnmarshaller.unmarshall(new JsonReader(new
StringReader(jsonState)), StateStorage.class);
and it will instantiate java class for you and fill in the data. And to save:
SharedPreferences.Editor editor = getPreferences(MODE_PRIVATE).edit();
StringWriter writer = new StringWriter();
JsonWriter jsonWriter = new JsonWriter(writer);
JSONMarshaller.marshall(jsonWriter, ss);
editor.putString(GAME_STATE, writer.toString());
editor.commit();
Databinding library is available on github, or from maven central:
https://github.com/ko5tik/jsonserializer
PS: at the moment I work on injection of preference values ( at the moment primitives only):
https://github.com/ko5tik/andject

Categories

Resources