最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

詳解Android WebView的input上傳照片的兼容問(wèn)題

 更新時(shí)間:2017年08月15日 11:08:22   作者:指間沙似流年  
本篇文章主要介紹了詳解Android WebView的input上傳照片的兼容問(wèn)題,非常具有實(shí)用價(jià)值,需要的朋友可以參考下

問(wèn)題

前幾天接到的一個(gè)需求,是關(guān)于第三方理財(cái)產(chǎn)品的H5上傳照片問(wèn)題。

對(duì)方說(shuō)他們的新的需求,需要接入方配合上傳資產(chǎn)照片的需求,測(cè)試之后發(fā)現(xiàn)我們這邊的app端,IOS端上傳沒(méi)有問(wèn)題,而Android端則點(diǎn)擊沒(méi)有任何反應(yīng)。

對(duì)方H5調(diào)用的方式是通過(guò)<input type='file' accept='image/*'/>的方式調(diào)用,本來(lái)以為這個(gè)問(wèn)題很簡(jiǎn)單,就是app端沒(méi)有設(shè)置相機(jī)權(quán)限,造成的點(diǎn)擊無(wú)反應(yīng)情況,而實(shí)際上加了之后發(fā)現(xiàn),并非簡(jiǎn)單的權(quán)限問(wèn)題。

解決問(wèn)題

因?yàn)锳ndroid的版本碎片問(wèn)題,很多版本的WebView都對(duì)喚起函數(shù)有不同的支持。

我們需要重寫(xiě)WebChromeClient下的openFileChooser()(5.0及以上系統(tǒng)回調(diào)onShowFileChooser())。我們通過(guò)Intent在openFileChooser()中喚起系統(tǒng)相機(jī)和支持Intent的相關(guān)app。

在系統(tǒng)相機(jī)或者相關(guān)app中一頓操作之后,當(dāng)返回app的時(shí)候,我們?cè)趏nActivityResult()中將選擇好的圖片通過(guò)ValueCallback的onReceiveValue方法返回給WebView。

附上代碼:

1、首先是重寫(xiě)各個(gè)版本的WebChromeClient的支持

webView.setWebChromeClient(new WebChromeClient() {
 //For Android 3.0+
 public void openFileChooser(ValueCallback<Uri> uploadMsg) {
   selectImage();
   mUM = uploadMsg;
   Intent i = new Intent(Intent.ACTION_GET_CONTENT);
   i.addCategory(Intent.CATEGORY_OPENABLE);
   i.setType("*/*");
   MyBaseWebViewActivity.this.startActivityForResult(Intent.createChooser(i, "File Chooser"), FCR);
 }

 // For Android 3.0+, above method not supported in some android 3+ versions, in such case we use this
 public void openFileChooser(ValueCallback uploadMsg, String acceptType) {
   selectImage();
   mUM = uploadMsg;
   Intent i = new Intent(Intent.ACTION_GET_CONTENT);
   i.addCategory(Intent.CATEGORY_OPENABLE);
   i.setType("*/*");
   MyBaseWebViewActivity.this.startActivityForResult(
       Intent.createChooser(i, "File Browser"),
       FCR);
 }

 //For Android 4.1+
 public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
   selectImage();
   mUM = uploadMsg;
   Intent i = new Intent(Intent.ACTION_GET_CONTENT);
   i.addCategory(Intent.CATEGORY_OPENABLE);
   i.setType("*/*");
   MyBaseWebViewActivity.this.startActivityForResult(Intent.createChooser(i, "File Chooser"), MyBaseWebViewActivity.FCR);
 }

 //For Android 5.0+
 public boolean onShowFileChooser(
     WebView webView, ValueCallback<Uri[]> filePathCallback,
     WebChromeClient.FileChooserParams fileChooserParams) {
   selectImage();
   if (mUMA != null) {
     mUMA.onReceiveValue(null);
   }
   mUMA = filePathCallback;
   Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
   if (takePictureIntent.resolveActivity(MyBaseWebViewActivity.this.getPackageManager()) != null) {
     File photoFile = null;
     try {
       photoFile = createImageFile();
       takePictureIntent.putExtra("PhotoPath", mCM);
     } catch (IOException ex) {
       Log.e(TAG, "Image file creation failed", ex);
     }
     if (photoFile != null) {
       mCM = "file:" + photoFile.getAbsolutePath();
       filePath = photoFile.getAbsolutePath();
       takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(photoFile));
     } else {
       takePictureIntent = null;
     }
   }
   Intent contentSelectionIntent = new Intent(Intent.ACTION_GET_CONTENT);
   contentSelectionIntent.addCategory(Intent.CATEGORY_OPENABLE);
   contentSelectionIntent.setType("*/*");
   Intent[] intentArray;
   if (takePictureIntent != null) {
     intentArray = new Intent[]{takePictureIntent};
   } else {
     intentArray = new Intent[0];
   }

   Intent chooserIntent = new Intent(Intent.ACTION_CHOOSER);
   chooserIntent.putExtra(Intent.EXTRA_INTENT, contentSelectionIntent);
   chooserIntent.putExtra(Intent.EXTRA_TITLE, "Image Chooser");
   chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentArray);
   startActivityForResult(chooserIntent, FCR);
   return true;
 }
});

