1. 程式人生 > >Android WebView嵌入H5之file頭像上傳檔案,適配所有版本

Android WebView嵌入H5之file頭像上傳檔案,適配所有版本

 最近公司需要開發新的專案,為了節省時間成本,要求整體嵌入H5介面。對,沒看錯是整體嵌入,心中一萬隻草泥馬奔騰,沒辦法大佬就是大佬,還是的照做。

今天來說說h5上傳頭像的問題吧。網上有很多的解決方案,開始我覺得好像很簡單,都有成功案例,等把程式碼copy下來,發現並不能執行成功,真是一臉懵逼...N臉懵逼。

先說說為什麼別人的程式碼都說測試通過了而在我這裡都不行了呢?

去google了一把,因為在4.4之後的核心不再是基於webkit核心,禁用了file域來讀取手機本地檔案,臥槽不是說向下相容的麼,相容尼瑪啊。上張圖,

這就是為什麼拍照之後並不能正確讀取到資料。問題找到之後,那麼解決就很好解決了。拍完照片之後,根據路徑把圖片插入到系統圖庫裡面,完美解決。

 File cameraFile = new File(mCameraFilePath);

                    Bitmap bitmap1 = getimage(cameraFile.getPath());

                    result = Uri.parse(MediaStore.Images.Media.insertImage(
                            mContext.getContentResolver(), bitmap1, null, null));
                Log.e(TAG,"5.0-result="+result);
                uploadMessage.onReceiveValue(result);
下面在解決一下系統版本相容的問題,需要重寫WebChromeClient的以下方法,因為方法中有hide,你只需要重寫就是。
// For Android < 3.0
    public void openFileChooser(ValueCallback<Uri> valueCallback) {
        uploadMessage = valueCallback;
        openImageChooserActivity();
    }

    // For Android  >= 3.0
    public void openFileChooser(ValueCallback valueCallback, String acceptType) {
        uploadMessage = valueCallback;
        openImageChooserActivity();
    }

    //For Android  >= 4.1
    public void openFileChooser(ValueCallback<Uri> valueCallback, String acceptType, String capture) {
        uploadMessage = valueCallback;
        openImageChooserActivity();
    }

    // For Android >= 5.0
    @Override
    public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, WebChromeClient.FileChooserParams fileChooserParams) {
        uploadMessageAboveL = filePathCallback;
        openImageChooserActivity();
        return true;
    }
看見有的同學,說重寫不起作用,我暫時是沒遇到的,如果遇到了再解決吧。下面貼一個webchromeclient 的完整程式碼,你只需要拷過去,簡單呼叫,在新增以下許可權就可以了。
package com.choe.webviewdemo2;

import android.annotation.TargetApi;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.ClipData;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.MediaStore;
import android.util.Log;
import android.webkit.ValueCallback;
import android.webkit.WebChromeClient;
import android.webkit.WebStorage;
import android.webkit.WebView;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;

/**
 * @author cyk
 * @date 2016/12/14 14:02
 * @email [email protected]
 * @desc
 * @modifier
 * @modify_time
 * @modify_remark
 */
public class MyWebChromeClient extends WebChromeClient {
    private Activity mContext;
    private String mCameraFilePath;
    private  ValueCallback<Uri> uploadMessage;
    private  ValueCallback<Uri[]> uploadMessageAboveL;
    private final static String TAG="MyWebChromeClient";
    private final static int FILE_CHOOSER_RESULT_CODE = 10000;


    public MyWebChromeClient(Activity context) {
        mContext = context;
    }

    @Override
    public void onProgressChanged(WebView view, int newProgress) {
        super.onProgressChanged(view, newProgress);
    }


    // For Android < 3.0
    public void openFileChooser(ValueCallback<Uri> valueCallback) {
        uploadMessage = valueCallback;
        openImageChooserActivity();
    }

    // For Android  >= 3.0
    public void openFileChooser(ValueCallback valueCallback, String acceptType) {
        uploadMessage = valueCallback;
        openImageChooserActivity();
    }

    //For Android  >= 4.1
    public void openFileChooser(ValueCallback<Uri> valueCallback, String acceptType, String capture) {
        uploadMessage = valueCallback;
        openImageChooserActivity();
    }

