getString вне контекста или действия

260

Я нашел R.stringдовольно удивительным то, что я не могу использовать жестко запрограммированные строки в своем коде, и я хотел бы продолжать использовать его в служебном классе, который работает с моделями в моем приложении для генерации вывода. Например, в этом случае я генерирую электронное письмо из модели вне действия.

Можно ли использовать getStringснаружи ContextилиActivity ? Полагаю, я мог бы перейти к текущей деятельности, но это кажется ненужным. Пожалуйста, поправьте меня, если я ошибаюсь!

Изменить: Можем ли мы получить доступ к ресурсам без использования Context?

SapphireSun
источник
4
Передавая контекст классу, который будет использовать строку, вы также передаете информацию о том, какой язык (en, es и т. Д.) Используется приложением. Так что, если у вас есть два файла strings.xml, он будет знать, какой из них использовать
спортивный

Ответы:

441

Да, мы можем получить доступ к ресурсам, не используя `Context`

Ты можешь использовать:

Resources.getSystem().getString(android.R.string.somecommonstuff)

... везде в вашем приложении, даже в объявлениях статических констант. К сожалению, он поддерживает только системные ресурсы .

Для локальных ресурсов используйте это решение . Это не тривиально, но работает.

Гангнус
источник
17
что подразумевается под системными ресурсами? strings.xml - это системный ресурс или нет? Для меня это не работает, говорит, что не могу найти ресурс.
Кирхгофф
6
Системные ресурсы принадлежат Android на устройстве. strings.xml принадлежит только вашему приложению. Ищите решение stackoverflow.com/a/4391811/715269
Gangnus
3
Это элегантное решение для тех классов Factory при доступе к строкам. Мне не нравится проходить контекст везде. Это просто ненужный беспорядок в тех случаях, когда мы действительно хотим, чтобы строка хранилась глобально.
Джей Снайдер,
1
это более эффективно, чем передавать контекст в класс и использовать его?
SoliQuiD
5
я получаю эту ошибкуandroid.content.res.Resources$NotFoundException: String resource ID #0x7f0f0061
Эбрахим Карими
108

К сожалению, единственный способ получить доступ к любому из строковых ресурсов - с помощью Context(то есть Activityили Service). Что я обычно делал в этом случае, так это просто требовал, чтобы вызывающая сторона передавала контекст.

Эрих Дуглас
источник
4
Спасибо за чаевые! Я только что попробовал это, но по какой-то причине я получил ошибку компиляции, когда я попытался:ctx.getString(ctx.R.string.blah);
SapphireSun
Я бы привел аргумент к методам util типа, Contextчтобы вы могли использовать его из Activity или Service.
MatrixFrog
2
Вам не нужно ctx.R.string.blah, просто используйтеR.string.blah
Pentium10
2
Я не уверен, откуда symbol not found errorприходит, но убедитесь, что вы Rимпортировали в верхней части класса.
Pentium10
11
Ответ ЛОЖЬ. Виз следующий. :-)
Гангнус
33

В MyApplicationкоторый распространяется Application:

public static Resources resources;

В MyApplicationх onCreate:

resources = getResources();

Теперь вы можете использовать это поле в любом месте вашего приложения.

konmik
источник
Будет ли это работать через сервис? (Особенно, когда Android убивает приложение и запускает только сервис)
Atul
1
Да, Android начинает выполнять ваш код, вызывая Application.onCreate и после этого запускает ваш сервис.
Конмик
23

Кстати, одной из причин ошибки символа не может быть то, что ваша IDE импортировала android.R; класс вместо твоего. Просто измените импорт android.R; чтобы импортировать your.namespace.R;

Итак, 2 основных вещи, чтобы сделать строку видимой в другом классе:

//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);
}
Ян Нарушкевич
источник
19

Уникальный подход

App.getRes().getString(R.string.some_id)

Это будет работать везде в приложении. ( Класс Util, диалог, фрагмент или любой класс в вашем приложении )

(1) Создайте или отредактируйте (если уже существует) свой Applicationкласс.

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 getResourses() {
        return res;
    }

}

(2) Добавьте поле имени к вашему manifest.xml <applicationтегу.

<application
        android:name=".App"
        ...
        >
        ...
    </application>

Теперь вы готовы идти. Используйте в App.getRes().getString(R.string.some_id)любом месте приложения.

Khemraj
источник
1
Мне лично нравится такой подход. Удобно получать пользовательские строковые ресурсы из любого места кода.
checkmate711
Есть ли проблемы с безопасностью с этим решением?
ниббана
@ user1823280 Нет, я не думаю.
Хемрай
Идеальное решение
Ясиру Наянаджит
4

