精华内容
下载资源
问答
  • android相册上传插件

    2014-09-23 13:58:21
    android相册上传插件,可以选择本地图片或者拍照上传。。选定图片后可以截取图片。比较不错的功能插件
  • 此代码,是unity拉取安卓苹果的插件,里面集成了安卓和ios的所有功能,直接用里面的代码也可以,也可以自己重写方法,按自己的需求来。有不懂的尽管提问
  • 支持unity5.0以上,支持打开Android和IOS手机系统摄像机和相册软件并截图,解决其他此类工具使用后打包APK出错问题。
  • 由于最近项目需要做,一个简单的unity3d上面链接android原生相册获取图片的插件,于是乎简单的了解了一下,以下是实现步骤: 1.从unity3d的项目路径中拷出jar包unity3D5.0\u3d\Unity\Editor\Data\PlaybackEngines\...

    由于最近项目需要做,一个简单的unity3d上面链接android原生相册获取图片的插件,于是乎简单的了解了一下,以下是实现步骤:

    1.从unity3d的项目路径中拷出jar包unity3D5.0\u3d\Unity\Editor\Data\PlaybackEngines\androidplayer\release\bin\classess

    2.新建一个acitivity把classes.jre包放入libs文件夹,并创建一个继承自UnityPlayerActivity的类

    2.1导入classes.jar

    2.2创建继承自UnityPlayerActivity的类

    public class Main extends UnityPlayerActivity{
    	
    	private String photoPath="";
    		
    	public static String FILE_NAME = "image.png";
    	
    	public static final int CHOOSE_PICTURE=4;
    	
    	private String newPath="";
        
    	private String headPath="";
    	
    	@Override
    	protected void onCreate(Bundle savedInstanceState) {
    		// TODO Auto-generated method stub
    		super.onCreate(savedInstanceState);
    	}
    }
    3.主要的类中书写供unity脚本回调的方法(Main()是实现打开相册的方法,RefreshPic是实现图片截图之后保存到照相机中的相册方法)

    public void Main()
    	{
    	Intent openAlbumIntent = new Intent(Intent.ACTION_GET_CONTENT);  
    		
        	openAlbumIntent.setType("image/*");  
            
        	startActivityForResult(openAlbumIntent, CHOOSE_PICTURE);  
    	}
    	
    	 public void RefreshPic(String oldpath)
    	 {
    		 photoPath="/mnt/sdcard/DCIM/Camera/";
    		 SimpleDateFormat sDateFormat=new SimpleDateFormat("yyyyMMddhhmmss");     
    		 FILE_NAME="p"+sDateFormat.format(new java.util.Date())+".png"; 
    		 copyFile(oldpath, photoPath);
    		 myDeleteFile(oldpath);
    		 Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE); 
    		 Uri uri = Uri.fromFile(new File(photoPath+FILE_NAME)); 
    		 intent.setData(uri); 
    		 sendBroadcast(intent);  
    	 }

    4.书写打开相册选择图片之后的回调方法

     @Override
    	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    		    // 拍照  
    	        if(requestCode==CHOOSE_PICTURE)
    	        {
    	             Uri originalUri = data.getData();  
    	             String[] proj = {MediaStore.Images.Media.DATA};
    				 Cursor cursor = getContentResolver().query(originalUri, proj, null, null, null); 
    				 int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
    				 cursor.moveToFirst();
    			     //判断游标是否为空
    				 if(cursor.getString(column_index)!=null)
    				 {
    			     //得到选择图片的原始路径
    				 String oldpath = cursor.getString(column_index); 
    				 //保存原始图片的路径
    				 newPath="/mnt/sdcard/Android/data/com.unitypluginstest.main/files/sourcefiles";
    				 //保存压缩后图片的路径
    				 headPath="/mnt/sdcard/Android/data/com.unitypluginstest.main/files/headfiles";
    				 SimpleDateFormat sDateFormat=new SimpleDateFormat("yyyyMMddhhmmss");     
    				 FILE_NAME="p"+sDateFormat.format(new java.util.Date())+".png";  
    				 //将选择的图片文件复制到新的路径
    				 copyFile(oldpath, newPath);
    				 //位图工厂模式打开对图片进行压缩设置
    				 BitmapFactory.Options options = new BitmapFactory.Options();
    	             options.inSampleSize =2;
    	             Bitmap b = BitmapFactory.decodeFile(newPath+"/"+FILE_NAME, options);
    	        	 BitmapFactory.Options options1 = new BitmapFactory.Options();
    	             options1.inSampleSize =getSuitableSize(b.getWidth()*2,b.getHeight()*2);
    	             Bitmap b1= BitmapFactory.decodeFile(newPath+"/"+FILE_NAME, options1);
    	             saveBitmap(headPath, b1);
    	             }
    	             else
    	             {
    	 			 Toast.makeText(getApplicationContext(), "当前的路径不存在", Toast.LENGTH_SHORT).show();
    	             }
    	        }
    		super.onActivityResult(requestCode, resultCode, data);
    	}
    5.书写回调图片方法之后的相关工具方法(文件复制,压缩,保存图片等)

     public void copyFile(String oldPath, String newPath) {   
    	       try {   
    	           int bytesum = 0;   
    	           int byteread = 0;   
    	           File oldfile = new File(oldPath); 
    	           File newfile=new File(newPath);
    	           if(!newfile.exists())
    	           {
    	        	   newfile.mkdirs();
    	           }
    	           if (oldfile.exists()) { 
    	               InputStream inStream = new FileInputStream(oldPath); //读入原文件   
    	               FileOutputStream fs = new FileOutputStream(newPath+"/"+FILE_NAME);   
    	               byte[] buffer = new byte[1024];   
    	               while ( (byteread = inStream.read(buffer)) != -1) {   
    	                   bytesum += byteread; 
    	                   fs.write(buffer, 0, byteread);   
    	               }   
    	               Log.e("tag", "success!");
    	               inStream.close();   
    	               fs.close();
    	           }   
    	       }   
    	       catch (Exception e) {   
    	           e.printStackTrace();   
    	  
    	       }   
    	   }   
    	 
    	 public boolean myDeleteFile(String path)
    	 {
    		 File file=new File(path);
    		 if(file.exists())
    		 {
    			 file.delete();
    			 return true;
    		 }else
    		 {
    			 return false;
    		 }
    	 }
    private int getSuitableSize(int width, int height) {
    			int suitableSize = 16;
    			if(width>height)
    			{
    				suitableSize=Math.round(width/128);
    			}else if(height>width) 
    			{
    				suitableSize=Math.round(height/128);
    			}else 
    			{
    				suitableSize=Math.round(width/128);
    			}
    			return suitableSize;
    		}
    		
    		public void saveBitmap(String filePath,Bitmap bmp) { 
    			File file = new File(filePath); 
    			if(!file.exists())
    			{
    				file.mkdirs();
    			}
    			try { 
    			FileOutputStream out = new FileOutputStream(file+"/"+FILE_NAME); 
    			bmp.compress(Bitmap.CompressFormat.PNG, 30, out); 
    			out.flush(); 
    			out.close(); 
    			} catch (FileNotFoundException e) { 
    			// TODO Auto-generated catch block 
    			e.printStackTrace(); 
    			} catch (IOException e) { 
    			// TODO Auto-generated catch block 
    			e.printStackTrace(); 
    			} 
    			} 
    

    6.生成自己编写的jar包,并且导出相关的资源文件



    展开全文
  • 我们先通过loadUrl(url)加载我们的本地h5项目,使用loadUrl这个方法一定是继承自CordovaActivity,cordovaActivity的onCreate...h5中如何调用引入的cordova插件呢,以相册选取为例,下面我们来看下: navigator.ca...

    我们先通过loadUrl(url)加载我们的本地h5项目,使用loadUrl这个方法一定是继承自CordovaActivity,cordovaActivity的onCreate方法中调用了loadConfig(),这个方法主要是加载配置文件(res/xml/config.xml )中的信息。

    h5中如何调用引入的cordova插件呢,以相册选取为例,下面我们来看下:

    navigator.camera.getPicture(cameraSuccess, cameraError, cameraOptions);

    我们通过上面代码调用Camera的getPicture()方法,看下Camera.js的代码:

    cameraExport.getPicture = function (successCallback, errorCallback, options) {
        argscheck.checkArgs('fFO', 'Camera.getPicture', arguments);
        options = options || {};
        var getValue = argscheck.getValue;
    
        var quality = getValue(options.quality, 50);
        var destinationType = getValue(options.destinationType, Camera.DestinationType.FILE_URI);
        var sourceType = getValue(options.sourceType, Camera.PictureSourceType.CAMERA);
        var targetWidth = getValue(options.targetWidth, -1);
        var targetHeight = getValue(options.targetHeight, -1);
        var encodingType = getValue(options.encodingType, Camera.EncodingType.JPEG);
        var mediaType = getValue(options.mediaType, Camera.MediaType.PICTURE);
        var allowEdit = !!options.allowEdit;
        var correctOrientation = !!options.correctOrientation;
        var saveToPhotoAlbum = !!options.saveToPhotoAlbum;
        var popoverOptions = getValue(options.popoverOptions, null);
        var cameraDirection = getValue(options.cameraDirection, Camera.Direction.BACK);
    
        var args = [quality, destinationType, sourceType, targetWidth, targetHeight, encodingType,
            mediaType, allowEdit, correctOrientation, saveToPhotoAlbum, popoverOptions, cameraDirection];
    
        exec(successCallback, errorCallback, 'Camera', 'takePicture', args);
        // XXX: commented out
        // return new CameraPopoverHandle();
    };

    在Camera.js中调用了exec(successCallback, errorCallback, 'Camera', 'takePicture', args),exec在Camera.js中定义了,如下

    var exec = require('cordova/exec');

    在cordova.js中定义了这个方法如下:

    // file: /Users/steveng/repo/cordova/cordova-android/cordova-js-src/exec.js
    
    define("cordova/exec", function(require, exports, module) {
    
    nativeApiProvider = require('cordova/android/nativeapiprovider'),
    
    utils = require('cordova/utils'),
    
    base64 = require('cordova/base64'),
    
    channel = require('cordova/channel'),
    
    // JS->Native的可选交互形式
    
    jsToNativeModes = {
    
    PROMPT: 0, // 基于prompt()的交互
    
    JS_OBJECT: 1// 基于JavascriptInterface的交互   
    
    },
    
    // Native->JS的可选交互形式
    
    nativeToJsModes = {
    
    POLLING: 0,// 轮询(JS->Native自助获取消息)
    
    // 使用 webView.loadUrl("javascript:") 来执行消息,解决软键盘的Bug  
    
    LOAD_URL: 1,
    
    // 拦截事件监听,使用online/offline事件来告诉JS获取消息  
    
    // 默认值 NativeToJsMessageQueue.DEFAULT_BRIDGE_MODE=2  
    
    ONLINE_EVENT: 2,
    
    EVAL_BRIDGE: 3
    
    },
    
    jsToNativeBridgeMode, // Set lazily.
    
    nativeToJsBridgeMode = nativeToJsModes.EVAL_BRIDGE,
    
    pollEnabled = false,
    
    bridgeSecret = -1;
    
    
    
    var messagesFromNative = [];
    
    var isProcessing = false;
    
    var resolvedPromise = typeof Promise == 'undefined' ? null : Promise.resolve();
    
    var nextTick = resolvedPromise ? function(fn) { resolvedPromise.then(fn); } : function(fn) { setTimeout(fn); };
    
    function androidExec(success, fail, service, action, args) {
    
    if (bridgeSecret < 0) {
    
    // If we ever catch this firing, we'll need to queue up exec()s
    
    // and fire them once we get a secret. For now, I don't think
    
    // it's possible for exec() to be called since plugins are parsed but
    
    // not run until until after onNativeReady.
    
    throw new Error('exec() called without bridgeSecret');
    
    }
    
    // 默认采用JavascriptInterface交互方式  
    
    if (jsToNativeBridgeMode === undefined) {
    
    androidExec.setJsToNativeBridgeMode(jsToNativeModes.JS_OBJECT);
    
    }
    
    
    
    // If args is not provided, default to an empty array
    
    args = args || [];
    
    
    
    // Process any ArrayBuffers in the args into a string.
    
    for (var i = 0; i < args.length; i++) {
    
    if (utils.typeName(args[i]) == 'ArrayBuffer') {
    
    args[i] = base64.fromArrayBuffer(args[i]);
    
    }
    
    }
    
    
    
    var callbackId = service + cordova.callbackId++,
    
    argsJson = JSON.stringify(args);
    
    if (success || fail) {
    
    cordova.callbacks[callbackId] = {success:success, fail:fail};
    
    }
    
    // 选择合适的交互方式和Native进行交互  
    
    // 根据Native端NativeToJsMessageQueue.DISABLE_EXEC_CHAINING的配置,回传消息可以是同步或者异步  
    
    // 默认是同步的,返回PluginResult对象的JSON串。异步的话messages为空。  
    
    var msgs = nativeApiProvider.get().exec(bridgeSecret, service, action, callbackId, argsJson);
    
     // 如果参数被传递到Java端,但是接收到的是null,切换交互方式到prompt()在执行一次,Galaxy S2在传递某些Unicode字符的
    
    //时候少数情况下有问题
    
    if (jsToNativeBridgeMode == jsToNativeModes.JS_OBJECT && msgs === "@Null arguments.") {
    
    androidExec.setJsToNativeBridgeMode(jsToNativeModes.PROMPT);
    
    androidExec(success, fail, service, action, args);
    
    // 执行完成后,把交互方式再切回JavascriptInterface
    
    androidExec.setJsToNativeBridgeMode(jsToNativeModes.JS_OBJECT);
    
    } else if (msgs) {
    
    messagesFromNative.push(msgs);
    
    // 处理Native返回的消息 
    
    nextTick(processMessages);
    
    }
    
    }
    
    ......
    
    module.exports = androidExec;
    
    });

    主要代码:var msgs = nativeApiProvider.get().exec(bridgeSecret, service, action, callbackId, argsJson);

    nativeApiProvider.get()最终返回nativeApi ,看下nativeApiProvider中是如何定义的

    // file: /Users/steveng/repo/cordova/cordova-android/cordova-js-src/android/nativeapiprovider.js
    
    define("cordova/android/nativeapiprovider", function(require, exports, module) {
    
    // WebView中是否通过addJavascriptInterface提供了访问ExposedJsApi.java的_cordovaNative对象 如果不存在选择prompt()形
    
    //式的交互方式  
    
    var nativeApi = this._cordovaNative || require('cordova/android/promptbasednativeapi');
    
    var currentApi = nativeApi;
    
    module.exports = {
    
    get: function() { return currentApi; },
    
    setPreferPrompt: function(value) {
    
    currentApi = value ? require('cordova/android/promptbasednativeapi') : nativeApi;
    
    },
    
    // Used only by tests.
    
    set: function(value) {
    
    currentApi = value;
    
    }
    
    };
    
    });

    var nativeApi = this._cordovaNative || require('cordova/android/promptbasednativeapi');

    如果this._cordovaNative有值

    这个代码是如何和java代码通信的呢,看下SystemWebViewEngine.java:

    public class SystemWebViewEngine implements CordovaWebViewEngine {
    
    ......
    
    // Yeah, we know. It'd be great if lint was just a little smarter.
    
    @SuppressLint("AddJavascriptInterface")
    
    private static void exposeJsInterface(WebView webView, CordovaBridge bridge) {
    
    SystemExposedJsApi exposedJsApi = new SystemExposedJsApi(bridge);
    
    webView.addJavascriptInterface(exposedJsApi, "_cordovaNative");
    
    }
    
    }

    我们看到了标识_cordovaNative这个字符,在看下调用的SystemExposedJsApi.java中的exec()方法:

    class SystemExposedJsApi implements ExposedJsApi {
        private final CordovaBridge bridge;
    
        SystemExposedJsApi(CordovaBridge bridge) {
            this.bridge = bridge;
        }
    
        @JavascriptInterface
        public String exec(int bridgeSecret, String service, String action, String callbackId, String arguments) throws JSONException, IllegalAccessException {
            return bridge.jsExec(bridgeSecret, service, action, callbackId, arguments);
        }
    
        @JavascriptInterface
        public void setNativeToJsBridgeMode(int bridgeSecret, int value) throws IllegalAccessException {
            bridge.jsSetNativeToJsBridgeMode(bridgeSecret, value);
        }
    
        @JavascriptInterface
        public String retrieveJsMessages(int bridgeSecret, boolean fromOnlineEvent) throws IllegalAccessException {
            return bridge.jsRetrieveJsMessages(bridgeSecret, fromOnlineEvent);
        }
    }

    加了注解@JavascriptInterface,将方法暴露给js,js就可以直接调用这个方法了和原生webview中的java与js交互完全一样,也就是说,nativeApiProvider.get().exec这个方法最终调用了SystemExposedJsApi中的exec方法,然后调用CordovaBridge中的jsExec方法(bridge.jsExec),若this._cordovaNative没值则调用promptbasednativeapi.js中的exec方法,

    // file: /Users/steveng/repo/cordova/cordova-android/cordova-js-src/android/promptbasednativeapi.js
    
    define("cordova/android/promptbasednativeapi", function(require, exports, module) {
    
    // prompt()来和Native进行交互,Native端会在SystemWebChromeClient.onJsPrompt()中拦截处理  
    
    module.exports = {
    
    exec: function(bridgeSecret, service, action, callbackId, argsJson) {
    
    return prompt(argsJson, 'gap:'+JSON.stringify([bridgeSecret, service, action, callbackId]));
    
    },
    
    setNativeToJsBridgeMode: function(bridgeSecret, value) {
    
    prompt(value, 'gap_bridge_mode:' + bridgeSecret);
    
    },
    
    //接受消息
    
    retrieveJsMessages: function(bridgeSecret, fromOnlineEvent) {
    
    return prompt(+fromOnlineEvent, 'gap_poll:' + bridgeSecret);
    
    }
    
    };
    
    
    
    });

     js 通过prompt弹窗往anroid native 发送消息

    prompt(argsJson, 'gap:'+JSON.stringify([bridgeSecret, service, action, callbackId]))

    在java端是如何截取这个消息呢,SystemWebChromeClient这个类继承了WebChromeClient,并在onJsPrompt方法中截取了这个弹窗消息:

    public boolean onJsPrompt(WebView view, String origin, String message, String defaultValue, final JsPromptResult result) {
        // Unlike the @JavascriptInterface bridge, this method is always called on the UI thread.
        String handledRet = parentEngine.bridge.promptOnJsPrompt(origin, message, defaultValue);
        if (handledRet != null) {
            result.confirm(handledRet);
        } else {
            dialogsHelper.showPrompt(message, defaultValue, new CordovaDialogsHelper.Result() {
                @Override
                public void gotResult(boolean success, String value) {
                    if (success) {
                        result.confirm(value);
                    } else {
                        result.cancel();
                    }
                }
            });
        }
        return true;
    }

    调用了CordovaBridge.java中的promptOnJsPrompt方法:

    public String promptOnJsPrompt(String origin, String message, String defaultValue) {
        if (defaultValue != null && defaultValue.length() > 3 && defaultValue.startsWith("gap:")) {
            JSONArray array;
            try {
                array = new JSONArray(defaultValue.substring(4));
                int bridgeSecret = array.getInt(0);
                String service = array.getString(1);
                String action = array.getString(2);
                String callbackId = array.getString(3);
                String r = jsExec(bridgeSecret, service, action, callbackId, message);
                return r == null ? "" : r;
            } catch (JSONException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
            return "";
        }
        // Sets the native->JS bridge mode.
        else if (defaultValue != null && defaultValue.startsWith("gap_bridge_mode:")) {
            try {
                int bridgeSecret = Integer.parseInt(defaultValue.substring(16));
                jsSetNativeToJsBridgeMode(bridgeSecret, Integer.parseInt(message));
            } catch (NumberFormatException e){
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
            return "";
        }
        // Polling for JavaScript messages
        else if (defaultValue != null && defaultValue.startsWith("gap_poll:")) {
            int bridgeSecret = Integer.parseInt(defaultValue.substring(9));
            try {
                String r = jsRetrieveJsMessages(bridgeSecret, "1".equals(message));
                return r == null ? "" : r;
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
            return "";
        }
        else if (defaultValue != null && defaultValue.startsWith("gap_init:")) {
            // Protect against random iframes being able to talk through the bridge.
            // Trust only pages which the app would have been allowed to navigate to anyway.
            if (pluginManager.shouldAllowBridgeAccess(origin)) {
                // Enable the bridge
                int bridgeMode = Integer.parseInt(defaultValue.substring(9));
                jsMessageQueue.setBridgeMode(bridgeMode);
                // Tell JS the bridge secret.
                int secret = generateBridgeSecret();
                return ""+secret;
            } else {
                LOG.e(LOG_TAG, "gap_init called from restricted origin: " + origin);
            }
            return "";
        }
        return null;
    }

    var nativeApi = this._cordovaNative || require('cordova/android/promptbasednativeapi');

    var currentApi = nativeApi;

    最后也是将数据传递到了jsExec方法中,即nativeApi不管是哪种js与native的交互都是回调了CordovaBridge.java中的jsExec方法,在看下jsExec方法:

    public String jsExec(int bridgeSecret, String service, String action, String callbackId, String arguments) throws JSONException, IllegalAccessException {
        if (!verifySecret("exec()", bridgeSecret)) {
            return null;
        }
        // If the arguments weren't received, send a message back to JS.  It will switch bridge modes and try again.  See CB-2666.
        // We send a message meant specifically for this case.  It starts with "@" so no other message can be encoded into the same string.
        if (arguments == null) {
            return "@Null arguments.";
        }
    
        jsMessageQueue.setPaused(true);
        try {
            // Tell the resourceApi what thread the JS is running on.
            CordovaResourceApi.jsThread = Thread.currentThread();
    
            pluginManager.exec(service, action, callbackId, arguments);
            String ret = null;
            if (!NativeToJsMessageQueue.DISABLE_EXEC_CHAINING) {
                ret = jsMessageQueue.popAndEncode(false);
            }
            return ret;
        } catch (Throwable e) {
            e.printStackTrace();
            return "";
        } finally {
            jsMessageQueue.setPaused(false);
        }
    }

    里面调用了pluginManager.exec(service, action, callbackId, arguments)方法,在PluginManager.java中

    public void exec(final String service, final String action, final String callbackId, final String rawArgs) {
        CordovaPlugin plugin = getPlugin(service);
        if (plugin == null) {
            LOG.d(TAG, "exec() call to unknown plugin: " + service);
            PluginResult cr = new PluginResult(PluginResult.Status.CLASS_NOT_FOUND_EXCEPTION);
            app.sendPluginResult(cr, callbackId);
            return;
        }
        CallbackContext callbackContext = new CallbackContext(callbackId, app);
        try {
            long pluginStartTime = System.currentTimeMillis();
            boolean wasValidAction = plugin.execute(action, rawArgs, callbackContext);
            long duration = System.currentTimeMillis() - pluginStartTime;
    
            if (duration > SLOW_EXEC_WARNING_THRESHOLD) {
                LOG.w(TAG, "THREAD WARNING: exec() call to " + service + "." + action + " blocked the main thread for " + duration + "ms. Plugin should use CordovaInterface.getThreadPool().");
            }
            if (!wasValidAction) {
                PluginResult cr = new PluginResult(PluginResult.Status.INVALID_ACTION);
                callbackContext.sendPluginResult(cr);
            }
        } catch (JSONException e) {
            PluginResult cr = new PluginResult(PluginResult.Status.JSON_EXCEPTION);
            callbackContext.sendPluginResult(cr);
        } catch (Exception e) {
            LOG.e(TAG, "Uncaught exception from plugin", e);
            callbackContext.error(e.getMessage());
        }
    }
    CordovaPlugin plugin = getPlugin(service);根据service名称获取插件对象
     /**
         * Get the plugin object that implements the service.
         * If the plugin object does not already exist, then create it.
         * If the service doesn't exist, then return null.
         *
         * @param service       The name of the service.
         * @return              CordovaPlugin or null
         */
        public CordovaPlugin getPlugin(String service) {
            CordovaPlugin ret = pluginMap.get(service);
            if (ret == null) {
                PluginEntry pe = entryMap.get(service);
                if (pe == null) {
                    return null;
                }
                if (pe.plugin != null) {
                    ret = pe.plugin;
                } else {
                    ret = instantiatePlugin(pe.pluginClass);
                }
                ret.privateInitialize(service, ctx, app, app.getPreferences());
                pluginMap.put(service, ret);
            }
            return ret;
        }

    然后调用了plugin.execute(action, rawArgs, callbackContext)方法,execute是CordovaPlugin.java中的一个方法,而我们的拍照类CameraLauncher 继承了CordovaPlugin,并重写了execute方法,在CordovaPlugin中有这么一行注释说的很清楚:

    /**
     * Plugins must extend this class and override one of the execute methods.
     */
    public class CordovaPlugin {......}

    CameraLauncher.java代码如下:

    public class CameraLauncher extends CordovaPlugin implements MediaScannerConnectionClient {
    
    ......
    
    public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
    
    ......
    
    if (action.equals("takePicture")) {
    
    ......
    
    return true;
    
    }
    
    return false;
    
    }
    
    }

    最初代码是通过

    exec(successCallback, errorCallback, 'Camera', 'takePicture', args);来调用相册的,字符串takePicture就是通过这一步步传递过来的,当然调用完之后还有一系列的拿到返回值的操作,在CameraLauncher中的getImage方法中,通过startActivityForResult方法及onActivityResult方法获取选中的图片信息,在processResultFromGallery方法中调用CallbackContext.java中的success方法,然后通过webView.sendPluginResult(pluginResult, callbackId)方法调用了webview的sendPluginresult方法:
    public void sendPluginResult(PluginResult cr, String callbackId) {
            nativeToJsMessageQueue.addPluginResult(cr, callbackId);
        }
    
    private final LinkedList<JsMessage> queue = new LinkedList<JsMessage>();
    queue.add(message);

    将信息添加到消息队列中,js通过prompt(+fromOnlineEvent, 'gap_poll:' + bridgeSecret)接受消息,字符串gap_poll后面会用到

    前面说过在CordovaBridge中的promptOnJsPrompt方法中会拦截prompt方法,如下:

      // Polling for JavaScript messages
            else if (defaultValue != null && defaultValue.startsWith("gap_poll:")) {
                int bridgeSecret = Integer.parseInt(defaultValue.substring(9));
                try {
                    String r = jsRetrieveJsMessages(bridgeSecret, "1".equals(message));
                    return r == null ? "" : r;
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
                return "";
            }

     最终调用了NativeToJsMessageQueue.java中的popAndEncode方法返回队列中的消息数据,如下:

       /**
         * Combines and returns queued messages combined into a single string.
         * Combines as many messages as possible, while staying under MAX_PAYLOAD_SIZE.
         * Returns null if the queue is empty.
         */
        public String popAndEncode(boolean fromOnlineEvent) {
            synchronized (this) {
                if (activeBridgeMode == null) {
                    return null;
                }
                activeBridgeMode.notifyOfFlush(this, fromOnlineEvent);
                if (queue.isEmpty()) {
                    return null;
                }
                int totalPayloadLen = 0;
                int numMessagesToSend = 0;
                for (JsMessage message : queue) {
                    int messageSize = calculatePackedMessageLength(message);
                    if (numMessagesToSend > 0 && totalPayloadLen + messageSize > MAX_PAYLOAD_SIZE && MAX_PAYLOAD_SIZE > 0) {
                        break;
                    }
                    totalPayloadLen += messageSize;
                    numMessagesToSend += 1;
                }
    
                StringBuilder sb = new StringBuilder(totalPayloadLen);
                for (int i = 0; i < numMessagesToSend; ++i) {
                    JsMessage message = queue.removeFirst();
                    packMessage(message, sb);
                }
    
                if (!queue.isEmpty()) {
                    // Attach a char to indicate that there are more messages pending.
                    sb.append('*');
                }
                String ret = sb.toString();
                return ret;
            }
        }

     

    看到这里对于我们本次的需求,在外壳添加几个插件就可以动手了。

     

    展开全文
  • 打开安卓或苹果的原生相册,选择照片或者视频,完成后返回文件路径,可以根据路径进行文件读取。 插件名称: Image and Video Picker 集成步骤: 把ImageAndVideoPicker->Prefab->PickerEventListener放入到...

    相册选择插件集成说明
    功能:
    打开安卓或苹果的原生相册,选择照片或者视频,完成后返回文件路径,可以根据路径进行文件读取。 
    插件名称:
    Image and Video Picker 
    集成步骤:

    1. 把ImageAndVideoPicker->Prefab->PickerEventListener放入到场景内
    2. 增加相册操作的回调方法,可以参考ImageAndVideoPicker->Example->IVPickerExample.cs
    3. 提供的回调方法如下:
    4. onImageSelect(当选择图片)
    5. onImageLoad(当加载图片)
    6. onVideoSelect(当选择了视频)
    7. onError(当出现错误)
    8. onCancel(当取消选择)
    9. 根据不同的平台调用方法拉起相册(方法内的参数请查看插件提供的文档)

    安卓读取图片方法:AndroidPicker.BrowseImage(false)
    安卓读取视频方法:AndroidPicker.BrowseVideo()
    苹果读取图片方法:IOSPicker.BrowseImage(false)
    苹果读取视频方法:IOSPicker.BrowseVideo()

    展开全文
  • 1、android自定义相册,实现了拍照、图片选择(单选/多选)、 裁剪(单/多裁剪)、旋转、ImageLoader无绑定任由开发者选择、功能可配置、主题样式可配置。GalleryFinal为你定制相册。通过Gradle抓取:compile'...

    1、android自定义相册,实现了拍照、图片选择(单选/多选)、 裁剪(单/多裁剪)、旋转、ImageLoader无绑定任由开发者选择、功能可配置、主题样式可配置。GalleryFinal为你定制相册。

    通过Gradle抓取:compile'cn.finalteam:galleryfinal:1.4.8.7'

    2、精仿iOS的PickerView控件,有时间选择和选项选择并支持一二三级联动效果

    ——TimePickerView时间选择器,支持年月日时分,年月日,年月,时分等格式

    ——OptionsPickerView选项选择器,支持一,二,三级选项选择,并且可以设置是否联动

    通过Gradle抓取:compile'com.bigkoo:pickerview:2.0.8'

    3、Listview滑动日历

    通过Gradle抓取:compile'com.github.traex.calendarlistview:library:1.2.3'

    GitHub地址:https://github.com/traex/CalendarListview

    4、自定义形状的ImageView

    通过Gradle抓取:

    compile'com.github.siyamed:android-shape-imageview:0.9.+@aar'

    GitHub地址:https://github.com/siyamed/android-shape-imageview

    5、PhotoView图片浏览缩放控件

    通过Gradle抓取:compile'com.bm.photoview:library:1.3.6'

    GitHub地址:https://github.com/siyamed/android-shape-imageview

    6、周日历Android Week View

    通过Gradle抓取:compile'com.github.alamkanak:android-week-view:1.2.6'

    GitHub地址:https://github.com/alamkanak/Android-Week-View

    7、首页banner

    通过Gradle抓取:compile'com.bigkoo:convenientbanner:2.0.5'

    GitHub地址:https://github.com/saiwu-bigkoo/Android-ConvenientBanner

    8、各种类型统计图

    通过Gradle抓取:compile'com.github.PhilJay:MPAndroidChart:v2.2.4'

    GitHub地址:https://github.com/PhilJay/MPAndroidChart

    9、开源表情键盘

    通过Gradle抓取:

    compile'com.github.w446108264:XhsEmoticonsKeyboard:2.0.0'

    GitHub地址:https://github.com/w446108264/XhsEmoticonsKeyboard

    10、Async-Http网络请求框架

    通过Gradle抓取:compile'com.loopj.android:android-async-http:1.4.9'

    GitHub地址:https://github.com/loopj/android-async-http

    11、Picasso图片加载框架

    通过Gradle抓取:compile'com.squareup.picasso:picasso:2.5.2'

    官网地址:http://square.github.io/picasso/

    12、依赖注解框架Butter Knife

    通过Gradle抓取:compile'com.jakewharton:butterknife:7.0.1'

    官网地址:http://jakewharton.github.io/butterknife/

    展开全文
  • 经过诸多摸索与Android端、IOS端的相机相册插件寻找与改写 先是在github中找到了一个UnityNativeCamera的项目,经过测试可以在安卓与IOS双端完美运行,只是缺少相册功能。 后来经过修改项目安卓代码与ios代码,...
  • android特效 插件

    2018-09-10 16:17:45
    各种帮助类汇总:https://github.com/Blankj/AndroidUtilCode 常用的 iOS 风格 dialog 和 meterial design 风格的 dialog:https://github.com/glassLake/DialogUtils 提高 Android 应用的颜值,酷炫 UI 组件...
  • ------本文转载自 Android插件化原理解析——contentprovider的插件化  这一系列的文章实在是写的好!  1, 概述 目前为止我们已经完成了Android四大组件中 Activity,Service以及BroadcastReceiver的插件化...
  • Unity Android 打开相册和摄像头

    千次阅读 热门讨论 2018-11-17 14:10:07
    需求:要实现打开手机的相册和摄像头,选择照片或者拍照后,在unity进行。   1.android插件 我使用的是AndroidStuido来写插件,下面是一步步介绍流程 (1)创建android工程 注意红框里面的东西,要...
  • 查看更多 平台兼容性 Android iOS 适用版本区间:5.0 - 11.0 × 原生插件通用使用流程: 购买插件,选择该插件绑定的项目。 在HBuilderX里找到项目,在manifest的app原生插件配置中勾选模块,如需要填写参数则参考...
  • cordova-android保存图片插件,不知道为何不适用ios,仅供参考
  • 2.下载Android stdio 3.编写插件 4.导入插件 5.unity调用 6.打包运行 1.sdk、jdk 网上教程多的是,这里不再赘述,但是需要注意的是,在安装sdk的时候,你所安装的 SDKPlatform 的API,要和Android SDK Build-tools相...
  • 这是一款基于jQuery的手风琴相册集播放插件相册分四组,每一组均可展开播放相片。 建议开发童鞋使用统一开发环境UDE来进行查看、调试、开发哦~~其属于MM应用引擎的应用开发部署工具,它是基于云计算基础架构,...
  • 目前为止我们已经完成了Android四大组件中Activity,Service以及BroadcastReceiver的插件化,这几个组件各不相同,我们根据它们的特点定制了不同的插件化方案;那么对于ContentProvider,它又有什么特点?应该如何...
  • unityplugins Unity Android插件,集成功能: 调用Android相机、相册,以实现头像上传功能;
  • UnityNativeGallery-master.zip 用于unity访问相册等功能 比较方便快捷
  • ——下载插件并导入Unity: ——如果是Android设备的话需要设置写权限到外部卡:PlayerSettings—Player—OtherSettings—Write Permission选择External(SDCard) ——代码实现点击按钮拉取手机相册 using ...
  • 集成一个第三方相册功能,只需集成一个插件APK到项目中,无需集成额外代码,并且支持随时更新相册功能,无需发布版本更新,无需AndroidManifest中声明四大组件,这就是插件化。  插件化可利用性很广,但事实上...
  • 从零开发一个Android高德地图插件

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 4,181
精华内容 1,672
关键字:

安卓相册插件