2、選完照片之后

/**
* 打開(kāi)圖庫(kù),同時(shí)處理圖片
*/
private void selectImage() {
  compressPath = Environment.getExternalStorageDirectory().getPath() + "/QWB/temp";
  File file = new File(compressPath);
  if (!file.exists()) {
    file.mkdirs();
  }
  compressPath = compressPath + File.separator + "compress.png";
  File image = new File(compressPath);
  if (image.exists()) {
    image.delete();
  }
}

// Create an image file
private File createImageFile() throws IOException {
  @SuppressLint("SimpleDateFormat") String timeStamp = DateUtils.nowTimeDetail();
  String imageFileName = "img_" + timeStamp + "_";
  File storageDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
  return File.createTempFile(imageFileName, ".jpg", storageDir);
}

private String mCM;
private String filePath = "";
private ValueCallback<Uri> mUM;
private ValueCallback<Uri[]> mUMA;
private final static int FCR = 1;
String compressPath = "";

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
  super.onActivityResult(requestCode, resultCode, intent);
  if (Build.VERSION.SDK_INT >= 21) {
    Uri[] results = null;
    //Check if response is positive
    if (resultCode == Activity.RESULT_OK) {
      if (requestCode == FCR) {
        if (null == mUMA) {
          return;
        }
        if (intent == null) {
          //Capture Photo if no image available
          if (mCM != null) {
            // results = new Uri[]{Uri.parse(mCM)};
            results = new Uri[]{afterChosePic(filePath, compressPath)};
          }
        } else {
          String dataString = intent.getDataString();
          if (dataString != null) {
            results = new Uri[]{Uri.parse(dataString)};
            LogUtil.d("tag", intent.toString());
//              String realFilePath = getRealFilePath(Uri.parse(dataString));
//              results = new Uri[]{afterChosePic(realFilePath, compressPath)};
          }
        }
      }
    }
    mUMA.onReceiveValue(results);
    mUMA = null;
  } else {
    if (requestCode == FCR) {
      if (null == mUM) return;
      Uri result = intent == null || resultCode != RESULT_OK ? null : intent.getData();
      mUM.onReceiveValue(result);
      mUM = null;
    }
  }
}

/**
* 選擇照片后結(jié)束
*/
private Uri afterChosePic(String oldPath, String newPath) {
  File newFile;
  try {
    newFile = FileUtils.compressFile(oldPath, newPath);
  } catch (Exception e) {
    e.printStackTrace();
    newFile = null;
  }
  return Uri.fromFile(newFile);
}

