getString außerhalb eines Kontexts oder einer Aktivität

260

Ich fand das R.stringziemlich großartig, um fest codierte Zeichenfolgen aus meinem Code herauszuhalten, und ich möchte es weiterhin in einer Utility-Klasse verwenden, die mit Modellen in meiner Anwendung zusammenarbeitet, um eine Ausgabe zu generieren. In diesem Fall generiere ich beispielsweise eine E-Mail von einem Modell außerhalb der Aktivität.

Ist es möglich, getStringaußerhalb eines Contextoder zu verwendenActivity ? Ich nehme an, ich könnte die aktuelle Aktivität bestehen, aber es scheint unnötig. Bitte korrigiere mich wenn ich falsch liege!

Bearbeiten: Können wir auf die Ressourcen zugreifen, ohne sie zu verwenden Context?

SapphireSun
quelle
4
Indem Sie den Kontext an die Klasse übergeben, die die Zeichenfolge verwenden soll, übergeben Sie auch Informationen darüber, welche Sprache (en, es usw.) von der App verwendet wird. Wenn Sie also zwei strings.xml haben, wissen Sie, welche Sie verwenden müssen
Sport

Antworten:

439

Ja, wir können auf Ressourcen zugreifen, ohne "Kontext" zu verwenden

Sie können verwenden:

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

... überall in Ihrer Anwendung, auch in statischen Konstantendeklarationen. Leider werden nur die Systemressourcen unterstützt .

Verwenden Sie für lokale Ressourcen diese Lösung . Es ist nicht trivial, aber es funktioniert.

Gangnus
quelle
17
Was versteht man unter Systemressourcen? Die Datei strings.xml ist eine Systemressource oder nicht? Für mich funktioniert es nicht, sagt kann keine Ressource finden.
Kirhgoff
6
Die Systemressourcen gehören zu Android auf dem Gerät. string.xml gehört nur zu Ihrer Anwendung. Suchen Sie nach stackoverflow.com/a/4391811/715269 Lösung
Gangnus
3
Dies ist eine elegante Lösung für diese Factory-Klassen beim Zugriff auf Zeichenfolgen. Ich mag es nicht, Context überall weiterzugeben. Das ist nur unnötige Unordnung für Fälle, in denen wir wirklich nur eine Zeichenfolge global speichern möchten.
Jay Snayder
1
Ist dies effizienter, als den Kontext an eine Klasse weiterzugeben und ihn zu verwenden?
SoliQuiD
5
Ich bekomme diesen Fehlerandroid.content.res.Resources$NotFoundException: String resource ID #0x7f0f0061
Ebrahim Karimi
108

Leider können Sie nur mit einem Context(dh einem Activityoder Service) auf eine der Zeichenfolgenressourcen zugreifen . Was ich in diesem Fall normalerweise getan habe, ist einfach zu verlangen, dass der Anrufer den Kontext weitergibt.

Erich Douglass
quelle
4
Danke für den Tipp! Ich habe es gerade versucht, aber aus irgendeinem Grund bekam ich einen Kompilierungsfehler, als ich versuchte:ctx.getString(ctx.R.string.blah);
SapphireSun
Ich würde das Argument zu den util-Methoden vom Typ machen, Contextdamit Sie es entweder von einer Aktivität oder einem Dienst verwenden können.
MatrixFrog
2
Sie brauchen das nicht ctx.R.string.blah, verwenden Sie einfachR.string.blah
Pentium10
2
Ich bin nicht sicher, woher das symbol not found errorkommt, aber stellen Sie sicher, dass Sie über Rder Klasse importiert haben .
Pentium10
11
Die Antwort ist FALSCH. Vis den nächsten. :-)
Gangnus
33

In MyApplication, das sich erstreckt Application:

public static Resources resources;

In MyApplication's onCreate:

resources = getResources();

Jetzt können Sie dieses Feld von überall in Ihrer Anwendung verwenden.

konmik
quelle
Würde es durch Service funktionieren? (Besonders wenn Android die App beendet und nur den Dienst startet)
Atul
1
Ja, Android beginnt mit der Ausführung Ihres Codes durch Aufrufen von Application.onCreate und führt anschließend Ihren Dienst aus.
Konmik
23

Übrigens, einer der Gründe für den Fehler " Symbol nicht gefunden" kann sein, dass Ihre IDE android.R importiert hat. Klasse statt deiner. Ändern Sie einfach den Import android.R; um your.namespace.R zu importieren;

Also 2 grundlegende Dinge, um String in der verschiedenen Klasse sichtbar zu machen:

//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
quelle
19

Einzigartiger Ansatz

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

Dies funktioniert überall in der App. ( Util-Klasse, Dialog, Fragment oder eine beliebige Klasse in Ihrer App )

(1) Erstellen oder bearbeiten Sie Ihre ApplicationKlasse (falls bereits vorhanden) .

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) Fügen Sie Ihrem manifest.xml <applicationTag ein Namensfeld hinzu.

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

Jetzt können Sie loslegen. Verwenden Sie App.getRes().getString(R.string.some_id)überall in der App.

