1. 程式人生 > >android手機拍照6.0,7.0問題

android手機拍照6.0,7.0問題

因前期專案執行沒有再7.0以上手機執行,最近一個同事用的華為8.0手機執行專案進行拍照,結果閃退,問題就來了,整的是焦頭爛額啊,先是用的FilePrivider,網上好多部落格都進行了講解,我在使用過程中FileProvider.getUriForFile發現返回為空,結果,又忙乎了,最後忘了啥原因了改好了,但是我在下面程式碼中因為要得到圖片具體路徑,上傳到阿里雲伺服器上,就是拿不到路徑,空指標,辦法試盡了,也是自己能力不夠,就是不行;沒辦法,專案著急更新一版,就在網上找了找,看到了TakePhoto,感覺能實現我的問題,就用了這個第三方。

匯入這個庫的時候提示找不到,最後找了一個老版本就能導進去了,我用的這個版本

compile 'com.jph.takephoto:takephoto_library:3.0.0'

還有匯入衝突問題,在文章末尾有解決辦法
TakePhoto有兩種方式:1是繼承它的activity或者fragment;2是實現介面,重寫方法,我是用的第二張

1.首先實現介面,重寫三個方法

public class RealNameActivityStep1 extends Activity implements View.OnClickListener, TakePhoto.TakeResultListener, InvokeListener {
    @Override
public void takeSuccess(TResult result) { } @Override public void takeFail(TResult result, String msg) { } @Override public void takeCancel() { } }

同時記得加上6.0,7.0許可權處理


    private InvokeParam invokeParam;
    @Override
    public void onRequestPermissionsResult(int
requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); //以下程式碼為處理Android6.0、7.0動態許可權所需 PermissionManager.TPermissionType type = PermissionManager.onRequestPermissionsResult(requestCode, permissions, grantResults); PermissionManager.handlePermissionsResult(this, type, invokeParam, this); } @Override public PermissionManager.TPermissionType invoke(InvokeParam invokeParam) { PermissionManager.TPermissionType type = PermissionManager.checkPermission(TContextWrap.of(this), invokeParam.getMethod()); if (PermissionManager.TPermissionType.WAIT.equals(type)) { this.invokeParam = invokeParam; } return type; }

還有重要一點,在 onCreate,onActivityResult,onSaveInstanceState方法中呼叫TakePhoto對用的方法。


    /**
     * 獲取TakePhoto例項
     *
     * @return
     */
    public TakePhoto getTakePhoto() {
        if (takePhoto == null) {
            takePhoto = (TakePhoto) TakePhotoInvocationHandler.of(this).bind(new TakePhotoImpl(this, this));
        }
        return takePhoto;
    }
    //這三個方法裡呼叫takeohoto的方法
        @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        getTakePhoto().onCreate(savedInstanceState);
   }       
        @Override
    public void onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) {
        getTakePhoto().onSaveInstanceState(outState);
        super.onSaveInstanceState(outState, outPersistentState);
    }
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        getTakePhoto().onActivityResult(requestCode, resultCode, data);
        super.onActivityResult(requestCode, resultCode, data);
    }

看一下作者是怎麼說的這個takephotot
支援通過相機拍照獲取圖片
支援從相簿選擇圖片
支援從檔案選擇圖片
支援批量圖片選取
支援圖片壓縮以及批量圖片壓縮
支援圖片裁切以及批量圖片裁切
支援照片旋轉角度自動糾正
支援自動許可權管理(無需關心SD卡及攝像頭許可權等問題)
支援對裁剪及壓縮引數個性化配置
提供自帶裁剪工具(可選)
支援智慧選取及裁剪異常處理
支援因拍照Activity被回收後的自動恢復
支援Android8.1
+支援多種壓縮工具
+支援多種圖片選擇工具

TakePhoto提供拍照,從相簿選擇,從檔案中選擇三種方式獲取圖片。

API:
/**
 * 從檔案中獲取圖片(不裁剪)
 */
void onPickFromDocuments();
/**
 * 從相簿中獲取圖片(不裁剪)
 */
void onPickFromGallery();
/**
 * 從相機獲取圖片(不裁剪)
 * @param outPutUri 圖片儲存的路徑
 */
void onPickFromCapture(Uri outPutUri);
/**
 * 圖片多選
 * @param limit 最多選擇圖片張數的限制
 **/
