Comment puis-je obtenir un contenu de ressource à partir d'un contexte statique?

168

Je veux lire les chaînes d'un xmlfichier avant de faire quoi que ce soit d'autre, comme setTextsur les widgets, alors comment puis-je faire cela sans objet d'activité à appeler getResources()?

bébé perdu
la source

Réponses:

373
  1. Créez une sous-classe de Application, par exemplepublic class App extends Application {
  2. Définissez l' android:nameattribut de votre <application>balise dans AndroidManifest.xmlpour pointer vers votre nouvelle classe, par exempleandroid:name=".App"
  3. Dans la onCreate()méthode de votre instance d'application, enregistrez votre contexte (par exemple this) dans un champ statique nommé mContextet créez une méthode statique qui renvoie ce champ, par exemple getContext():

Voici à quoi cela devrait ressembler:

public class App extends Application{

    private static Context mContext;

    @Override
    public void onCreate() {
        super.onCreate();
        mContext = this;
    }

    public static Context getContext(){
        return mContext;
    }
}

Vous pouvez désormais utiliser: à App.getContext()chaque fois que vous souhaitez obtenir un contexte, puis getResources()(ou App.getContext().getResources()).

Cristian
la source
9
L'instance de l'application n'est pas une valeur dynamique, comment @Gangnus? Dans tous les cas, j'ai trouvé à la dure que s'appuyer sur la statique dans Android n'est rien d'autre qu'un mal de tête. "Maintenant vous le voyez, maintenant vous ne le faites pas"
Bostone
18
Je ne peux pas éviter de penser que c'est un «hack». Bien que je l'utilise (btw merci d'avoir donné cette solution, puisque j'étais sur le point d'extérioriser la localisation), j'ai ce mauvais pressentiment, comme si c'était faux.
Illiax
8
Mieux ou pire que de simplement passer le contexte en tant que premier paramètre de chaque méthode statique de votre application? Le premier semble piraté, mais le second est inutilement répétitif.
Dave le
12
La documentation dit: "Il n'est normalement pas nécessaire de sous-classer Application. Dans la plupart des cas, les singletons statiques peuvent fournir la même fonctionnalité de manière plus modulaire. Si votre singleton a besoin d'un contexte global (par exemple pour enregistrer des récepteurs de diffusion), la fonction pour récupérer on peut lui donner un Context qui utilise en interne Context.getApplicationContext () lors de la première construction du singleton. " ~ developer.android.com/reference/android/app/Application.html
David d C e Freitas
25
Pour éviter les fuites de mémoire, il serait préférable de stocker le contexte dans un WeakReference: private static WeakReference <Context> mContext; public static Context getContext () {return mContext.get (); } Cela devrait aider lorsque l'application se bloque et que vous ne pouvez pas définir le contexte statique sur null (WeakReference peut être récupéré par la mémoire).
FrankKrumnow
102

Pour les ressources système uniquement!

Utilisation

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

Vous pouvez les utiliser partout dans votre application, même dans les déclarations de constantes statiques!

Gangnus
la source
2
C'est super. Je ne suis généralement pas offensé ... juste quand quelqu'un utilise des majuscules: P Je plaisante. Eh bien, votre standard fonctionne pour certaines ressources comme les chaînes et les drawables ... cependant, comme le dit la documentation, cela ne fonctionne pas bien pour des choses comme les mesures d'orientation, etc. De plus, et le plus important, cela ne vous permettra pas d'obtenir un contexte global qui est parfois utile pour des choses qui peuvent en avoir besoin ( Toastpar exemple, lever une SharedPreferenceinstance, ouvrir une base de données, comme le dit mon professeur de langue latine: et cetera ).
Cristian
1
Vous ne pouvez même pas gagner la paix dans le monde entier par elle :-). Mais cela aide à résoudre le problème posé par la question ici. Je ne dis pas qu'il résout chaque tâche, seulement qu'il résout sa tâche presque à chaque endroit de l'application. J'ai cherché une telle solution pendant 10 mois - tout le temps que j'utilise Android. Et maintenant je l'ai trouvé.
Gangnus
18
Vous devez être prudent ici. N'essayez pas de trouver les ressources de votre application à l'aide de cette méthode. Lisez les petits caractères: renvoie un objet Ressources partagées global qui donne accès uniquement aux ressources système (pas de ressources d'application) et n'est pas configuré pour l'écran actuel (ne peut pas utiliser les unités de dimension, ne change pas en fonction de l'orientation, etc.).
Bostone
4
@ DroidIn.net Citation: "Mais pour les ressources système uniquement!". Je sais / * soupir / *
Gangnus
1
J'ai eu une exception en utilisant cela: android.content.res.Resources $ NotFoundException: ID de ressource de chaîne
vinidog
6

Ma solution Kotlin consiste à utiliser un contexte d'application statique:

class App : Application() {
    companion object {
        lateinit var instance: App private set
    }

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

Et la classe Strings, que j'utilise partout:

object Strings {
    fun get(@StringRes stringRes: Int, vararg formatArgs: Any = emptyArray()): String {
        return App.instance.getString(stringRes, *formatArgs)
    }
}

Ainsi, vous pouvez avoir un moyen propre d'obtenir des chaînes de ressources

Strings.get(R.string.some_string)
Strings.get(R.string.some_string_with_arguments, "Some argument")

Veuillez ne pas supprimer cette réponse, laissez-moi en garder une.

Vitalii Malyi
la source
Solution simple et propre, merci de partager le code!
Jeehut
Merci! Bien que ce soit une solution connue, cela a Stringsété utile.
CoolMind
4

Il y a aussi une autre possibilité. Je charge des shaders OpenGl à partir de ressources comme celle-ci:

static private String vertexShaderCode;
static private String fragmentShaderCode;

static {
    vertexShaderCode = readResourceAsString("/res/raw/vertex_shader.glsl");
    fragmentShaderCode = readResourceAsString("/res/raw/fragment_shader.glsl");
}

private static String readResourceAsString(String path) {
    Exception innerException;
    Class<? extends FloorPlanRenderer> aClass = FloorPlanRenderer.class;
    InputStream inputStream = aClass.getResourceAsStream(path);

    byte[] bytes;
    try {
        bytes = new byte[inputStream.available()];
        inputStream.read(bytes);
        return new String(bytes);
    } catch (IOException e) {
        e.printStackTrace();
        innerException = e;
    }
    throw new RuntimeException("Cannot load shader code from resources.", innerException);
}

Comme vous pouvez le voir, vous pouvez accéder à n'importe quelle ressource dans le chemin /res/... Change aClassvers votre classe. C'est aussi comment je charge des ressources dans les tests (androidTests)

Grégory Stein
la source
1
La seule solution qui a fonctionné pour moi lorsque je n'ai pas d'activité (développer un plugin sans classe qui pourrait étendre Application). Merci +1
itaton
3

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

}

Initialisez le Singleton dans votre Applicationsous-classe:

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

Si je ne me trompe pas, cela vous donne un accès à applicationContext partout, appelez-le avec ApplicationContextSingleton.getInstance.getApplicationContext(); Vous ne devriez pas avoir besoin de l'effacer à aucun moment, car lorsque l'application se ferme, cela va avec de toute façon.

N'oubliez pas de mettre à jour AndroidManifest.xmlpour utiliser cette Applicationsous - classe:

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

Vous devriez maintenant pouvoir utiliser ApplicationContextSingleton.getInstance (). GetApplicationContext (). GetResources () de n'importe où, ainsi que les très rares endroits où les sous-classes d'application ne peuvent pas.

S'il vous plaît laissez-moi savoir si vous voyez quelque chose de mal ici, merci. :)

Versa
la source
2

Une autre solution:

Si vous avez une sous-classe statique dans une classe externe non statique, vous pouvez accéder aux ressources depuis la sous-classe via des variables statiques dans la classe externe, que vous initialisez lors de la création de la classe externe. Comme

public class Outerclass {

    static String resource1

    public onCreate() {
        resource1 = getString(R.string.text);
    }

    public static class Innerclass {

        public StringGetter (int num) {
            return resource1; 
        }
    }
}

Je l'ai utilisé pour la fonction getPageTitle (position int) du FragmentPagerAdapter statique dans mon FragmentActivity, ce qui est utile en raison de I8N.

Stéphan Brunker
la source
2

Raccourci

J'utilise à la App.getRes()place de App.getContext().getResources()(comme @Cristian a répondu)

Il est très simple à utiliser n'importe où dans votre code!

Voici donc une solution unique par laquelle vous pouvez accéder aux ressources de n'importe où Util class.

(1) Créez ou modifiez votre Applicationclasse.

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) Ajoutez un champ de nom à votre manifest.xml <applicationbalise. (ou sauter ceci si déjà là)

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