Khemraj
quelle
1
Ich persönlich mag diesen Ansatz. Bequem, um benutzerdefinierte Zeichenfolgenressourcen von einer beliebigen Stelle im Code abzurufen.
Schachmatt711
Gibt es Sicherheitsprobleme mit dieser Lösung?
Nibbana
@ user1823280 Nein, ich glaube nicht.
Khemraj
Perfekte Lösung
Yasiru Nayanajith
4

Wenn Sie eine Klasse haben, die Sie in einer Aktivität verwenden, und auf die Ressource in dieser Klasse zugreifen möchten, empfehle ich Ihnen, einen Kontext als private Variable in der Klasse zu definieren und im Konstruktor zu initialisieren:

public class MyClass (){
    private Context context;

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

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

Machen Sie einen Moment des Unterrichts in Ihrer Aktivität:

MyClass m=new MyClass(this);
Malus Jan.
quelle
0

Auf diese Weise können Sie applicationContextvon überall darauf zugreifen, sodass Sie applicationContextüberall darauf zugreifen können, wo Sie es verwenden können. Toast, getString(), sharedPreferencesEtc.

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

}

Initialisieren Sie den Singleton in Ihrer ApplicationUnterklasse:

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

Wenn ich mich nicht irre, gibt Ihnen dies überall einen Haken an applicationContext. Rufen Sie ihn mit auf. ApplicationContextSingleton.getInstance.getApplicationContext(); Sie sollten dies zu keinem Zeitpunkt löschen müssen, da dies beim Schließen der Anwendung sowieso dazu gehört.

Denken Sie daran, ein Update durchzuführen AndroidManifest.xml, um diese ApplicationUnterklasse zu verwenden:

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

Bitte lassen Sie mich wissen, wenn Sie hier etwas falsch sehen, danke. :) :)

Versa
quelle
0

Der beste Ansatz aus der Antwort von Khemraj:

App-Klasse

class App : Application() {

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


    // MARK: - Lifecycle

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

}

Erklärung im Manifest

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

Konstanten Klasse

class Localizations {

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

}

Verwenden von

textView.text = Localizations.info
Mickael Belhassen
quelle
0

Es ist besser, so etwas ohne Kontext und Aktivität zu verwenden :

Resources.getSystem().getString(R.string.my_text)
reza_khalafi
quelle
0

Irgendwie gefielen die hackigen Lösungen zum Speichern statischer Werte nicht, so dass etwas länger, aber eine saubere Version, die auch getestet werden kann.

Es wurden 2 Möglichkeiten gefunden, dies zu tun.

  1. Übergeben Sie context.resources als Parameter an Ihre Klasse, in der Sie die Zeichenfolgenressource möchten. Relativ einfach. Wenn die Übergabe als Parameter nicht möglich ist, verwenden Sie den Setter.

z.B

data class MyModel(val resources: Resources) {
    fun getNameString(): String {
        resources.getString(R.string.someString)
    }
}
  1. Verwenden Sie die Datenbindung (erfordert jedoch Fragment / Aktivität)

Bevor Sie lesen: Diese Version verwendet 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>

Aktivität / Fragment-

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

Manchmal müssen Sie den Text basierend auf einem Feld in einem Modell ändern. Sie würden also auch dieses Modell datenbinden, und da Ihre Aktivität / Ihr Fragment das Modell kennt, können Sie den Wert sehr gut abrufen und dann die Zeichenfolge basierend darauf datenbinden.

Rajkiran
quelle
0

Sie können dies in Kotlin tun, indem Sie eine Klasse erstellen, die Application erweitert, und dann ihren Kontext verwenden, um die Ressourcen an einer beliebigen Stelle in Ihrem Code aufzurufen

Ihre App-Klasse wird so aussehen

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

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

Deklarieren Sie Ihre Anwendungsklasse in AndroidManifest.xml (sehr wichtig)

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

Verwenden Sie den folgenden Code, um beispielsweise auf eine Zeichenfolgendatei zuzugreifen

App.context?.resources?.getText(R.string.mystring)
Ahmed Raza
quelle
Dies funktioniert nicht, wenn Sie das Gebietsschema zur Laufzeit programmgesteuert ändern, da der Anwendungskontext ein Singleton ist und beim Start Ihres Prozesses initialisiert wird.
Szörényi Ádám
-2

Folgendes habe ich getan: Erstellen Sie in Ihrer MainActivity eine statische Variable für den Kontext, wie unten gezeigt:

public static Context mContext;

und in onCreate () initialisiere mContext dazu;

mContext = this;

In der Datei, in der Sie auf den Kontext zugreifen möchten, sagen Sie beispielsweise:

private Context context = MainActivity.mContext;

Jetzt können Sie eine Zeichenfolgenressource auf folgende Weise abrufen:

String myString = context.getResources().getString(R.string.resource_id);
Soham Chari
quelle
-8

Ich habe es benutzt getContext().getApplicationContext().getString(R.string.nameOfString); Es funktioniert für mich.

vivynz
quelle
14
Glaubst du, es getContext()ist überall verfügbar?!
Hamzeh Soboh
1
Dies gibt keine Antwort auf die Frage, da getContext () nur in Activites- und Fragmentklassen verfügbar ist
Umar Ata