I have trouble understanding the following thing: in my localized application I have an Enum in an activity that stores some localized strings (R.string.aString) which are compared against another localized string.
If while in application I change the locale and I came back and start the Activity that contains the Enum I observe that it's members have are the same as before localization change.
What is the reason for this?
Edit :
class Settings extends Activity
{
public enum SettingPreferenceScreen
{
Connection (R.string.Connection , xml_resource_1)
Legend (R,string.Legend ,xml_resource_2)
.......
String key;
int res;
SettingPreferenceScreen(String key, int res)
{....}
public int getResource (String key)
{
for(SettingPreferenceScreen p : SettingPreferenceScreen.values())
if(key.equals(p.key))
return p.res;
return -1;
}
}
}
First of all, try avoiding Enums when you develop for android.
Second, my guess is that the Enum get created on the onCreate() method
of your Activity and when you open the application for the second time that method is not called. Check the Activity's lifecycle.
R.string does not contain strings, it contains resource ID constants. (Auto-generated int values.) These IDs will be the same regardless of the configuration. The ID constants are used to fetch application resources from a Resources object (or from a Context, which calls through to your Resources). When you call getString or similar, the system will return the localized resource when applicable.
It seems like you're trying to re-implement functionality that Android already provides for you. Can you give us some more details if this is not the case?
Update:
It is not necessary to avoid enums in Android anymore... I don't know when it came but the android sdk now includes proguard which optimizes the code at build time and replaces the enums with int constants (cf. proguard.cfg).
Furthermore, the paragraph telling you not to use enums in the dev guide has been removed (http://developer.android.com/guide/practices/design/performance.html)
Related
I'm kinda new to Android development so my question might be weird or not even possible. I wouldn't know!
Anyway, I'm building multiple apps that will have a lot of shared elements, so I decided to build a library with those components and use it in all of the apps, rather than stupid copying and pasting code.
For example, the library handles the welcome screen and login/signup flow activities, among other things. So here are the problems this approach might cause:
While the behavior is the same across the apps, but the logo that I show at the welcome screen is different. Right now I populate it with an image resource from the library resources (R class) which will be the same for all apps and is obviously not correct.
The login/signup process is based on Firebase, which will require the app to have a key to be able to use them. Right now I also populate it with a dummy string resource from the library resources.
So my question really boils down to 3 parts:
Is there anyway I could pass this info from the app to the library? can I somehow modify the R class of the library? Or can I use the app's R class from the library? I can also call this part of the library as a function passing the parameters I need. But the first solution looks maybe more clean to me?
Whatever the answer to Q1 is. Where would I do this and how? The library has the welcome activity itself which is supposed to be the first activity in the app. How and where do I do this once the app starts and before the first activity starts?
If what I'm doing is wrong or impossible, is there any other way to achieve it?
Is there anyway I could pass this info from the app to the library?
can I somehow modify the R class of the library? Or can I use the
app's R class from the library? I can also call this part of the
library as a function passing the parameters I need. But the first
solution looks maybe more clean to me?
You don't need to modify the R class because you can override the resource file by creating a file with the same name. But it's not a clean solution because you constantly need to ensure your project and library resources name are the same.
Whatever the answer to Q1 is. Where would I do this and how? The
library has the welcome activity itself which is supposed to be the
first activity in the app. How and where do I do this once the app
starts and before the first activity starts?
Instead of overriding the resources name, you're better to modify your library to receive a configuration as a contract to use the library. Here the sample:
First, create the class for holding the configuration:
public class Configuration {
private int welcomeImageDrawableId;
private int logoDrawableId;
// constructor
public Configuration(int welcomeImageDrawableId, int logoDrawableId) {
this.welcomeImageDrawableId = welcomeImageDrawableId;
this.logoDrawableId = logoDrawableId;
}
// setter and getter.
public int getLogoDrawableId() {
return logoDrawableId;
}
}
Second, use the configuration class for the library by creating a Singleton class which will be used internally by the library:
public class MyLibrary {
private static MyLibrary myLibrary;
private Configuration configuration;
private MyLibrary(){}
private MyLibrary(Configuration configuration) {
this.configuration = configuration;
}
public static MyLibrary getInstance() {
if(myLibrary == null) {
throw new RuntimeException("Need call createInstanceWith method first!!");
}
return myLibrary;
}
public static MyLibrary createInstanceWith(Configuration configuration) {
if(myLibrary == null) {
synchronized(MyLibrary.class) {
if (myLibrary == null) {
myLibrary = new MyLibrary(configuration);
}
}
}
return test;
}
public Configuration getConfiguration() {
return configuration;
}
}
Third, use the configuration class in your library via the singleton class. something like this:
// assume imvLogo is an existing ImageView
Configuration configuration = MyLibrary.getInstance().getConfiguration();
imvLogo.setImageResource(configuration.getLogoDrawableId());
Last, register the contract when the library is used with:
Configuration configuration = new Configuration(R.drawable.welcome, R.drawable.logo);
MyLibrary.createInstanceWith(configuration);
Note: all the code isn't tested yet, error is to be expected.
Apart from the solution above, I also found another way to achieve this whole thing without having to initialize libraries and whatnot.
I think the correct way to do this is to use productFlavors in the library. This allows the library to share the one main set of source code, one main set of resources, then an extra set of resource per app/flavors. This is very sufficient for my purposes.
For more info about build variants and flavors:
https://developer.android.com/studio/build/build-variants
Why the R class in android is not static? when it contains all static content.
public final class R {
//static content
}
As per Java language policies, A top level public class cannot be static. And when you dive deep into the usage of static class you will find it is used to create independent inner class that does not hold anonymous reference of outer class. Therefore the purpose and use of static keyword before class is completely different.
non-static inner class
class A
{
int var1;
class B{
int calc(){
// can access A.var1 directly
}
}
}
static inner class
class A
{
int var1;
static class B{
int calc(){
// cannot access A.var1 directly, need object to be passed
}
}
}
R.java is the dynamically generated class, created during build process to dynamically identify all assets (from strings to android widgets to layouts), for usage in java classes in Android app. Note this R.java is Android specific (though you may be able to duplicate it for other platforms, its very convenient), so it doesn't have much to do with Java language constructs.
android.R.java is not just where XML ids are stored. It also contains access to resources - such as drawables, layouts, strings, arrays, and basically anything you can declare in resources.
Personally I find that it is useful when using Eclipse. I can simply type findViewById(R.id. and Eclipse will show a tooltip with a list of options to choose from.
However at a platform level, I would say that the hardcoded id variables help prevent errors when using Strings to identify resources -- something that can be debuggable while programming (or during compilation, rather than runtime).
I'm developing an android app implementing MVP and clean architecture. I have the following scenario:
One core module with presenters and view interfaces,...
One domain module with repositories, data sources,..
App module with the core implementation (so the Fragment/Activities).
Currently the strings.xml file is in the app module, but I'm thinking whether it should be in a commons module or not. The problem is that, sometimes, the presenter must set the text to the view, so the presenter should need to access to the strings.xml. I've thought in two possible solutions:
1) Create a TextHelper interface on core module that will be implemented on the app module and injected to the presenter, so the presenter will use this helper to get the strings it requires. (This is the solution I have implemented).
2) Move the strings.xml file to a common module so the file can be accessed from core module. But this solution would have a problem: the presenter doesn't have a context.
What do you think? What is the best approach?
Thanks in advance
If your view has nested if/elses related to strings, then they should probably be unit-tested. Therefore, that logic should stay in presenters or use-cases, where can be tested more quickly.
Your question is about how to retrieve the actual strings, given that they reside in the "outer layers" of the Clean Architecture scheme, i.e. in the Context object. IMHO your TextHelper is the right approach, as it allows to inject a mock when writing unit tests: you're interested in how the strings are processed, rather than how the strings actually look. I'm trying a very similar approach and calling it StringsRepository.
A point of uncertainty is how the the repository API should look like:
A single method like getString(#StringRes int stringResId, Object... formatArgs) that simply wraps Context.getString(): very simple to implement, but will make the presenters depend on your R.string class, which in turns requires strings.xml to be in the same module as your code under test;
One method per string with optional arguments, each one containing the reference to the appropriate string ID. This solution allows for best abstraction, but may become big (both the interface and the implementation...) and many domain classes may depend upon it. Handle with care.
Like (2), but with several classes, one per each part of your app. Each class may have a base class similar to (1) but with that method with protected visibility.
The best options for your case would be (2) or (3), but your mileage may vary.
You can use Application class to get the context any where from the app.
public class MVPApplication extends Application {
private static Context context;
public static Context getContext() {
return context;
}
#Override
public void onCreate() {
super.onCreate();
context = getApplicationContext();
}
}
In my current application, I am implementing localization. I came across various ways to access the string (external string). Some methods are:
Accessing directly using R.string.hello
Using getResourse().getString(R.string.hello)
getString.
The Eclipse tool to externalize resources, which creates a message.property file and access the string from a snippet.
The main confusion is between 1 and 2. If I use "1" that is accessing directly, can I get resources based on locale?
TL;DR
It doesn't matter whether you use R.string.hello or getResources().getString(R.string.hello).
Both will point the same thing.
So, yes, for your localization, then it will point to the right stuff.
It's been taken care of.
Some trivial stuff:
R.string.hello is actually an integer.
And most methods, that are used by passing R.string.hello (or other resources) as its argument, basically have the function to translate to corresponding string of the number.
Let's take TextView as an example.
It has two methods for setText.
First:
public final void setText(CharSequence text)
Second:
public final void setText(int resid)
So, when you call using getResources().getString():
myTextView.setText(getResources().getString(R.string.hello);
Then the first method is called.
And when you call using R.string.hello directly, then the second method is called.
If you take a look closer to the source, the second method's content (calling using R.string.hello directly) is actually calling the first method.
https://github.com/android/platform_frameworks_base/blob/master/core/java/android/widget/TextView.java#L4133
Both are provided for our convenience, so that we can use either direct resource R.string.hello or using getResources().getString(R.string.hello).
Implementation of Context method getString():
/**
* Return a localized string from the application's package's
* default string table.
*
* #param resId Resource id for the string
*/
public final String getString(int resId) {
return getResources().getString(resId);
}
So it's the same as using getResourse().getString(R.string.hello) (2), and it returns the result in the proper locale.
I am wondering about the getString().
I can see that doing getString(R.string.some_text) works. Also getResources().getString(R.string.connection_error) works.
So my question is why should we use the getString or when?
Thanks!
The question is easy to misinterpret.
If you are in a valid context (like an Activity), there is no difference, because the context has a reference to the resources, so it can resolve a getString(int); directly, which returns a String.
Adding more information for your peace of mind.
If you can use getString directly, go ahead and do it. Now sometimes you might need to use getResources() because it contains a lot of helper methods.
This is the Android source code for getResources.getString():
/**
* Return the string value associated with a particular resource ID. It
* will be stripped of any styled text information.
* {#more}
*
* #param id The desired resource identifier, as generated by the aapt
* tool. This integer encodes the package, type, and resource
* entry. The value 0 is an invalid identifier.
*
* #throws NotFoundException Throws NotFoundException if the given ID does not exist.
*
* #return String The string data associated with the resource,
* stripped of styled text information.
*/
public String getString(int id) throws NotFoundException {
CharSequence res = getText(id);
if (res != null) {
return res.toString();
}
throw new NotFoundException("String resource ID #0x"
+ Integer.toHexString(id));
}
Neat huh? :)
The truth is that the Resources object does a lot more than just "get strings", you can take a look here.
Now compare with the Activity version of getString():
Return a localized string from the application's package's default
string table.
So in summary, other than the fact that the Resources object will be stripped of any styled text information. and that the Resources object can do a lot more, the end result is the same. The Activity version is a convenient shortcut :)
The methods are same. Logically, there's not a difference. you can assume, it does exactly:
public final String getString(int resId) {
return getResources().getString(resId);
}
The only difference I know is that getResources() may be required to fetch other apps resources as object. getString() will access your own resources.
If you use it for TextView there are two methods setText() in it. One takes (CharSequence string) and another takes (int resId). That's why your both variants work.
Generally I would recommend to define all strings in strings.xml files and get them via getResources().getString(int resId) in the code. Having that approach you'll be able to easily localize your app. You can read more about app resources here
Very Basic difference.
R.string.some_text = return ID integer, identifying string resource in your space
getResources().getString(R.string.connection_error) = Will return you actualy string associated with ID `R.string.connection_error`
They both can be made use in Android system, where many of widgets can take directly id or value of a resource. Practically there is no difference in value returned only difference is is the Term Context, from you activity context is available to you hence calling getString directly routes to resources for this context, while from classes where context is not available say from a Adapter, you will need to first access the Context, then the resource associated with the context and at the end the String so you write getContext().getResources().getString(R.string.connection_error)
I hope it clears your confusion.
One good reason is about formating & styling like :(http://developer.android.com/guide/topics/resources/string-resource.html#FormattingAndStyling)