3、工具類(lèi)

public class FileUtils {
  /**
   * 把圖片壓縮到200K
   *
   * @param oldpath
   *      壓縮前的圖片路徑
   * @param newPath
   *      壓縮后的圖片路徑
   * @return
   */
  public static File compressFile(String oldpath, String newPath) {
    Bitmap compressBitmap = FileUtils.decodeFile(oldpath);
    Bitmap newBitmap = ratingImage(oldpath, compressBitmap);
    ByteArrayOutputStream os = new ByteArrayOutputStream();
    newBitmap.compress(Bitmap.CompressFormat.PNG, 100, os);
    byte[] bytes = os.toByteArray();

    File file = null ;
    try {
      file = FileUtils.getFileFromBytes(bytes, newPath);
    } catch (Exception e) {
      e.printStackTrace();
    }finally{
      if(newBitmap != null ){
        if(!newBitmap.isRecycled()){
          newBitmap.recycle();
        }
        newBitmap = null;
      }
      if(compressBitmap != null ){
        if(!compressBitmap.isRecycled()){
          compressBitmap.recycle();
        }
        compressBitmap = null;
      }
    }
    return file;
  }

  private static Bitmap ratingImage(String filePath,Bitmap bitmap){
    int degree = readPictureDegree(filePath);
    return rotaingImageView(degree, bitmap);
  }

  /**
   * 旋轉(zhuǎn)圖片
   * @param angle
   * @param bitmap
   * @return Bitmap
   */
  public static Bitmap rotaingImageView(int angle , Bitmap bitmap) {
    //旋轉(zhuǎn)圖片 動(dòng)作
    Matrix matrix = new Matrix();;
    matrix.postRotate(angle);
    System.out.println("angle2=" + angle);
    // 創(chuàng)建新的圖片
    Bitmap resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0,
        bitmap.getWidth(), bitmap.getHeight(), matrix, true);
    return resizedBitmap;
  }

  /**
   * 讀取圖片屬性:旋轉(zhuǎn)的角度
   * @param path 圖片絕對(duì)路徑
   * @return degree旋轉(zhuǎn)的角度
   */
  public static int readPictureDegree(String path) {
    int degree = 0;
    try {
      ExifInterface exifInterface = new ExifInterface(path);
      int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
      switch (orientation) {
        case ExifInterface.ORIENTATION_ROTATE_90:
          degree = 90;
          break;
        case ExifInterface.ORIENTATION_ROTATE_180:
          degree = 180;
          break;
        case ExifInterface.ORIENTATION_ROTATE_270:
          degree = 270;
          break;
      }
    } catch (IOException e) {
      e.printStackTrace();
    }
    return degree;
  }

  /**
   * 把字節(jié)數(shù)組保存為一個(gè)文件
   *
   * @param b
   * @param outputFile
   * @return
   */
  public static File getFileFromBytes(byte[] b, String outputFile) {
    File ret = null;
    BufferedOutputStream stream = null;
    try {
      ret = new File(outputFile);
      FileOutputStream fstream = new FileOutputStream(ret);
      stream = new BufferedOutputStream(fstream);
      stream.write(b);
    } catch (Exception e) {
      // log.error("helper:get file from byte process error!");
      e.printStackTrace();
    } finally {
      if (stream != null) {
        try {
          stream.close();
        } catch (IOException e) {
          // log.error("helper:get file from byte process error!");
          e.printStackTrace();
        }
      }
    }
    return ret;
  }

  /**
   * 圖片壓縮
   *
   * @param fPath
   * @return
   */
  public static Bitmap decodeFile(String fPath) {
    BitmapFactory.Options opts = new BitmapFactory.Options();
    opts.inJustDecodeBounds = true;
    opts.inDither = false; // Disable Dithering mode
    opts.inPurgeable = true; // Tell to gc that whether it needs free
    opts.inInputShareable = true; // Which kind of reference will be used to
    BitmapFactory.decodeFile(fPath, opts);
    final int REQUIRED_SIZE = 400;
    int scale = 1;
    if (opts.outHeight > REQUIRED_SIZE || opts.outWidth > REQUIRED_SIZE) {
      final int heightRatio = Math.round((float) opts.outHeight
          / (float) REQUIRED_SIZE);
      final int widthRatio = Math.round((float) opts.outWidth
          / (float) REQUIRED_SIZE);
      scale = heightRatio < widthRatio ? heightRatio : widthRatio;//
    }
    Log.i("scale", "scal ="+ scale);
    opts.inJustDecodeBounds = false;
    opts.inSampleSize = scale;
    Bitmap bm = BitmapFactory.decodeFile(fPath, opts).copy(Bitmap.Config.ARGB_8888, false);
    return bm;
  }

 

  /**
   * 創(chuàng)建目錄
   * @param path
   */
  public static void setMkdir(String path)
  {
    File file = new File(path);
    if(!file.exists())
    {
      file.mkdirs();
      Log.e("file", "目錄不存在 創(chuàng)建目錄  ");
    }else{
      Log.e("file", "目錄存在");
    }
  }

  /**
   * 獲取目錄名稱(chēng)
   * @param url
   * @return FileName
   */
  public static String getFileName(String url)
  {
    int lastIndexStart = url.lastIndexOf("/");
    if(lastIndexStart!=-1)
    {
      return url.substring(lastIndexStart+1, url.length());
    }else{
      return null;
    }
  }

  /**
   * 刪除該目錄下的文件
   *
   * @param path
   */
  public static void delFile(String path) {
    if (!TextUtils.isEmpty(path)) {
      File file = new File(path);
      if (file.exists()) {
        file.delete();
      }
    }
  }
}