Maintenant, vous êtes prêt à partir.

Utilisez App.getRes().getString(R.string.some_id)n'importe où dans le code.

Khemraj
la source
0

Je pense que plus de chemin est possible. Mais parfois, j'utilise cette solution. (global complet):

    import android.content.Context;

    import <your package>.R;

    public class XmlVar {

        private XmlVar() {
        }

        private static String _write_success;

        public static String write_success() {
            return _write_success;
        }


        public static void Init(Context c) {
            _write_success = c.getResources().getString(R.string.write_success);
        }
    }
//After activity created:
cont = this.getApplicationContext();
XmlVar.Init(cont);
//And use everywhere
XmlVar.write_success();
user2684935
la source
0

Je charge le shader pour openGL ES à partir de la fonction statique.

N'oubliez pas que vous devez utiliser des minuscules pour le nom de votre fichier et de votre répertoire, sinon l'opération échouera

public class MyGLRenderer implements GLSurfaceView.Renderer {

    ...

    public static int loadShader() {
        //    Read file as input stream
        InputStream inputStream = MyGLRenderer.class.getResourceAsStream("/res/raw/vertex_shader.txt");

        //    Convert input stream to string
        Scanner s = new Scanner(inputStream).useDelimiter("\\A");
        String shaderCode = s.hasNext() ? s.next() : "";
    }

