getString poza kontekstem lub działaniem

260

Uważam, że jest to R.stringniesamowite, jeśli chodzi o trzymanie zakodowanych ciągów z dala od mojego kodu i chciałbym nadal używać go w klasie narzędzi, która współpracuje z modelami w mojej aplikacji do generowania danych wyjściowych. Na przykład w tym przypadku generuję wiadomość e-mail z modelu poza działaniem.

Czy można używać getStringpoza ContextlubActivity ? Przypuszczam, że mógłbym przekazać obecną aktywność, ale wydaje się to niepotrzebne. Proszę popraw mnie jeżeli się mylę!

Edycja: Czy możemy uzyskać dostęp do zasobów bez użycia Context?

SapphireSun
źródło
4
Przesyłając kontekst do klasy, która ma używać ciągu, przekazujesz również informacje o tym, jakiego języka (en, es itp.) Używa aplikacja. Więc jeśli masz dwa ciągi.xml, będzie wiedział, którego użyć
sport

Odpowiedzi:

440

Tak, możemy uzyskać dostęp do zasobów bez użycia „Kontekstu”

Możesz użyć:

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

... wszędzie w twojej aplikacji, nawet w deklaracjach stałych statycznych. Niestety obsługuje tylko zasoby systemowe .

W przypadku zasobów lokalnych skorzystaj z tego rozwiązania . To nie jest trywialne, ale działa.

Gangnus
źródło
17
co oznaczają zasoby systemowe? strings.xml jest zasobem systemowym czy nie? Dla mnie to nie działa, mówi, że nie można znaleźć zasobu.
kirhgoff
6
Zasoby systemowe należą do Androida na urządzeniu. strings.xml należy tylko do Twojej aplikacji. Poszukaj rozwiązania stackoverflow.com/a/4391811/715269
Gangnus
3
Jest to eleganckie rozwiązanie dla tych klas Factory podczas uzyskiwania dostępu do ciągów. Nie lubię przekazywać kontekstu wszędzie. To po prostu niepotrzebny bałagan w przypadkach, w których naprawdę chcemy, aby ciąg był przechowywany globalnie.
Jay Snayder,
1
czy jest to bardziej efektywne niż przekazywanie kontekstu do klasy i używanie go?
SoliQuiD
5
dostaję ten błądandroid.content.res.Resources$NotFoundException: String resource ID #0x7f0f0061
Ebrahim Karimi
108

Niestety, jedynym sposobem na uzyskanie dostępu do któregokolwiek z ciągów znaków jest Contextużycie znaku (tj. ActivityLub Service). To, co zwykle robiłem w tym przypadku, to po prostu wymagać od dzwoniącego przekazania w kontekście.

Erich Douglass
źródło
4
Dzięki za wskazówkę! Właśnie próbowałem tego, ale z jakiegoś powodu wystąpił błąd kompilacji, gdy próbowałem:ctx.getString(ctx.R.string.blah);
SapphireSun
Zrobiłbym argument na temat metod typu Contextuse, abyś mógł użyć go z działania lub usługi.
MatrixFrog,
2
Nie potrzebujesz ctx.R.string.blah, po prostu użyjR.string.blah
Pentium10,
2
Nie jestem pewien, skąd symbol not found errorpochodzi, ale upewnij się, że Rzaimportowałeś na szczycie klasy.
Pentium10,
11
Odpowiedź brzmi FAŁSZ. Vis the next one. :-)
Gangnus
33

W MyApplication, która rozciąga się Application:

public static Resources resources;

W MyApplication„s onCreate:

resources = getResources();

Teraz możesz używać tego pola z dowolnego miejsca w aplikacji.

konmik
źródło
Czy działałoby to za pośrednictwem usługi? (Szczególnie, gdy Android zabija aplikację i uruchamia tylko usługę)
Atul
1
Tak, Android zaczyna wykonywać kod, wywołując aplikację Application.onCreate, a następnie uruchamia usługę.
konmik
23

BTW, jednym z powodów błędu nie znaleziono symbolu może być to, że IDE zaimportował android.R; klasa zamiast twojej. Wystarczy zmienić import android.R; aby zaimportować twoją.namespace.R;

Więc 2 podstawowe rzeczy, aby łańcuch był widoczny w innej klasie:

//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);
}
Jan Naruszkiewicz
źródło
19

Unikalne podejście

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

Będzie to działać wszędzie w aplikacji. ( Użyj klasy, okna dialogowego, fragmentu lub dowolnej klasy w swojej aplikacji )

(1) Utwórz lub edytuj (jeśli już istnieje) swoją Applicationklasę.

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) Dodaj pole nazwy do manifest.xml <applicationtagu.

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

Teraz możesz już iść. Używaj w App.getRes().getString(R.string.some_id)dowolnym miejscu w aplikacji.