    // For Android >= 5.0
    @Override
    public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, WebChromeClient.FileChooserParams fileChooserParams) {
        uploadMessageAboveL = filePathCallback;
        openImageChooserActivity();
        return true;
    }

    @Override
    //擴容
    public void onReachedMaxAppCacheSize(long requiredStorage, long quota, WebStorage.QuotaUpdater quotaUpdater) {
        quotaUpdater.updateQuota(requiredStorage*2);
    }

    @Override
    public void onConsoleMessage(String message, int lineNumber, String sourceID) {
        Log.e("h5端的log", String.format("%s -- From line %s of %s", message, lineNumber, sourceID));
    }


    private void openImageChooserActivity() {

        initDialog();
    }

    /**
     * 上傳頭像時的彈出框
     */
    private void initDialog(){
        new AlertDialog.Builder(mContext)
                .setTitle("更改頭像")
                .setItems(new String[]{"拍照", "相簿選取"},
                        new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog,
                                                int which) {
                                switch (which) {
                                    case 0:
                                        Intent i1=createCameraIntent();
                                        mContext.startActivityForResult(Intent.createChooser(i1, "Image Chooser"), FILE_CHOOSER_RESULT_CODE);
                                        break;
                                    case 1:
                                        Intent i=createFileItent();
                                        mContext.startActivityForResult(Intent.createChooser(i, "Image Chooser"), FILE_CHOOSER_RESULT_CODE);
                                        break;
                                }

                            }
                        }).setNegativeButton("取消", null).show();
    }

    /**
     * 建立選擇相簿的intent
     * @return
     */
    private Intent createFileItent(){
        Intent i = new Intent(Intent.ACTION_GET_CONTENT);
        i.addCategory(Intent.CATEGORY_OPENABLE);
        i.setType("image/*");

        Intent   intent = new Intent(
                Intent.ACTION_PICK,
                MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
        intent.setDataAndType(
                MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                "image/*");
        return intent;
    }

    /**
     * 建立呼叫照相機的intent
     * @return
     */
    private Intent createCameraIntent() {
        VersionUtils.checkAndRequestPermissionAbove23(mContext);
        Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);

        File externalDataDir = Environment
                .getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM);
        System.out.println("externalDataDir:" + externalDataDir);
        File cameraDataDir = new File(externalDataDir.getAbsolutePath()
                + File.separator + "browser-photo");
        cameraDataDir.mkdirs();
        mCameraFilePath = cameraDataDir.getAbsolutePath() + File.separator
                + System.currentTimeMillis() + ".jpg";
        System.out.println("mcamerafilepath:" + mCameraFilePath);
        cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT,
                Uri.fromFile(new File(mCameraFilePath)));

        return cameraIntent;
    }

    /**
     * 處理拍照返回函式
     * @param requestCode
     * @param resultCode
     * @param data
     */
    public  void onActivityResult(int requestCode, int resultCode, Intent data){
        if (requestCode == FILE_CHOOSER_RESULT_CODE) {
            if (null == uploadMessage&& null == uploadMessageAboveL)
                return;
            Uri result = data == null || resultCode != Activity.RESULT_OK ? null
                    : data.getData();
            if (uploadMessageAboveL != null) {//5.0以上
                onActivityResultAboveL(requestCode, resultCode, data);
            }else if(uploadMessage != null) {
                if (result == null && data == null
                        && resultCode == Activity.RESULT_OK) {
                    File cameraFile = new File(mCameraFilePath);

                    Bitmap bitmap1 = getimage(cameraFile.getPath());

                    result = Uri.parse(MediaStore.Images.Media.insertImage(
                            mContext.getContentResolver(), bitmap1, null, null));
                }
                Log.e(TAG,"5.0-result="+result);
                uploadMessage.onReceiveValue(result);
                uploadMessage = null;
            }


        }
    }

    /**
     * 處理拍照返回函式  5。0以上
     * @param requestCode
     * @param resultCode
     * @param intent
     */
    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    private void onActivityResultAboveL(int requestCode, int resultCode, Intent intent) {
        Log.e(TAG,"5.0+ 返回了");
        if (requestCode != FILE_CHOOSER_RESULT_CODE || uploadMessageAboveL == null)
            return;
        Uri[] results = null;
        if (resultCode == Activity.RESULT_OK) {
            if (intent != null) {
                String dataString = intent.getDataString();
                ClipData clipData = intent.getClipData();
                if (clipData != null) {
                    results = new Uri[clipData.getItemCount()];
                    for (int i = 0; i < clipData.getItemCount(); i++) {
                        ClipData.Item item = clipData.getItemAt(i);
                        results[i] = item.getUri();
                    }
                }
                if (dataString != null)
                    results = new Uri[]{Uri.parse(dataString)};
            }else {

                File cameraFile = new File(mCameraFilePath);

                Bitmap bitmap1 = getimage(cameraFile.getPath());

                Uri result = Uri.parse(MediaStore.Images.Media.insertImage(
                        mContext.getContentResolver(), bitmap1, null, null));
                results=new Uri[]{result};
            }
        }
        uploadMessageAboveL.onReceiveValue(results);
        uploadMessageAboveL = null;
    }

    /**
     * 根據圖片路徑獲取圖p片
     * @param srcPath
     * @return
     */
    private Bitmap getimage(String srcPath) {
        BitmapFactory.Options newOpts = new BitmapFactory.Options();
        // 開始讀入圖片,此時把options.inJustDecodeBounds 設回true了
        newOpts.inJustDecodeBounds = true;
        Bitmap bitmap = BitmapFactory.decodeFile(srcPath, newOpts);// 此時返回bm為空

        newOpts.inJustDecodeBounds = false;
        int w = newOpts.outWidth;
        int h = newOpts.outHeight;
        // 現在主流手機比較多是800*480解析度,所以高和寬我們設定為
        float hh = 800f;// 這裡設定高度為800f
        float ww = 480f;// 這裡設定寬度為480f
        // 縮放比。由於是固定比例縮放,只用高或者寬其中一個數據進行計算即可
        int be = 1;// be=1表示不縮放
        if (w > h && w > ww) {// 如果寬度大的話根據寬度固定大小縮放
            be = (int) (newOpts.outWidth / ww);
        } else if (w < h && h > hh) {// 如果高度高的話根據寬度固定大小縮放
            be = (int) (newOpts.outHeight / hh);
        }
        if (be <= 0)
            be = 1;
        newOpts.inSampleSize = be;// 設定縮放比例
        // 重新讀入圖片,注意此時已經把options.inJustDecodeBounds 設回false了
        bitmap = BitmapFactory.decodeFile(srcPath, newOpts);
        return compressImage(bitmap);// 壓縮好比例大小後再進行質量壓縮
    }

    /**
     * 裁剪圖片大小
     * @param image
     * @return
     */
    private Bitmap compressImage(Bitmap image) {

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        image.compress(Bitmap.CompressFormat.JPEG, 100, baos);// 質量壓縮方法,這裡100表示不壓縮,把壓縮後的資料存放到baos中
        int options = 100;
        while (baos.toByteArray().length / 1024 > 100) { // 迴圈判斷如果壓縮後圖片是否大於100kb,大於繼續壓縮
            baos.reset();// 重置baos即清空baos
            image.compress(Bitmap.CompressFormat.JPEG, options, baos);// 這裡壓縮options%,把壓縮後的資料存放到baos中
            options -= 10;// 每次都減少10
        }
        ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());// 把壓縮後的資料baos存放到ByteArrayInputStream中
        Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, null);// 把ByteArrayInputStream資料生成圖片
        return bitmap;
    }
}

在webview的介面,需要重寫onActivityResult方法,並呼叫處理選擇圖片返回的方法。

protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        Log.e(TAG,"收到返回訊息了");
        mMyWebChromeClient.onActivityResult(requestCode,resultCode,data);
    }
新增上這些東西,上傳頭像基本搞定。

忘了說了,適配6.0的時候,還需要進行一下許可權申請,拍照屬於危險許可權。

/**
     * 6.0 手機系統以上 檢查並請求許可權
     * @param context 必須為 Activity
     */
    public static void checkAndRequestPermissionAbove23(Activity context){
        int i = ContextCompat.checkSelfPermission(context, Manifest.permission.CAMERA);
        if (i!= PackageManager.PERMISSION_GRANTED){
            ActivityCompat.requestPermissions(context,
                    new String[]{Manifest.permission.CAMERA},Constant.TAKE_PHOTO_PERMISSION
            );
        }
    }

到這裡,基本就適配了所有版本,親測可用。