Если у вас есть класс, который вы используете в действии, и вы хотите иметь доступ к ресурсам в этом классе, я рекомендую вам определить контекст как частную переменную в классе и инициализировать его в конструкторе:

public class MyClass (){
    private Context context;

    public MyClass(Context context){
       this.context=context;
    }

    public testResource(){
       String s=context.getString(R.string.testString).toString();
    }
}

Сделайте мгновение класса в своей деятельности:

MyClass m=new MyClass(this);
Малус Ян
источник
0

Это должно дать вам доступ applicationContextиз любого места, что позволит вам получить доступ к любому applicationContextместу, которое может его использовать; Toast, getString(), sharedPreferencesИ т.д.

Синглтон:

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;
    }

}

Инициализируйте Singleton в своем Applicationподклассе:

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);
    }
}

Если я не ошибаюсь, это дает вам ApplicationContextSingleton.getInstance.getApplicationContext(); повод для приложенияContext повсюду, вызывайте его с помощью Вам не нужно очищать это в любой момент, так как когда приложение закрывается, это все равно идет с этим.

Не забудьте обновить AndroidManifest.xml чтобы использовать этот Applicationподкласс:

<?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"
    >

Пожалуйста, дайте мне знать, если вы видите здесь что-то не так, спасибо. :)

Versa
источник
0

Лучший подход из ответа Хемрая:

Класс приложения

class App : Application() {

    companion object {
        lateinit var instance: Application
        lateinit var resourses: Resources
    }


    // MARK: - Lifecycle

    override fun onCreate() {
        super.onCreate()
        instance = this
        resourses = resources
    }

}

Декларация в манифесте

<application
        android:name=".App"
        ...>
</application>     

Класс констант

class Localizations {

    companion object {
        val info = App.resourses.getString(R.string.info)
    }

}

С помощью

textView.text = Localizations.info
Микаэль Белхассен
источник
0

Лучше использовать что-то подобное без контекста и активности :

Resources.getSystem().getString(R.string.my_text)
reza_khalafi
источник
0

Каким-то образом не понравились хакерские решения для хранения статических значений, поэтому мы получили немного более длинную, но чистую версию, которая также может быть протестирована.

Найдены 2 возможных способа сделать это

  1. Передайте context.resources в качестве параметра вашему классу, где вы хотите получить строковый ресурс. Довольно просто. Если передача в качестве параметра невозможна, используйте установщик.

например

data class MyModel(val resources: Resources) {
    fun getNameString(): String {
        resources.getString(R.string.someString)
    }
}
  1. Используйте привязку данных (хотя требуется фрагмент / активность)

Прежде чем читать: эта версия использует 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>

Деятельность / Fragment-

val binding = NameOfYourBinding.inflate(inflater)
binding.someStringFetchedFromRes = resources.getString(R.string.someStringFetchedFromRes)

Иногда вам нужно изменить текст на основе поля в модели. Таким образом, вы бы также привязали данные к этой модели, и поскольку ваша деятельность / фрагмент знает о модели, вы можете очень легко получить значение и затем привязать данные к строке на основе этого.

Раджкиран
источник
0

Вы можете сделать это в Kotlin , создав класс, расширяющий Application, а затем использовать его контекст для вызова ресурсов в любом месте вашего кода.

Ваш класс приложения будет выглядеть так

 class App : Application() {
    override fun onCreate() {
        super.onCreate()
        context = this
    }

    companion object {
        var context: Context? = null
            private set
    }
}

Объявите свой класс приложения в AndroidManifest.xml (очень важно)

<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>

Например, для доступа к строковому файлу используйте следующий код

App.context?.resources?.getText(R.string.mystring)
Ахмед Раза
источник
Это не будет работать, если вы измените языковой стандарт программно во время выполнения, поскольку контекст приложения является одноэлементным и инициализируется при запуске процесса.
Сореньи Адам
-2

Вот что я сделал. В вашей MainActivity создайте статическую переменную для контекста, как показано ниже:

public static Context mContext;

и в onCreate () инициализируйте mContext для этого;

mContext = this;

Затем в файле, где вы хотите получить доступ к контексту, скажем,

private Context context = MainActivity.mContext;

Теперь вы можете получить строковый ресурс следующим образом,

String myString = context.getResources().getString(R.string.resource_id);
Сохам Чари
источник
-8

Я использовал getContext().getApplicationContext().getString(R.string.nameOfString); это работает для меня.

vivynz
источник
14
Как вы думаете getContext(), доступно везде ?!
Хамзех Собох
1
Это не дает ответа на вопрос, потому что getContext () доступен только в классах
Умар Ата,