I'm implementing internationalization for my application. The main part of the internationalization is supporting multi-languages.
One approach for supporting multi-language is, creating multiple values directories under the res/ directory and having strings.xml for the corresponding languages. Example here.
But my requirement is something like this:
The user enters his credentials to login to the application. Based on the language selected while creating an account on this app, the user would have selected a language.
So, on successful login, i'll be making a call to a service that will be returning all the strings in the application. And dynamically i must be associating these string to the labels in the application.
How can the above thing be done efficiently?
One approach that i have thought is, make a call to the service on successful login and store all the information on the Shared Preferences. and then use it.
Is there any other way to do this?
How do i change the text in cases of the xml layout files having android:text=""?
Please share your views regarding the same.
Take a look at Change language programmatically in Android . Whatever you do, you should use Android standard way (resources) instead of reinventing the wheel.
Update:
Due to your strange constraints, if you decide to reinvent the wheel, you could for example create derived classes using the TAG field of the views, something like:
public class LocalizableTextView extends TextView {
public LocalizableTextView(Context ctx, AttributeSet attrs) {
super(ctx, attrs);
setText(MyLocalizableStuff.get(this.getTag());
}
}
and create a static helper class MyLocalizableStuff like this: (needs error checking, etc, just typed out of my head)
public static class MyLocalizableStuff {
private static HashMap<Integer,String> sStringTable=new HashMap<>();
public static String get(Object code) {
Integer intCode=Integer.valueOf((String)code);
String result=sStringTable.get(intCode);
return result;
}
public static void init(Context ctx) {
// read your strings and store them on the stringtable
// you will call this init from onCreate like
// MyLocalizableStuff.init(context)
}
}
This way, you can insert LocalizableTextViews in your XML and assign a (numeric) TAG code that will map to the String and in construction time, will be assigned to the TextView. You could also use Strings as the code, but bear in mind that the HashMap will then be slower.
You could also use a SparseArray to store the string table instead of a HashMap, it will be probably faster.
But again, I wouldn't go this route.
Related
How can I passs multiple strings into custom views constructor in android
public DrawSomethingView(Context context, String originalBgPath, String pattern) {}
and why it forces to do it like that ?
public DrawSomethingView(Context context, AttributeSet S){
You need the standard constructors so the framework knows how to inflate them, e.g. from an XML layout. If you put a DrawSomethingView in your layout, how would it know what to pass for originalBgPath etc? You also need a Context and AttributeSet at a minimum to call one of the View superclass's constructors. You can read more about it here
That link also tells you how to create your own attributes, that you can add to the XML for your custom view, and pull out the data in the class itself. So you could add your strings as part of the XML definition. If you want to set them programmatically instead, you'll need to add some properties you can set (which you could also set when you read those attributes).
But there's no way to force them to be set, since you can't create your own version of the constructor that requires them (and the framework wouldn't have values to provide anyway). So you'll need to handle the possibility they're missing (e.g. nullable properties, default values), and maybe write a builder function that you can call from your code, that does require those values and creates and sets up the custom view before passing it back to you.
Let's say I have a layout like this:
Creating the layout is not complicated, if the questions are fixed. But my requirement is to display the questions from database, like this:
As you can see, there are 4 sectionIds. That means we have 4 categories. I'm thinking to use LinearLayout for this. Then for each categories, we have different amount of questions. If the question type is R, use RatingBar. But if the type is D, then use TextArea. I also plan to use LinearLayout for each questions. Now the challenge is creating those layout dynamically, which I think is not that easy. What's the least complicated way to do this?
I have had to do this in several opportunities, so I wish I can give a tip or more:
If your problem is not complex, look for something that has already solved it:
This library create a layout based on Firebase data
This library is for creating forms
Most of the times, any library will work because programmatic views are tight to specific requirements, so you will have to go on your own.
Customized Solution
Creating programmatic views it has a skew learning curve but with time you will be able to solve it.
Defining your fields:
Create fields or partial package and put those classes inside. You want to create a class for each type of field so you can reuse easily and modify it is done in that class.
Also, define what is common for every field. You can do this with an interface:
interface FormField {
String result();
boolean isValid();
void setError();
}
In this case, this interface will allow you to handle the result of the field, know if it is valid and set the errors. Validations should be inner once are requested, and error should be settable do internal validations and from outside.
The result you are getting can change by specific methods in other classes but having a common String is most of the time useful even for showing a summary to the user.
There is another benefit, you can create a list of your fields by your interface, every fields implement it:
List<FormFields> fields = new ArrayList();
//fields.add(ANY CLASS THAT IMPLEMENTS THE INTERFACE);
Creating a Field
Start by creating a class that extends a suitable view for your field:
public class InputText extends EditText implements FormFiel {
//You can simply add customizations in the constructor
}
public class InputText extends LinearLayout implements FormField {
//Maybe you need an input with a label, hence a TextView and an EditText will go inside of this
}
The rule of thumb for this task about constructor is one param for java two params for xml. So wherever you need to put the view, you use either one of those.
Handling Field appearance
If you need something simple maybe just set everything in the constructor.
public class InputText extends EditText implements FormField {
//You can simply add customizations in the constructor
public InputText(Context context) {
super(context)
setLayoutParams(new LayoutParams(LayoutParams.WrapContent...)
}
}
if you need something more complex use the layout inflater.
public class InputText extends LinearLayout implements FormField {
//You can simply add customizations in the constructor
public InputText(Context context) {
super(context)
LayoutInflater layoutInflater = LayoutInflater.from(getContext())
//You have to create the layout, a neat trick is create it inside a linear layout eand then use refactor/extract
//The last boolean in the method attach it to the view
layoutInflater.inflate(R.layout.field_input_text, this, true;)
//Maybe you want to find something, this could be a field variable
TexteView tv = this.findViewbyId(R.id.inside_the_inflated_layout)
}
}
Define a model
You need to define a model for all the field types
public class FireField {
private String label, hint, type;
// Empty constructor and getters and setters
}
Create a Container class
The container class probably is a linear layout with orientation vertical, you want to create it anyway and added in the xml (remember 2 params constructor) so you can add inside that view a way to get all your fields, a special method for that.
It can be a list of fields, it can be the list of the data you need to send.
This way, fiedls take care of their own logic and form take care of the general logic.
Add the views
You have to fetch your data then do a loop and for each type of the data, add a new view, this is when the List<FormField> of views come in handy, following is pseudo code
List<FireField> data = new ArrayList();
//You can also have String, View map here, where the key is the type and the value View is your field
List<FormField> fields = new ArrayList();
for (DataSnapshot children: snapshot.getChildren()) {
FireField field = children.getValue(FireField.class);
data.add(field);
if (field.getType.equals("INPUT_TEXT)) {
//Here Im addinf the field in the constructor, then the view should take care of it
new InputText(context, field)
//Here I'm initializing the view, the view inside that method should set labels and other
InputText input = new InputText(this);
input.initialize(field);
fields.add(input);
//You can add it here or in other place re using some of the lists above
container.addView(input);
}
}
Finally use any of the list and the container method to get the data and send it to Firebase
Everything I've read about Intents talks about using them to push data, or to start one Activity from another Activity. I want to pull data from an Activity that's already running.
The Tab Layout tutorial at http://developer.android.com/resources/tutorials/views/hello-tabwidget.html illustrates what I want to do. (My app is doing some engineering calculations instead, but the tutorial code provides a good analogy to my app.) The tutorial creates an app with three tabs, and each tab hosts a separate activity.
To expand on the example in the tutorial, suppose I select an artist in the Artists tab/activity. I want to be able to select the Albums tab/activity and have it display all the albums featuring that artist.
It seems to me that I need to use an Intent to do this. All of the tutorials I've found assume that I would create a "See albums" Button in the Artists tab/activity, and that pressing the Button would execute an Intent that starts the Albums activity and passes artistName.
I DO NOT want to create that Button. Real estate on the Artists layout is precious, and I have a perfectly good Albums tab, AND the HelloTabWidget activity already contains an intent to create the Albums tab.
Besides, a user will want to skip back and forth between Album and Artist in order to change artist selections, and the tabs are a perfectly good way to do this. There's no need to complicate the UI with another button.
So how can I have the Albums activity PULL artistName from the Artists activity when the Albums tab is selected (or the Albums layout is displayed), rather than have the Artists activity START Albums and PUSH the artistName?
Equivalents I can think of from other programming worlds:
Global variables. Discouraged in Android devt, right? And if they do exist, what are they called?
A getter, like artistName = Artists.getArtistName(); . I get the feeling that it's not that easy.
Writing to, and reading from, a file - that is, mass storage or non-volatile memory. I don't need the artistName value to be permanent. It will be reset to null every time the user launches the application.
So how is it done in the Android world? Do I use an Intent - and if so, how?
Global variables were the right answer.
I thought Java discouraged their use, but a couple of links that appeared in the "Related" links on the right margin of this window mentioned them directly. One was "Android: How to declare global variables?" and the other was "how to pass value betweeen two tab in android". Both pointed to the Application Class as the place to define global variables and methods. Armed with this new knowledge, I found an article called "Android Application Class" on the Xoriant blog that expanded on the StackOverflow answers.
It's best to review those three links first. I need to add some tips to what those authors have said.
Your Application class has to be in its own separate file. (That might be a "duh" to some people, but not to everybody.) Here's a good framework for an example called Something.java:
public class Something extends Application {
// Put application wide (global) variables here
// Constants are final, so they don't have to be private
// But other variables should be declared private;
// use getters/setters to access them
public final boolean FEET = false;
public final boolean METERS = true;
private boolean units = FEET;
#Override
public void onCreate() {
super.onCreate();
// Put any application wide (global) initialization here
}
// Put application wide (global) methods here
public boolean getUnits() {
return units;
}
public void setUnits(boolean whichOne) {
units = whichOne;
}
}
I'm using Eclipse with the ADT plug-in, in Windows XP. Eclipse doesn't always behave properly if you edit XML code directly, so it's best to open AndroidManifest.xml, then select the Application tab and enter your application name in the Name field. You don't need to put a dot or period in front of the name. Just type in the name of your class, like "Globals" or "MyApplication" or whatever. (Note that this is the default application in your Manifest. You don't have to create a separate <application></application> tag.
This step may not be necessary on an actual Android device, but it was necessary for the emulator: you need to use the getApplicationContext() command in every onCreate() and every method that will be accessing the global variables and methods. I tried to put it outside of onCreate() with the rest of my activity wide variables, and it didn't work. Putting it inside every method seems wasteful, but both the emulator and the Android device work fine with it that way. Here's a sample showing how I used it:
public void fooBar() {
// Access to global variables and methods
final Something s = (Something)getApplicationContext();
// ...
// This next line demonstrates both a global method and a global variable
if (s.getUnits() == s.FEET) {
// do something with feet
} else {
// do something with meters instead
}
// ...
}
Those were the only hiccups I encountered. The three references that I have listed, taken together, are otherwise pretty complete.
I have a number of objects which are fairly simple data style classes with many attributes with getters and setters for each attribute... an example might be along the lines of:
private employee ()
{
private String firstName();
private String secondName();
private boolean disabled();
private int yearsInService();
//various Getters and Setters
public String getFirstName();
public void setFirstName(String name);
public boolean getDisabled();
public void setDisabled(boolean value);
}
These classes are populated via OrmLite/SQLIte
Some of these classes have 30 or 40 attributes.
My intention is to display these attributes in a grid/list of some kind to the user - ideally something with a description column and a value column, and that the user be able to select any particular attribute modify the value. Possibly like the Preferences screen only these aren't preferences?
I've done a fair amount of searching and I'm not really getting a feel for the best solution at the moment. Should I be using Listview? It seems complex to get something like this set up.
If you want to use the ListView OR Grid for INLINE Editing you may experience certain issues like discussed here.
So the best option is to populate the data in a ListView & when the
user clicks to edit it, display a dialogue to them & provide the
EDITING functionality there.
i have created a live wallpaper and in that there is a "setting" button which loads PreferenceActivity but without clicking on "settings" but i want to access the SharedPreferences within subclass of Engine or WallpaperService. As i just want to access the small single string so i don;t want user to go into settings and access that string.
So i want to execute this code inside Subclass of Engine or WallpaperSerivce
SharedPreferences mPrefs = getPreferenceManager().getSharedPreferences();
String option = mPrefs.getString(
this.getResources().getString(R.string.name),
this.getResources().getString(R.string.option));
It is not the best way to do it I'm sure but I use getters and setters to achieve this effect.
private int mySetting = defaultvalue
public int getMySetting() {
return mySetting;
}
public void setMySetting(int mySetting) {
this.mySetting = mySetting;
}
I obviously used some plain text in that code but hopefully it is pretty self explanitory
You set this variable while in Settings Class with...
Settings.this.setMySetting(value);
Remove "this" to call from other classes
You can retieve this information in any of your classes using the following
Settings.getMySetting();
You can use pretty much any variable type just make sure you define the mySetting variable as that type before trying to pass a value other than int as in this example. Hopefully this helps.