I've found the R.string
pretty awesome for keeping hardcoded strings out of my code, and I'd like to keep using it in a utility class that works with models in my application to generate output. For instance, in this case I am generating an email from a model outside of the activity.
Is it possible to use getString
outside a Context
or Activity
? I suppose I could pass in the current 开发者_Go百科activity, but it seems unnecessary. Please correct me if I'm wrong!
Edit: Can we access the resources without using Context
?
Yes, we can access resources without using `Context`
You can use:
Resources.getSystem().getString(android.R.string.somecommonstuff)
... everywhere in your application, even in static constants declarations. Unfortunately, it supports the system resources only.
For local resources use this solution. It is not trivial, but it works.
Unfortunately, the only way you can access any of the string resources is with a Context
(i.e. an Activity
or Service
). What I've usually done in this case, is to simply require the caller to pass in the context.
In MyApplication
, which extends Application
:
public static Resources resources;
In MyApplication
's onCreate
:
resources = getResources();
Now you can use this field from anywhere in your application.
##Unique Approach
##App.getRes().getString(R.string.some_id)
This will work everywhere in app. (Util class, Dialog, Fragment or any class in your app)
(1) Create or Edit (if already exist) your Application
class.
import android.app.Application;
import android.content.res.Resources;
public class App extends Application {
private static App mInstance;
private static Resources res;
@Override
public void onCreate() {
super.onCreate();
mInstance = this;
res = getResources();
}
public static App getInstance() {
return mInstance;
}
public static Resources getRes() {
return res;
}
}
(2) Add name field to your manifest.xml
<application
tag.
<application
android:name=".App"
...
>
...
</application>
Now you are good to go. Use App.getRes().getString(R.string.some_id)
anywhere in app.
BTW, one of the reason of symbol not found error may be that your IDE imported android.R; class instead of yours one. Just change import android.R; to import your.namespace.R;
So 2 basic things to get string visible in the different class:
//make sure you are importing the right R class
import your.namespace.R;
//don't forget about the context
public void some_method(Context context) {
context.getString(R.string.YOUR_STRING);
}
The best approach from the response of Khemraj:
App class
class App : Application() {
companion object {
lateinit var instance: Application
lateinit var resourses: Resources
}
// MARK: - Lifecycle
override fun onCreate() {
super.onCreate()
instance = this
resourses = resources
}
}
Declaration in the manifest
<application
android:name=".App"
...>
</application>
Constants class
class Localizations {
companion object {
val info = App.resourses.getString(R.string.info)
}
}
Using
textView.text = Localizations.info
If you have a class that you use in an activity and you want to have access the ressource in that class, I recommend you to define a context as a private variable in class and initial it in constructor:
public class MyClass (){
private Context context;
public MyClass(Context context){
this.context=context;
}
public testResource(){
String s=context.getString(R.string.testString).toString();
}
}
Making an instant of class in your activity:
MyClass m=new MyClass(this);
You can do this in Kotlin by creating a class that extends Application and then use its context to call the resources anywhere in your code
Your App class will look like this
class App : Application() {
override fun onCreate() {
super.onCreate()
context = this
}
companion object {
var context: Context? = null
private set
}
}
Declare your Application class in AndroidManifest.xml (very important)
<application
android:allowBackup="true"
android:name=".App" //<--Your declaration Here
...>
<activity
android:name=".SplashActivity" android:theme="@style/SplashTheme">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".MainActivity"/>
</application>
To access e.g. a string file use the following code
App.context?.resources?.getText(R.string.mystring)
Inside Activity :
Text(text = getString(android.R.string.yes))
Inside Non-Activity :
Text(text = Resources.getSystem().getString(android.R.string.yes))
OR
Text(text = stringResource(android.R.string.yes))
This should get you access to applicationContext
from anywhere allowing you to get applicationContext
anywhere that can use it; Toast
, getString()
, sharedPreferences
, etc.
The Singleton:
package com.domain.packagename;
import android.content.Context;
/**
* Created by Versa on 10.09.15.
*/
public class ApplicationContextSingleton {
private static PrefsContextSingleton mInstance;
private Context context;
public static ApplicationContextSingleton getInstance() {
if (mInstance == null) mInstance = getSync();
return mInstance;
}
private static synchronized ApplicationContextSingleton getSync() {
if (mInstance == null) mInstance = new PrefsContextSingleton();
return mInstance;
}
public void initialize(Context context) {
this.context = context;
}
public Context getApplicationContext() {
return context;
}
}
Initialize the Singleton in your Application
subclass:
package com.domain.packagename;
import android.app.Application;
/**
* Created by Versa on 25.08.15.
*/
public class mApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
ApplicationContextSingleton.getInstance().initialize(this);
}
}
If I´m not wrong, this gives you a hook to applicationContext everywhere, call it with ApplicationContextSingleton.getInstance.getApplicationContext();
You shouldn´t need to clear this at any point, as when application closes, this goes with it anyway.
Remember to update AndroidManifest.xml
to use this Application
subclass:
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.domain.packagename"
>
<application
android:allowBackup="true"
android:name=".mApplication" <!-- This is the important line -->
android:label="@string/app_name"
android:theme="@style/AppTheme"
android:icon="@drawable/app_icon"
>
Please let me know if you see anything wrong here, thank you. :)
Following and based on Khemraj Sharma's answer, this is the Kotlin version, with an extra extention as a bonus:
class App: Application() {
override fun onCreate() {
super.onCreate()
mInstance = this
res = resources
}
companion object{
private var mInstance: App? = null
private var res: Resources? = null
fun getInstance(): App? {
return mInstance
}
fun getRes(): Resources? {
return res
}
}
}
Then we can create an extension:
fun Int.resourceToString(): String {
return App.getRes()?.getString(this) ?: "Not Found"
}
And use the extention directly on any resource id:
var asString = R.string.my_string.resourceToString()
Somehow didn't like the hacky solutions of storing static values so came up with a bit longer but a clean version which can be tested as well.
Found 2 possible ways to do it-
- Pass context.resources as a parameter to your class where you want the string resource. Fairly simple. If passing as param is not possible, use the setter.
e.g.
data class MyModel(val resources: Resources) {
fun getNameString(): String {
resources.getString(R.string.someString)
}
}
- Use the data-binding (requires fragment/activity though)
Before you read: This version uses Data binding
XML-
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="someStringFetchedFromRes"
type="String" />
</data>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{someStringFetchedFromRes}" />
</layout>
Activity/Fragment-
val binding = NameOfYourBinding.inflate(inflater)
binding.someStringFetchedFromRes = resources.getString(R.string.someStringFetchedFromRes)
Sometimes, you need to change the text based on a field in a model. So you would data-bind that model as well and since your activity/fragment knows about the model, you can very well fetch the value and then data-bind the string based on that.
If you want to use getString Outside of a Context or Activity you should have context in constructor or method parameter so that you can access getstring() method. Specially in Fragment You shoud ensure that getActivity() or getContext() are not providing null value. To avoid null from getActivity() or getContext() in a Fragment try this : Declare a variable :
Context mContext;
now override onAttach and onDetach method of Fragment :
@Override
public void onAttach(Context context) {
super.onAttach(context);
mContext = context;
}
@Override
public void onDetach() {
super.onDetach();
mContext = null;
}
Now Use mContext whereever you are using getString() method. ex:
Toast.makeText(mContext, mContext.getString(R.string.sample_toast_from_string_file), Toast.LENGTH_SHORT).show();
If you are using Hilt, you can actually inject the context:
@Module
@InstallIn(SingletonComponent::class)
interface ResourceProvider {
companion object {
@Provides
@Singleton
@MyQualifier
fun providesBaseUrl(@ApplicationContext context: Context): String = with(context) {
getString(R.string.my_value)
}
}
}
- AppCompatActivity
- Companion object
Activity code:
class MainActivity : AppCompatActivity() {
companion object {
lateinit var instance: AppCompatActivity
private set
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
instance = this
}
}
Getting a resource from AnyWhere:
val text = MainActivity.instance.getString(R.string.task_1)
I used
getContext().getApplicationContext().getString(R.string.nameOfString);
It works for me.
精彩评论