void onPickMultiple(int limit);

TakePhoto支援對圖片進行裁剪,無論是拍照的照片,還是從相簿、檔案中選擇的圖片。你只需要呼叫TakePhoto的相應方法即可:

API:
/**
 * 從相機獲取圖片並裁剪
 * @param outPutUri 圖片裁剪之後儲存的路徑
 * @param options 裁剪配置             
 */
void onPickFromCaptureWithCrop(Uri outPutUri, CropOptions options);
/**
 * 從相簿中獲取圖片並裁剪
 * @param outPutUri 圖片裁剪之後儲存的路徑
 * @param options 裁剪配置
 */
void onPickFromGalleryWithCrop(Uri outPutUri, CropOptions options);
/**
 * 從檔案中獲取圖片並裁剪
 * @param outPutUri 圖片裁剪之後儲存的路徑
 * @param options 裁剪配置
 */
void onPickFromDocumentsWithCrop(Uri outPutUri, CropOptions options);
/**
 * 圖片多選,並裁切
 * @param limit 最多選擇圖片張數的限制
 * @param options  裁剪配置
 * */
void onPickMultipleWithCrop(int limit, CropOptions options);

另外,TakePhoto也支援你對指定圖片進行裁剪:

/**
 * 裁剪圖片
 * @param imageUri 要裁剪的圖片
 * @param outPutUri 圖片裁剪之後儲存的路徑
 * @param options 裁剪配置
 */
void onCrop(Uri imageUri, Uri outPutUri, CropOptions options)throws TException;
/**
 * 裁剪多張圖片
 * @param multipleCrop 要裁切的圖片的路徑以及輸出路徑
 * @param options 裁剪配置
 */
void onCrop(MultipleCrop multipleCrop, CropOptions options)throws TException;

壓縮圖片
你可以選擇是否對圖片進行壓縮處理,你只需要告訴它你是否要啟用壓縮功能以及CompressConfig即可。

API
 /**
  * 啟用圖片壓縮
  * @param config 壓縮圖片配置
  * @param showCompressDialog 壓縮時是否顯示進度對話方塊
  * @return
  */
 void onEnableCompress(CompressConfig config,boolean showCompressDialog);

TakePhoto takePhoto=getTakePhoto();
takePhoto.onEnableCompress(compressConfig,true);
takePhoto.onPickFromGallery();

對指定圖片進行壓縮
另外,你也可以對指定圖片進行壓縮:

Usage:

new CompressImageImpl(compressConfig,result.getImages(), new CompressImage.CompressListener() {
    @Override
    public void onCompressSuccess(ArrayList<TImage> images) {
        //圖片壓縮成功
    }
    @Override
    public void onCompressFailed(ArrayList<TImage> images, String msg) {
        //圖片壓縮失敗
    }
}).compress();

CompressConfig compressConfig=new CompressConfig.Builder().setMaxSize(50*1024).setMaxPixel(800).create();

還有好多,就不貼了,自己用不到,具體github搜尋takephoto就有全部

我基本就用了兩個,一個拍照圖片顯示並上傳到阿里雲伺服器,另一個就是相簿選取上傳阿里雲

自己用這個的好處,也正是吸引我的地方,能解決我問題的地方:

強調內容

相容性
Android6.0
由於Android6.0新增了”執行時許可權控制(Runtime Permissions)”,為了應對這一改變,TakePhoto加入和自動許可權管理,當TakePhoto檢測到需要許可權時,TakePhoto會自動申請許可權,所以小夥伴們不用擔心許可權的使用問題。

Android7.0
在Android N中,Android 框架執行了 StrictMode,應用間共享檔案和以前也有所區別。為了適配Android7.0的改變,同時也為了方便大家使用TakePhoto,TakePhoto會自動根據手機的Android版本自行適配,小夥伴們依舊可以向TakePhoto傳遞Uri imageUri = Uri.fromFile(file);型別的Uri而不用擔心相容性問題。

