getString Поза межами контексту чи діяльності


260

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

Чи можливо використовувати getStringпоза ContextабоActivity ? Я думаю, що я міг би пройти в поточній діяльності, але це здається непотрібним. Будь ласка, виправте мене, якщо я помиляюся!

Редагувати: Чи можемо ми отримати доступ до ресурсів без використання Context?


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

Відповіді:


440

Так, ми можемо отримати доступ до ресурсів без використання "Контексту"

Ви можете використовувати:

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

... скрізь у вашій програмі, навіть у деклараціях статичних констант. На жаль, він підтримує лише системні ресурси .

Для місцевих ресурсів використовуйте це рішення . Це не банально, але працює.


17
що розуміється під системними ресурсами? strings.xml - це системний ресурс чи ні? Для мене це не працює, каже, не може знайти ресурс.
kirhgoff

6
Системні ресурси належать Android на пристрої. strings.xml належать лише вашій програмі. Подивіться на stackoverflow.com/a/4391811/715269 рішення
Гангнус

3
Це елегантне рішення для тих заводських класів при доступі до струн. Мені не подобається всюди проходити по Контексту. Це просто непотрібне захаращення для тих випадків, коли ми дійсно просто хочемо, щоб рядок зберігався в усьому світі.
Джей Снайдер

1
чи це більш ефективно, ніж передавати контекст класу та використовувати його ??
SoliQuiD

5
я отримую цю помилкуandroid.content.res.Resources$NotFoundException: String resource ID #0x7f0f0061
Ебрагім Карімі

108

На жаль, єдиний спосіб отримати доступ до будь-якого з рядкових ресурсів - це за допомогою Context(тобто, Activityабо Service). Що я зазвичай робив у цьому випадку, це просто вимагати від абонента пройти в контексті.


4
Дякую за пораду! Я просто спробував це, але чомусь у мене з’явилася помилка компіляції, коли я спробував:ctx.getString(ctx.R.string.blah);
SapphireSun

Я б зробив аргумент щодо типів Contextутилітів, щоб ви могли використовувати його як з діяльності, так і з сервісу.
MatrixFrog

2
Вам не потрібен ctx.R.string.blah, просто використовуйтеR.string.blah
Pentium10

2
Я не впевнений, звідки symbol not found errorпоходить, але переконайтеся, що ви Rімпортували понад клас.
Pentium10

11
Відповідь - ЛАЖНА. Віз наступного. :-)
Gangnus

33

В MyApplication, який поширюється Application:

public static Resources resources;

В MyApplication«з onCreate:

resources = getResources();

Тепер ви можете використовувати це поле з будь-якого місця вашої програми.


Чи буде це працювати через сервіс? (Особливо, коли Android вбиває додаток і запускає лише сервіс)
Atul

1
Так, Android починає виконувати ваш код, зателефонувавши Application.onCreate і після цього він запустить вашу послугу.
konmik

23

До речі, однією з причин виявлення помилки символу може бути те, що ваш IDE імпортував android.R; клас замість вашого. Просто змініть імпорт android.R; щоб імпортувати your.namespace.R;

Отже, дві основні речі, щоб отримати рядок видимим у різних класах:

//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)будь-де в додатку.


1
Мені особисто подобається такий підхід. Зручно отримувати користувацькі рядкові ресурси з будь-якої точки коду.
checkmate711

Чи є проблеми із безпекою щодо цього рішення?
nibbana

@ user1823280 Ні, я не думаю.
Хемрадж

Ідеальне рішення
Yasiru Nayanajith

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

Якщо я не помиляюсь, це дає вам гачок на applicationContext скрізь, зателефонуйте до нього. ApplicationContextSingleton.getInstance.getApplicationContext(); Вам не потрібно це робити в будь-який момент, оскільки коли програма закривається, це все одно відбувається.

Не забудьте оновити, 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"
    >

Будь ласка, дайте мені знати, якщо ви бачите щось не так, дякую. :)


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)

0

Якось не сподобалися хакі розв’язання зберігання статичних значень, тому придумали трохи довше, але чисту версію, яку також можна перевірити.

Знайдено 2 можливі способи зробити це -

  1. Передайте контекст.ресурси як параметр своєму класу, куди потрібно рядовий ресурс. Досить просто. Якщо передача парами неможлива, використовуйте сетер.

напр

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>

Діяльність / фрагмент-

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

Іноді вам потрібно змінити текст на основі поля в моделі. Таким чином, ви також прив'язуєте дану модель до цієї моделі, і оскільки ваша діяльність / фрагмент знає про модель, ви можете дуже добре отримати значення, а потім зв’язати дані на основі цього рядка.


0

Це можна зробити в Котліні , створивши клас, який розширює програму 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)

Це не спрацює, якщо змінити локальну програму під час виконання програми, оскільки контекст програми є однотонним та ініціалізується при запуску процесу.
Szörényi Ádám

-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); Це працює на мене.


14
Як ви думаєте getContext(), доступний скрізь ?!
Хамзе Собох

1
Це не дає відповіді на питання, оскільки getContext () доступний лише у класах з активами та фрагментами
Umar Ata
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.