我正在开发Android的Cordova插件,我很难克服从活动中访问项目资源 - 插件应该是项目独立的,但访问资源(例如R.java)证明是棘手的。
我的插件目前由两个非常简单的类组成:RedLaser.java
和RedLaserScanner.java
。
RedLaser.java
继承自CordovaPlugin,因此包含execute
方法,其外观类似于以下内容。
public class RedLaser extends CordovaPlugin {
private static final string SCAN_ACTION = "scan";
public boolean execute(String action, final JSONArray args, final CallbackContext callbackContext) throws JSONException {
if (action.equals(SCAN_ACTION)) {
this.cordova.getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
scan(args, callbackContext);
}
});
return true;
}
return false;
}
private void scan(JSONArray args, CallbackContext callbackContext) {
Intent intent = new Intent(this.cordova.getActivity().getApplicationContext(), RedLaserScanner.class);
this.cordova.startActivityForResult((CordovaPlugin) this, intent, 1);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
// Do something with the result
}
}
RedLaserScanner.java
RedLaserScanner包含Android Activity逻辑,并继承自BarcodeScanActivity(这是一个RedLaser SDK类,可能是继承自Activity);
一个非常简单的结构如下:
public class RedLaserScanner extends BarcodeScanActivity {
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.preview_overlay_new_portrait);
}
}
我遇到了麻烦,因为我需要访问项目的资源来访问R.layout.preview_overlay_new_portrait
(它们分散在Eclipse项目中) - 但我不能这样做,除非我导入com.myProject.myApp.R
- 这使得我的插件依赖于项目本身。
我做了一些调查,发现cordova.getActivity().getResources()
似乎很有用,但是我的RedLaserScanner无法访问它 - 因为它不会从CordovaPlugin继承。
有人可以帮我指点一下吗?
由于
答案 0 :(得分:7)
我刚遇到同样的问题,结果很容易解决。 RedLaserScanner扩展了一个活动,所以你可以像这样调用getResources():
setContentView(getResources("preview_overlay_new_portrait", "layout", getPackageName()));
答案 1 :(得分:3)
Hooks可用于替换源文件内容以删除错误的导入和/或添加正确的资源导入。
我创建了一个无需指定文件的脚本。它尝试查找源文件(使用 .java 扩展名),删除其中已有的任何资源导入,然后使用Cordova应用程序包名称导入正确的资源(如果需要)。
这是剧本:
#!/usr/bin/env node
/*
* A hook to add resources class (R.java) import to Android classes which uses it.
*/
function getRegexGroupMatches(string, regex, index) {
index || (index = 1)
var matches = [];
var match;
if (regex.global) {
while (match = regex.exec(string)) {
matches.push(match[index]);
console.log('Match:', match);
}
}
else {
if (match = regex.exec(string)) {
matches.push(match[index]);
}
}
return matches;
}
module.exports = function (ctx) {
// If Android platform is not installed, don't even execute
if (ctx.opts.cordova.platforms.indexOf('android') < 0)
return;
var fs = ctx.requireCordovaModule('fs'),
path = ctx.requireCordovaModule('path'),
Q = ctx.requireCordovaModule('q');
var deferral = Q.defer();
var platformSourcesRoot = path.join(ctx.opts.projectRoot, 'platforms/android/src');
var pluginSourcesRoot = path.join(ctx.opts.plugin.dir, 'src/android');
var androidPluginsData = JSON.parse(fs.readFileSync(path.join(ctx.opts.projectRoot, 'plugins', 'android.json'), 'utf8'));
var appPackage = androidPluginsData.installed_plugins[ctx.opts.plugin.id]['PACKAGE_NAME'];
fs.readdir(pluginSourcesRoot, function (err, files) {
if (err) {
console.error('Error when reading file:', err)
deferral.reject();
return
}
var deferrals = [];
files.filter(function (file) { return path.extname(file) === '.java'; })
.forEach(function (file) {
var deferral = Q.defer();
var filename = path.basename(file);
var file = path.join(pluginSourcesRoot, filename);
fs.readFile(file, 'utf-8', function (err, contents) {
if (err) {
console.error('Error when reading file:', err)
deferral.reject();
return
}
if (contents.match(/[^\.\w]R\./)) {
console.log('Trying to get packages from file:', filename);
var packages = getRegexGroupMatches(contents, /package ([^;]+);/);
for (var p = 0; p < packages.length; p++) {
try {
var package = packages[p];
var sourceFile = path.join(platformSourcesRoot, package.replace(/\./g, '/'), filename)
if (!fs.existsSync(sourceFile))
throw 'Can\'t find file in installed platform directory: "' + sourceFile + '".';
var sourceFileContents = fs.readFileSync(sourceFile, 'utf8');
if (!sourceFileContents)
throw 'Can\'t read file contents.';
var newContents = sourceFileContents
.replace(/(import ([^;]+).R;)/g, '')
.replace(/(package ([^;]+);)/g, '$1 import ' + appPackage + '.R;');
fs.writeFileSync(sourceFile, newContents, 'utf8');
break;
}
catch (ex) {
console.log('Could not add import to "' + filename + '" using package "' + package + '". ' + ex);
}
}
}
});
deferrals.push(deferral.promise);
});
Q.all(deferrals)
.then(function() {
console.log('Done with the hook!');
deferral.resolve();
})
});
return deferral.promise;
}
只需在 plugin.xml 中添加 after_plugin_install 挂钩(适用于Android平台):
<hook type="after_plugin_install" src="scripts/android/addResourcesClassImport.js" />
希望它有所帮助!
答案 2 :(得分:1)
我为此实施了一个帮助程序以保持清洁。当您创建一个插件时,它也会有所帮助,该插件接受存储在插件中的字符串资源文件中的config.xml参数。
private int getAppResource(String name, String type) {
return cordova.getActivity().getResources().getIdentifier(name, type, cordova.getActivity().getPackageName());
}
您可以按如下方式使用它:
getAppResource("app_name", "string");
这将返回app_name的字符串资源ID,实际值仍然需要通过调用:
来检索this.activity.getString(getAppResource("app_name", "string"))
或者原始问题中的情况:
setContentView(getAppResource("preview_overlay_new_portrait", "layout"));
这些天我只是创建一个帮助程序,它直接从帮助程序返回值:
private String getStringResource(String name) {
return this.activity.getString(
this.activity.getResources().getIdentifier(
name, "string", this.activity.getPackageName()));
}
反过来你会这样打电话:
this.getStringResource("app_name");
我认为重要的是要指出,当你拥有资源ID时,你并不总是存在。
答案 3 :(得分:-1)
尝试使用android.R.layout.preview_overlay_new_portrait