Khemraj
źródło
1
Osobiście podoba mi się to podejście. Wygodne jest uzyskiwanie niestandardowych zasobów ciągów z dowolnego miejsca w kodzie.
checkmate711,
Czy z tym rozwiązaniem występują problemy związane z bezpieczeństwem?
nibbana
@ user1823280 Nie, nie sądzę.
Khemraj
Idealne rozwiązanie
Yasiru Nayanajith
4

Jeśli masz klasę, której używasz w działaniu i chcesz mieć dostęp do zasobu w tej klasie, zalecam zdefiniowanie kontekstu jako zmiennej prywatnej w klasie i zainicjowanie go w konstruktorze:

public class MyClass (){
    private Context context;

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

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

Chwila lekcji w swojej działalności:

MyClass m=new MyClass(this);
Malus Jan
źródło
0

Powinno to zapewnić Ci dostęp applicationContextz dowolnego miejsca, umożliwiając dostęp do dowolnego applicationContextmiejsca, w którym można z niego korzystać; Toast, getString(), sharedPreferences, Itd.

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

}

Zainicjuj Singleton w swojej Applicationpodklasie:

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

Jeśli się nie mylę, daje to haczyk do aplikacji Kontekst wszędzie, zadzwoń z nim ApplicationContextSingleton.getInstance.getApplicationContext(); Nie musisz tego usuwać w żadnym momencie, ponieważ kiedy aplikacja się zamyka, to i tak się dzieje.

Pamiętaj o aktualizacji, AndroidManifest.xmlaby użyć tej Applicationpodklasy:

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

Daj mi znać, jeśli zauważysz coś nie tak, dziękuję. :)

Versa
źródło
0

Najlepsze podejście z odpowiedzi Khemraj:

Klasa aplikacji

class App : Application() {

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


    // MARK: - Lifecycle

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

}

Deklaracja w manifeście

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

Klasa stałych

class Localizations {

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

}

Za pomocą

textView.text = Localizations.info
Mickael Belhassen
źródło
0

Lepiej jest użyć czegoś takiego bez kontekstu i aktywności :

Resources.getSystem().getString(R.string.my_text)
reza_khalafi
źródło
0

Jakoś nie podobały się pospieszne rozwiązania przechowywania wartości statycznych, więc wymyśliłem nieco dłuższą, ale czystą wersję, którą również można przetestować.

Znaleziono 2 możliwe sposoby, aby to zrobić-

  1. Przekaż kontekst.resources jako parametr do swojej klasy, w której chcesz zasób łańcucha. Dość proste. Jeśli podanie jako param nie jest możliwe, użyj setera.

na przykład

data class MyModel(val resources: Resources) {
    fun getNameString(): String {
        resources.getString(R.string.someString)
    }
}
  1. Użyj powiązania danych (wymaga jednak fragmentu / aktywności)

Zanim przeczytasz: Ta wersja używa 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>

Aktywność / fragment

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

Czasami musisz zmienić tekst na podstawie pola w modelu. Więc powiążesz również ten model, a ponieważ twoja aktywność / fragment wie o modelu, możesz bardzo dobrze pobrać wartość, a następnie powiązać dane na podstawie tego łańcucha.

Rajkiran
źródło
0

Możesz to zrobić w Kotlinie , tworząc klasę rozszerzającą aplikację, a następnie wykorzystując jej kontekst do wywoływania zasobów w dowolnym miejscu w kodzie

Twoja klasa aplikacji będzie wyglądać tak

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

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

Zadeklaruj klasę aplikacji w AndroidManifest.xml (bardzo ważne)

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

Aby uzyskać dostęp np. Do pliku ciągów, użyj następującego kodu

App.context?.resources?.getText(R.string.mystring)
Ahmed Raza
źródło
Nie zadziała to, jeśli programowo zmienisz ustawienia regionalne w czasie wykonywania, ponieważ kontekst aplikacji jest singletonem i jest inicjowany podczas uruchamiania procesu.
Szörényi Ádám
-2

Oto, co zrobiłem: w swojej MainActivity utwórz zmienną statyczną dla kontekstu, jak pokazano poniżej:

public static Context mContext;

oraz w onCreate () zainicjuj mContext do tego;

mContext = this;

Następnie w pliku, w którym chcesz uzyskać dostęp do kontekstu, powiedz:

private Context context = MainActivity.mContext;

Teraz możesz uzyskać zasób łańcucha w następujący sposób:

String myString = context.getResources().getString(R.string.resource_id);
Soham Chari
źródło
-8

Użyłem getContext().getApplicationContext().getString(R.string.nameOfString); To działa dla mnie.

vivynz
źródło
14
Czy uważasz, że getContext()jest dostępny wszędzie ?!
Hamzeh Soboh
1
To nie daje odpowiedzi na pytanie, ponieważ getContext () jest dostępny tylko w klasach activites i fragmentów
Umar Ata