4、需要注意的問(wèn)題

在打release包的時(shí)候,因?yàn)榛煜膯?wèn)題,點(diǎn)擊又會(huì)沒(méi)有反應(yīng),這是因?yàn)閛penFileChooser()是系統(tǒng)api,所以需要在混淆是不混淆該方法。

-keepclassmembers class * extends android.webkit.WebChromeClient{
public void openFileChooser(...);
}

當(dāng)點(diǎn)擊拍照之后,如果相機(jī)是橫屏拍照的話,當(dāng)拍照結(jié)束之后跳回app的時(shí)候,會(huì)導(dǎo)致app端當(dāng)前的webView頁(yè)面銷(xiāo)毀并重新打開(kāi),需要在androidManifest.xml中當(dāng)前Activity添加:

android:configChanges="orientation|keyboardHidden|screenSize"

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • 音量控制鍵控制的音頻流(setVolumeControlStream)描述

    音量控制鍵控制的音頻流(setVolumeControlStream)描述

    當(dāng)開(kāi)發(fā)多媒體應(yīng)用或者游戲應(yīng)用的時(shí)候,需要使用音量控制鍵來(lái)設(shè)置程序的音量大小,在Android系統(tǒng)中有多種音頻流,感興趣的朋友可以了解下
    2013-01-01
  • Android內(nèi)存優(yōu)化操作方法梳理總結(jié)

    Android內(nèi)存優(yōu)化操作方法梳理總結(jié)

    這篇文章主要介紹了Android 內(nèi)存優(yōu)化知識(shí)點(diǎn)梳理總結(jié),Android 操作系統(tǒng)給每個(gè)進(jìn)程都會(huì)分配指定額度的內(nèi)存空間,App 使用內(nèi)存來(lái)進(jìn)行快速的文件訪問(wèn)交互,長(zhǎng)時(shí)間如此便需要優(yōu)化策略,文章分享優(yōu)化知識(shí)點(diǎn)總結(jié),需要的朋友可以參考一下
    2022-11-11
  • Android開(kāi)發(fā)中的Surface庫(kù)及用其制作播放器UI的例子

    Android開(kāi)發(fā)中的Surface庫(kù)及用其制作播放器UI的例子

    這篇文章主要介紹了Android開(kāi)發(fā)中的Surface庫(kù)及用其制作播放器界面的例子,利用SurfaceView和SurfaceHolder可以高效地繪制和控制圖形界面,需要的朋友可以參考下
    2016-04-04
  • Android實(shí)現(xiàn)搜索保存歷史記錄功能

    Android實(shí)現(xiàn)搜索保存歷史記錄功能

    這篇文章主要介紹了Android實(shí)現(xiàn)搜索保存歷史記錄功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-05-05
  • Android仿微信和QQ多圖合并框架(類(lèi)似群頭像)的實(shí)現(xiàn)方法

    Android仿微信和QQ多圖合并框架(類(lèi)似群頭像)的實(shí)現(xiàn)方法

    這篇文章主要給大家介紹了關(guān)于Android仿微信和QQ多圖合并框架的相關(guān)資料,其實(shí)就是我們平時(shí)所見(jiàn)的群聊頭像,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)各位Android開(kāi)發(fā)者們具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。
    2017-12-12
  • Android徹底清除APP數(shù)據(jù)的兩種方案總結(jié)

    Android徹底清除APP數(shù)據(jù)的兩種方案總結(jié)

    大家在用Android手機(jī)的時(shí)候肯定都遇到過(guò)內(nèi)存剩余空間越來(lái)越小的情況,所以下面這篇文章主要給大家介紹了關(guān)于Android徹底清除APP數(shù)據(jù)的兩種方案,需要的朋友可以參考下
    2021-11-11
  • Android Parcelable與Serializable詳解及區(qū)別

    Android Parcelable與Serializable詳解及區(qū)別

    這篇文章主要介紹了Android Parcelable與Serializable詳解及區(qū)別的相關(guān)資料,需要的朋友可以參考下
    2017-01-01
  • Android自定義TabLayout效果

    Android自定義TabLayout效果

    這篇文章主要為大家詳細(xì)介紹了Android自定義TabLayout效果的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-03-03
  • Kotlin函數(shù)式編程超詳細(xì)介紹

    Kotlin函數(shù)式編程超詳細(xì)介紹

    一個(gè)函數(shù)式應(yīng)用通常由三大類(lèi)函數(shù)構(gòu)成:變換transform、過(guò)濾filters合并combineo每類(lèi)函數(shù)都針對(duì)集合數(shù)據(jù)類(lèi)型設(shè)計(jì),目標(biāo)是產(chǎn)生一個(gè)最終結(jié)果。函數(shù)式編程用到的函數(shù)生來(lái)都是可組合的,也就是說(shuō),你可以組合多個(gè)簡(jiǎn)單函數(shù)來(lái)構(gòu)建復(fù)雜的計(jì)算行為
    2022-09-09
  • Android編程中Activity的四種啟動(dòng)模式

    Android編程中Activity的四種啟動(dòng)模式

    這篇文章主要介紹了Android編程中Activity的四種啟動(dòng)模式,較為詳細(xì)的分析了Activity四種啟動(dòng)模式的原理與功能,需要的朋友可以參考下
    2016-04-04

最新評(píng)論

蛟河市| 云阳县| 商都县| 五指山市| 措美县| 白朗县| 新源县| 建水县| 静宁县| 日土县| 通许县| 凤城市| 泽州县| 抚宁县| 麦盖提县| 谢通门县| 威远县| 监利县| 离岛区| 宁南县| 新民市| 澜沧| 宾川县| 吴江市| 武隆县| 沧源| 太和县| 丰宁| 余庆县| 澎湖县| 连城县| 嘉义县| 漯河市| 城口县| 琼结县| 房产| 黄冈市| 瓮安县| 聂拉木县| 前郭尔| 天长市|