So far i've been writing my Android app just by typing names in to methods. I am now sorting this out, going through and putting these into string.xml instead and referencing the string using:
txt.setText(this.getString(R.string.string_name));
However, when trying to use this in a static context (in public static void), it does not work and gives an error.
Does anyone have any pointers of how to overcome this? I am fairly new to Java/Android programming and this is the first time I have come across this problem. Any help is much appreciated.
Extra code:
public static void ShowCatAddedAlert(Context con)
{
AlertDialog.Builder builder=new AlertDialog.Builder(con);
builder.setTitle("Add new Category");
builder.setIcon(android.R.drawable.ic_dialog_info);
DialogListner listner=new DialogListner();
builder.setMessage("Category Added successfully");
builder.setPositiveButton("ok", listner);
AlertDialog diag=builder.create();
diag.show();
}
Assuming that txt is a TextView, then you can just do txt.setText(R.string.string_name). You can usually reference to a string by it's resource id rather than getting it explicitly. More on that http://developer.android.com/guide/topics/resources/string-resource.html
String resources, as all resources, are resolved from the application from a Context instance (usually that's an Activity instance or the Application instance). In a static context, you don't have any instances unless you pass them in to your static methods.
One way or another, you need to do something in a non static context. Either you keep a copy of the Resources object around and pass it to your static methods, or you pass a Context instance around that is capable of resolving your resources, or you have a static Resources object that gets set at some point before your static methods get called.
That being said, you might want to revisit whether or not you absolutely need these methods to be static.
Related
I have a RecyclerView with its Adapter in a Fragment. Currently I'm hunting for OOM causes and Context leaking might be one of the cause.
There are several approaches I did in getting the context in Adapter (Need the Context for SharedPreferences, Glide/Picasso, and replacing Fragments).
Passing the Context through the adapter constructor and then set it into global variable inside the adapter :
LobbyAdapter lobbyAdapter = new LobbyAdapter(this.getActivity);
Have a global Context in the Adapter and take the Context from onCreateViewHolder :
context = parent.getContext();
This causes problem when I new the Adapter using SwipeRefreshLayout. But this must be because of the flawed logic I did in the Fragment, still tracking this down.
Don't make a global Context variable, but, get every Context from the View from every ViewHolder related to the Context
Loading an image
The key here is using the holder to get the Context ((FriendProfileViewHolder) holder).coverPhoto.getContext()
Glide.with(((FriendProfileViewHolder) holder).coverPhoto.getContext())
.load(utilities.webAddress + profileDataModel.user_cover_image_path)
.diskCacheStrategy(DiskCacheStrategy.ALL)
.skipMemoryCache(true)
.centerCrop()
.into(((FriendProfileViewHolder) holder).coverPhoto);
In this part, due to my lack of experience with Context, I'm not sure which view should we get the Context if one method is reused by different Views
.
Additional question : (This might need new question thread..)
In several Adapters, I do an AsyncTask to get response from server to change image. And I need Context in the Interface to do getPackageName() to get package of the app, and getResources() to access resources.
String pictureName = output.image_name_profile;
String packageName = context.getPackageName();
if(!pictureName.equals("default")){
resId = context.getResources().getIdentifier("drawable/" + pictureName, null, packageName);
image = context.getResources().getDrawable(resId);
}
Maybe I should create a global variable and method to mutate those values?
I've used the first approach that you described in your question, the one for passing Context through a constructor and then set it to global variable for later use. This approach works great for Fragments containing RecyclerViews. Used it for a couple of times and it has never caused a Context leak problem.
I've not used any of the other approaches so will not comment on them.
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.
This is a real noob question I'm sure, but I am finding it quite perplexing.
Why an earth would you want to ever use intent.putExtra method to share information between classes in Android?
Let me explain. I am making my first Android app following the instructions from the developers guide (I am already at a moderate level with Java) and I am using some code that looks like this:
//Class field
//key holds string????? not fully understanding this...
public static final String EXTRA_MESSAGE = "self.anon.myfirstapp.MESSAGE";
//this method is activated by a button being pressed
public void sendMessage(View view) {
Intent intent = new Intent(this,DisplayMessageActivity.class);
EditText editText = (EditText) findViewById(R.id.edit_message);
String message = editText.getText().toString();
//puts string message inside the string EXTRA_MESSAGE - why????
intent.putExtra(EXTRA_MESSAGE, message);
startActivity(intent);
}
OK firstly I want to point out I see what is happening and for the most part how it works (am just confused by the field declaration = "myClassPath" why?)...
BUT....
Surely it would be easier just to have a static field called:
public static String message;
then my method would look like this:
public void sendMessage(View view) {
Intent intent = new Intent(this,DisplayMessageActivity.class);
EditText editText = (EditText) findViewById(R.id.edit_message);
message = editText.getText().toString();
startActivity(intent);
}
Then when my class DisplayMessageActivity needs the string message he just calls for:
String message = myClass.message;
That seems so much more straight forward. What is with the creation of the new string EXTRA_MESSAGE which just seems to hold the string message and why send it with the intent when my other class can access this info directly anyway -- and what does the field declaration with the "self.anon.myfirstapp.MESSAGE" mean? I can find no such folder or path leading to anything.
As someone else stated there are often situations (such as a screen rotate) in which the android system destroys and restarts the app - so all variable data is lost. It would only work consistently the way you suggest if your data is hard coded as a final variable. That is not the only reason for using intents though.
The great thing with using an intent to pass information is that you can use the intent not just to communicate with sub-activities within your own application but to any activity installed on that android system. For example you may want to launch an intent which starts the phone application and include as an extra the number that you want to call.
Perhaps a better question than yours though is "why would you not use intents to pass information?" The intent.putExtra() method allows you a convenient flexible and straight forward method to pass as much information as you like in a safe and secure way to any other activity.
intent.putExtra(EXTRA_MESSAGE, message);
works like a key value pair, when you want to retrieve the information from the intent you can simply do intent.get<type>Extra and get said information, in this case, intent.getStringExtra("self.redway.myfirstapp.MESSAGE'). its simply the key to retrieve the information, it does not have to be your entire classpath.
it could just as easily be intent.putExtra("message",message).
They are helpful when passing information that you don't necessarily want to reveal to another class but you do want it to be able to get that information in another manner from what i have found.
message = myClass.message It is not always certain that this will retain its value especially when it extends Android framework classes like Activity. When your activity is recreated(change of screen orientation) then message can lose its current value and be assigned a default value. myClass.message would work if message was a static field or else you would need to provide getter and setter methods for object of the Activity Class. Well creating objects of activity class is unheard of in my experience.
Is there any way I can add items in code to a String-Array resource? For instance, if I want to create a spinner that shows the user values, and I want to allow the user to add their own custom values.
No. this is not supported because resources are packaged in the binary .apk and as such cannot be changed.
Don't follow this design pattern, change your approach.
probably JoxTraex has no idea on android framework or as they are saying:
when someone says this can't be done - there always is someone who doesn't know that and will do it :)
so to the point:
Resources is an open class is just a wrapper around ResourcesImpl class
(with depreciated constructor - but is available)
public Resources(AssetManager assets, DisplayMetrics metrics, Configuration config)
in app every call to it is made via Context
so:
1) you always can provide your own Resources implementation
2) or (when its enough) your own Context implementation (so sky is the limit)
... i now feel eyes on "Context implementation" - yeah you can even replace top most context in LoadedApk, Activity, etc.. yeah cool? but when, how? how: via reflections, when: good app designer knows that this place is where (before) first/any call to such object is made ...
But there is one dangerous catch here - in one case android straight unwrap Context to ContextImp and then you need to return those Context instead yours - where is the place that i'll keep as secret for those who like riddles :)
there is also in Resources
a) a hidden constructor (best entry point as it is not making any ReourceImpl (which can be set by method mentioned bellow)
public Resources(#Nullable ClassLoader classLoader)
b) hidden method
setResImpl(ResourcesImpl)
sum up:
so by calling
Resources.getStringArray(**not existing in APK resource id here**);
on our own implementation we could get whatcha want :)
even better we can add to our implementation
addResourceById(int,Object)
method and add new resources :) then with assign ability check on resource we will use and do a up casting to our implementation :) to use our newly added method :)
btw: if someone says to you "you shouldn't do it - blablabla" this is the best reason to do it! - if not permitted by law :)
enough the theory go to a practice:
example Context implementation forwards calls to getString:
public class MyContext extends Context {
....
// override context get resources method
#Override
public android.content.res.Resources getResources() {
// get super resources
android.content.res.Resources resources = super.getResources();
// pull assets
android.content.res.AssetManager assets = resources.getAssets();
// pull metrics
android.util.DisplayMetrics displayMetrics = resources.getDisplayMetrics();
// pull configuration
android.content.res.Configuration configuration = resources.getConfiguration();
// construct new anon resource implementation
return new android.content.res.Resources(assets, displayMetrics, configuration) {
// overrride interesting method
#android.support.annotation.NonNull
#Override
public String getString(int id) throws android.content.res.Resources.NotFoundException {
return id == pl.ceph3us.base.common.R.string.my_sweet_google
? "fck_you_google";
: super.getString(id);
}
};
}
}
This is really doing my head in, I have been following these instructions but it won't work. Step three is causing me trouble. I'm note sure exactly what needs to be added where. It says;
"In the onCreate() method of your app instance, save your context (e.g. this) to a static >field named app and create a static method that returns this field, e.g. getApp():"
But I only have this at the top of my main java file:
protected static final String App = null;
The error I get is on this line, it says "The method getContext() is undefined for the type String":
String[] items = App.getContext().getResources().getStringArray(testholderint);
I figure the issues is with not following step three, and was wondering what exactly I need to add.
Once I've got this rectified my project is basically finished...
I think this line
protected static final String App = null;
shouldn't appear at all. The link you provided tells you to create a subclass of your application, like
public class App extends Application { ... }
In this subclass you should put the onCreate() and getContext() methods indicated in the code provided at that link. After doing so your last line (String[] items...) should work just fine.