    ...

}
user2174870
la source
0
public Static Resources mResources;

 @Override
     public void onCreate()
     {
           mResources = getResources();
     }
Makvin
la source
Eh bien, le problème est que getResources () a besoin d'un contexte. Donc, ce n'est probablement pas vraiment une solution pour "sans objet d'activité" (dans lequel vous avez posté la méthode onCreate ())
Tobias Reich
0

J'utilise le niveau d'API 27 et j'ai trouvé la meilleure solution après avoir lutté pendant environ deux jours. Si vous souhaitez lire un fichier xml d'une classe qui ne dérive pas d'Activité ou d'Application, procédez comme suit.

  1. Placez le fichier testdata.xml dans le répertoire des ressources.

  2. Écrivez le code suivant pour analyser le document testdata.

        InputStream inputStream = this.getClass().getResourceAsStream("/assets/testdata.xml");
    
        // create a new DocumentBuilderFactory
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        // use the factory to create a documentbuilder
        DocumentBuilder builder = factory.newDocumentBuilder();
        // create a new document from input stream
        Document doc = builder.parse(inputStream);
Jnana
la source
-1

Dans votre classe, où vous implémentez la fonction statique , vous pouvez appeler une méthode private \ public à partir de cette classe. La méthode private \ public peut accéder à getResources .

par exemple:

public class Text {

   public static void setColor(EditText et) {
      et.resetColor(); // it works

      // ERROR
      et.setTextColor(getResources().getColor(R.color.Black)); // ERROR
   }

   // set the color to be black when reset
   private void resetColor() {
       setTextColor(getResources().getColor(R.color.Black));
   }
}

et depuis une autre classe \ activité, vous pouvez appeler:

Text.setColor('some EditText you initialized');
Maor Cohen
la source
-1

si vous avez un contexte, je veux dire à l'intérieur;

public void onReceive(Context context, Intent intent){

}

vous pouvez utiliser ce code pour obtenir des ressources:

context.getResources().getString(R.string.app_name);
eren130
la source
2
Le titre de la question dit dans un contexte statique. Ce que votre réponse ne couvre pas.
Rune Schjellerup Philosof