主要程式碼:

    /**
     * @param request 區分是相簿還是拍照 1是拍照,2是相簿
     */
    private void openPhoto(int request) {
        File file = new File(Environment.getExternalStorageDirectory(), "/temp/" + System.currentTimeMillis() + ".jpg");
        if (!file.getParentFile().exists()) file.getParentFile().mkdirs();
        Uri imageUri = Uri.fromFile(file);
        if (request == 1) {
            //file:///storage/emulated/0/temp/1527231988466.jpg
            takePhoto.onPickFromCapture(imageUri);
        }
        takePhoto.onPickFromGallery();
    }
    //上面原來這麼寫的,但是不對,在快下班時候才發現,應該加改成如下:
        /**
     * @param request 區分是相簿還是拍照
     */
    private void openPhoto(int request) {
        File file = new File(Environment.getExternalStorageDirectory(), "/temp/" + System.currentTimeMillis() + ".jpg");
        if (!file.getParentFile().exists()) file.getParentFile().mkdirs();
        Uri imageUri = Uri.fromFile(file);
        if (request == 1) {
            //file:///storage/emulated/0/temp/1527231988466.jpg
            takePhoto.onPickFromCapture(imageUri);
        } else {
            takePhoto.onPickMultiple(1);
        }
    }

然後拍照成功後會回撥

    @Override
    public void takeSuccess(TResult result) {
//具體實現圖片上傳的功能了,我的是上傳身份證,1是正面,2是反面;這裡主要是拿到圖片路徑result.getImage().getPath(),以下涉及業務,不能寫了
        if (isImage1) {
            getImgKey("身份證", result.getImage().getPath(), "01", SPUtil.getString(SysParam.TOKEN));
        }
        if (isImage2) {
            getImgKey("身份證", result.getImage().getPath(), "02", SPUtil.getString(SysParam.TOKEN));
        }

    }

然後寫完了,執行在6.0以上手機上,拍照直接進不去,過一會崩了,一看日誌,才知道又是那個7.0檔案的問題,雖然作者說了:
重點內容
Android7.0
在Android N中,Android 框架執行了 StrictMode,應用間共享檔案和以前也有所區別。為了適配Android7.0的改變,同時也為了方便大家使用TakePhoto,TakePhoto會自動根據手機的Android版本自行適配,小夥伴們依舊可以向TakePhoto傳遞Uri imageUri = Uri.fromFile(file);型別的Uri而不用擔心相容性問題。
但是還是報 java.lang.IllegalArgumentException:Failed to find configured root that contains /storage/emulated/0/temp/1527233829268.jpg
解決辦法就是加個xml檔案,manifests裡面就不用配置provider了
這裡寫圖片描述

provider_path.xml內容,這個網上好多解釋,自己不是很懂

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <cache-path name="cache_dir" path="."/>
    <external-cache-path name="external_dir" path="." />
</paths>

對了有一點,因為之前我在匯入這個takephoto之前它提示有衝突,我之前用的FileProvider7.0以上拍照,在清單檔案裡配置了這個:

        <provider
            android:authorities="你的包名.fileprovider"
            android:name="android.support.v4.content.FileProvider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/provider_path" />
            ></provider>

因為配置了provider,提示衝突,後來我給註釋掉就成功了;個人懷疑,作者的這個TakePhoto裡面已經加了這個provider了,不然這個第三方在7.0以上拍照也肯定會失敗,所以清單檔案裡就不用寫了,但是xml檔案必須得建,不然進不去相機
這樣就ok了!成功了,之前因為拍照要顯示到頁面,還得上傳,因為現在手機太亂了,返回的date有的是空,有的拿不到uri路徑,就報空指標,很是操蛋,所以就另想了個辦法,我是先上傳圖片,上傳成功一霎那再網路進行獲取圖片,顯示到imageview上,就跳過了那個返回路徑問題。
還有另外一個簡單的辦法:
Glide不是能直接傳圖片路徑嗎,你可以和後臺商量一下定死一個域名,例如:http://www.baidu.com/,這就可以寫死,後面拼接上你的拍照或者相簿選取的圖片名,是不是就不用費事的網路請求,然後解析json串,拿到圖片路徑,最後glide加載出來,想法是不是很好,但是你後臺的得給力啊,願意幫你整,我這裡沒用這個辦法,後臺不在,只是提供一個思路。
例如:

//圖片名可以考慮在takeSuccess的result裡獲取,感覺不一定能獲取到
    @Override
    public void takeSuccess(TResult result) {
    }
//也可以考慮在onActivityResult獲取,只要能或獲取到就行,這兩個方法可以試試
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    }
 //最後直接一步:
 Glide.with(this).load("http://www.baidu.com/"+圖片名).into(mImageView);

OK了,完活!