【发布时间】:2011-05-22 11:03:00
【问题描述】:
我想先从xml 文件中读取字符串,然后再对小部件执行setText 之类的其他操作,那么如果没有活动对象来调用getResources(),我该怎么做呢?
【问题讨论】:
标签: java android static constants android-resources
我想先从xml 文件中读取字符串,然后再对小部件执行setText 之类的其他操作,那么如果没有活动对象来调用getResources(),我该怎么做呢?
【问题讨论】:
标签: java android static constants android-resources
Application 的子类,例如public class App extends Application {
AndroidManifest.xml 中设置<application> 标记的android:name 属性以指向您的新类,例如android:name=".App"
onCreate() 方法中,将您的上下文(例如this)保存到名为mContext 的静态字段中,并创建一个返回此字段的静态方法,例如getContext():它应该是这样的:
public class App extends Application{
private static Context mContext;
@Override
public void onCreate() {
super.onCreate();
mContext = this;
}
public static Context getContext(){
return mContext;
}
}
现在您可以在想要获取上下文时使用:App.getContext(),然后使用getResources()(或App.getContext().getResources())。
【讨论】:
仅适用于系统资源!
使用
Resources.getSystem().getString(android.R.string.cancel)
您可以在应用程序的任何地方使用它们,甚至在静态常量声明中!
【讨论】:
Toast,获得一个SharedPreference 实例,打开一个数据库,正如我的拉丁语老师所说:等等)。
我的 Kotlin 解决方案是使用静态应用程序上下文:
class App : Application() {
companion object {
lateinit var instance: App private set
}
override fun onCreate() {
super.onCreate()
instance = this
}
}
还有我到处使用的 Strings 类:
object Strings {
fun get(@StringRes stringRes: Int, vararg formatArgs: Any = emptyArray()): String {
return App.instance.getString(stringRes, *formatArgs)
}
}
所以你可以有一个干净的方式来获取资源字符串
Strings.get(R.string.some_string)
Strings.get(R.string.some_string_with_arguments, "Some argument")
请不要删除这个答案,让我保留一个。
【讨论】:
Strings 很有帮助。
我使用App.getRes() 而不是App.getContext().getResources()(正如@Cristian 回答的那样)
在代码中的任何地方使用都非常简单!
所以这是一个独特的解决方案,您可以通过它从任何地方访问资源,例如Util class。
(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 getRes() {
return res;
}
}
(2) 将名称字段添加到您的 manifest.xml <application 标签。 (或者如果已经存在则跳过此)
<application
android:name=".App"
...
>
...
</application>
现在你可以走了。
App.getRes().getString(R.string.some_id)。【讨论】:
getRes() 而不是getResources()
还有另一种可能性。我从以下资源加载 OpenGL 着色器:
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);
}
如您所见,您可以访问路径/res/... 中的任何资源
将aClass 更改为您的班级。这也是我在测试中加载资源的方式 (androidTests)
【讨论】:
单身人士:
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;
}
}
在 Application 子类中初始化 Singleton:
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"
>
现在您应该可以在任何地方使用 ApplicationContextSingleton.getInstance().getApplicationContext().getResources() 了,还有应用程序子类不能使用的极少数地方。
如果您在这里发现任何问题,请告诉我,谢谢。 :)
【讨论】:
另一种解决方案:
如果您在非静态外部类中有静态子类,则可以通过外部类中的静态变量从子类中访问资源,您在创建外部类时对其进行初始化。喜欢
public class Outerclass {
static String resource1
public onCreate() {
resource1 = getString(R.string.text);
}
public static class Innerclass {
public StringGetter (int num) {
return resource1;
}
}
}
我将它用于我的 FragmentActivity 中的静态 FragmentPagerAdapter 的 getPageTitle(int position) 函数,由于 I8N,它很有用。
【讨论】:
我认为,更多的方法是可能的。 但有时,我使用这个解决方案。 (完全全球):
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();
【讨论】:
我从静态函数为 openGL ES 加载着色器。
记住文件名和目录名必须小写,否则操作会失败
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() : "";
}
...
}
【讨论】:
我正在使用 API 级别 27,并在挣扎了大约两天后找到了最佳解决方案。如果要从不是从 Activity 或 Application 派生的类中读取 xml 文件,请执行以下操作。
将 testdata.xml 文件放入 assets 目录中。
编写以下代码,获取解析后的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);
【讨论】:
在没有上下文的情况下以 InputStream 的形式重新获取图像:
Class<? extends MyClass> aClass = MyClass.class;
URL r = aClass.getResource("/res/raw/test.png");
URLConnection urlConnection = r.openConnection();
return new BufferedInputStream(urlConnection.getInputStream());
如果您的文件需要目录树,它也可以工作(资产支持子目录):
URL r = aClass.getResource("/assets/images/base/2.png");
【讨论】:
你为什么不尝试
Resources.getSystem().getString(R.string.foo);
【讨论】:
您可以尝试另一种略有不同的方法。
您可以像提到的其他解决方案一样子类化Application 类,并存储对Resources 实例的静态引用。
创建一个应用程序类并在onCreate方法中初始化Resources变量。这将在您的应用启动时调用。我们可以在这里使用WeakReference 来防止由于将此实例存储为静态变量而可能发生的内存泄漏(尽管这种情况不太可能发生)
public class App extends Application {
private static WeakReference<Resources> res;
既然您提到您只想从 xml 资源声明中检索字符串,则无需将此资源变量公开给其他类,以封装资源实例并防止其泄漏。因此,您可以将引用存储为私有变量。
记得在onCreate中初始化这个变量:
@Override
public void onCreate() {
super.onCreate();
res = new WeakReference<>(getResources());
}
我们还需要在AndroidManifest.xml 的application 标记下将应用程序的android:name 声明为.App(或您设置的任何其他名称)。
<application android:name=".App"
........... other attributes here ...........
另一种检索字符串资源的方法不是在其他类中使用Resources 实例(或Context 实例),而是让App 类在静态方法中为您获取此资源。这使实例保持封装/私有。
您可以在App 类中使用静态方法来检索这些值(例如getStringGlobal,只是不要将其称为getString,因为它会与默认方法冲突)
public static String getStringGlobal(@StringRes int resId) {
if (res != null && res.get() != null) {
return res.get().getString(resId);
} else {
// This should not happen, you should throw an exception here, or you can return a fallback string to ensure the app still runs
}
}
正如所见,您还可以添加错误处理以防Resources 的实例不可用(这不应该发生,但以防万一)。
然后您可以通过调用检索字符串资源
App.getStringGlobal(R.string./*your string resource name*/)
所以你的App.java:
public class App extends Application {
private static WeakReference<Resources> res;
@Override
public void onCreate() {
super.onCreate();
res = new WeakReference<>(getResources());
}
public static String getStringGlobal(@StringRes int resId) {
if (res != null && res.get() != null) {
return res.get().getString(resId);
} else {
// This should not happen(reference to Resources invalid), you should throw an exception here, or you can return a fallback string to ensure the app still runs
}
}
}
【讨论】:
在您实现static 函数的类中,您可以从该类调用private\public 方法。 private\public 方法可以访问 getResources。
例如:
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));
}
}
从其他类\活动中,您可以调用:
Text.setColor('some EditText you initialized');
【讨论】:
如果你有上下文,我的意思是在里面;
public void onReceive(Context context, Intent intent){
}
您可以使用此代码获取资源:
context.getResources().getString(R.string.app_name);
【讨论】:
public Static Resources mResources;
@Override
public void onCreate()
{
mResources = getResources();
}
【讨论】: