2014-09-30 67 views
4

我正在開發一個適用於Android的Cordova插件,我很難克服從活動內部訪問項目資源 - 插件應該獨立於項目,但訪問資源(例如R.java)是證明棘手。Cordova plugin Android活動 - 訪問資源

我的插件目前由兩個非常簡單的類組成:RedLaser.javaRedLaserScanner.java。從CordovaPlugin

RedLaser.java

繼承等含有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活動邏輯和從BarcodeScanActivity(這是一個SDK的RedLaser類,大概是本身自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

你有沒有解決過這個問題? – mix3d 2015-05-27 20:21:21

+1

我還沒有 - 但我幾個月沒看過這個問題。我將在接下來的幾周內調查它,所以我會告訴你! – keldar 2015-05-28 07:53:54

+0

我剛剛使用了硬編碼應用程序的package.R導入的想法,因爲我現在有一個受控項目,但真正的解決方案會很好:D – mix3d 2015-05-28 14:39:48

回答

4

我只是碰到了同樣的問題,它原來是很容易解決的。 RedLaserScanner延伸的活動,這樣你就可以叫getResources()這樣的:

setContentView(getResources("preview_overlay_new_portrait", "layout", getPackageName())); 
+1

對於遇到此問題的任何人,請嘗試類似'cordova.getActivity ().getResources()。getIdentifier(「name」,「layout」,cordova.getActivity()。getPackageName())' – 2015-08-06 14:55:44

-1

嘗試使用android.R.layout.preview_overlay_new_portrait

+0

這隻會指向Android提供的資源,但是有問題的佈局最初包含在插件的資源中,但被複制到最終應用程序的Resources(R)中,並且不會顯示在android.R.layout – mix3d 2015-05-28 14:41:03

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時,你並不總是在那裏。

0

掛鉤可用於替換源文件內容以刪除錯誤的導入和/或添加正確的資源導入。

我創建了一個腳本,它不需要指定文件。它會嘗試查找源文件(使用.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" /> 

希望它可以幫助別人!