項(xiàng)目需求討論 - WebView下拍照及圖片選擇功能

前言:

現(xiàn)在很多app里面,都會(huì)有這么一個(gè)需求,就是上傳圖片的按鈕,當(dāng)然按了這個(gè)按鈕之后,就會(huì)出現(xiàn)二種選擇: 1. 直接拍照,2. 相冊選擇現(xiàn)有圖片。

因?yàn)楝F(xiàn)在的app這塊功能會(huì)有二個(gè)大的情況:

  1. 全部原生的 app 來實(shí)現(xiàn)。
  2. HyBrid 的 app 來實(shí)現(xiàn)。

本文先討論HyBrid的app的實(shí)現(xiàn)情況,下次再討論原生,不過其實(shí)大部分實(shí)現(xiàn)都是相似的。

其實(shí)這種在WebView配合下實(shí)現(xiàn)這類功能的文章很多很多,但是大多數(shù)都是上傳一大段代碼,然后讓大家自己看,千篇一律,所以本文主要是寫的完整的思路。

正文:

我們知道用戶會(huì)在網(wǎng)頁上點(diǎn)擊了某個(gè)按鈕,然后調(diào)用起安卓方面的相關(guān)操作。然后實(shí)現(xiàn)完整的功能。

1. 網(wǎng)頁端:

其實(shí)網(wǎng)頁端很簡單,只需要實(shí)現(xiàn)一個(gè)簡單的<input>標(biāo)簽即可。

總體思路是一個(gè)<input>標(biāo)簽和一個(gè)<img>標(biāo)簽重疊在一起(<input>在上,<img>在下,類似可以理解<img>作為背景),當(dāng)選完照片后,最后把圖片賦值給<img>標(biāo)簽。

但是在給<img>賦值的時(shí)候我遇到過不同的情況:

  1. 當(dāng)在Android這邊拍照或者進(jìn)入圖庫選完照片后,把圖片信息給了網(wǎng)頁端后,<input>標(biāo)簽的onchange監(jiān)聽到了圖片選擇好了,網(wǎng)頁端直接把圖片上傳到服務(wù)器并傳回來一個(gè)地址,顯示時(shí)把地址拼接成可以找到路徑的地址放在<img>標(biāo)簽中就可以了。

  2. 配合FileReader,F(xiàn)ileReader是作為文件API的重要成員用于讀取文件??梢詤⒖迹?h5 實(shí)現(xiàn)調(diào)用系統(tǒng)拍照或者選擇照片并預(yù)覽

2. Android端:

2.1 WebChromeClient

因?yàn)锳ndroid端訪問網(wǎng)頁大部分使用的是WebView,所以我們這里還是用WebView來說明。

既然用戶在網(wǎng)頁上點(diǎn)擊了<input>,我們肯定需要WebView能監(jiān)聽到,好比原生的Button點(diǎn)擊我們要監(jiān)聽也要寫一個(gè)OnclickListener來實(shí)現(xiàn)監(jiān)聽。我們這里使用的是WebChromeClient。

public class ImgWebChromeClient extends WebChromeClient {
        
      //.......
      //.......
        
     public ImgWebChromeClient(Activity activity) {
         this.activity = activity;
     }

        
        
}

我們實(shí)現(xiàn)我們的類,繼承WebChromeClient。然后我們就可以把這個(gè)我們自己定義的WebChromeClient設(shè)置給我們的WebView。

webView.setWebChromeClient(new ImgWebChromeClient(this));

我們可以看到我們在WebChromeClient在監(jiān)聽<input>點(diǎn)擊事件的時(shí)候,還要根據(jù)不同的版本來區(qū)分,主要是以Android 5.0版本來進(jìn)行大的劃分。

  1. Android 5.0及以上版本:


  2. Android 5.0以下版本:


都是openFileChooser方法,不同版本的里面參數(shù)不同。

所以我們可以看到主要是openFileChooseronShowFileChooser方法。

// For Android < 3.0
public void openFileChooser(ValueCallback<Uri> valueCallback) {
            ***
}

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

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

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

不管是什么版本,我們看到這幾個(gè)方法的參數(shù)里面都有ValueCallback參數(shù)。

/**
 * A callback interface used to provide values asynchronously.
 */
public interface ValueCallback<T> {
    /**
     * Invoked when the value is available.
     * @param value The value.
     */
    public void onReceiveValue(T value);
};

所以我們知道了,我們最后是調(diào)用openFileChooseronShowFileChooser方法里面的ValueCallback參數(shù),調(diào)用它的onReceiveValue方法把我們的選擇的圖片的Uri傳入,網(wǎng)頁端那邊就會(huì)收到信息了,就知道用戶到底選擇了什么圖片。

所以我們最終目標(biāo)就是獲取選擇的圖片的Uri,然后給ValueCallback.onReceiveValue方法,這就是我們整個(gè)文章的最主要的流程。

2.2 獲取相關(guān)圖片的Uri

上面我們提到了,我們最終目標(biāo)是獲取用戶選取的圖片Uri,然后傳給ValueCallback就可以。
所以我們這里就要講二大塊:

  1. 用戶怎么跳到自己想要的界面(相機(jī) or 圖庫)
  2. 用戶在自己想要的界面選擇好了圖片后 (拍好了照片 or 在圖庫選擇好了圖片),如何獲取相關(guān)圖片的Uri。

根據(jù)這二點(diǎn),我們一步步來分析。

2.2.1 相機(jī) or 圖庫

我們肯定想到是用戶點(diǎn)擊了某個(gè)按鈕后,我們需要跳出一個(gè)彈框,然后上面有拍照和圖庫按鈕:
比如我使用系統(tǒng)自帶的選擇框(不同手機(jī)顯示的彈框不同):


所以我們這里知道了這個(gè)又要細(xì)分任務(wù):

  1. 獲取相關(guān)權(quán)限
  2. 如何點(diǎn)擊按鈕后可以跳到相應(yīng)界面(拍照 or 圖庫)。
  3. 如何創(chuàng)建彈框,把上面的按鈕顯示在上面
2.2.1.1 獲取相關(guān)權(quán)限

emmm......這塊我覺得應(yīng)該不需要花更多的時(shí)間來說明了吧,主要就是:

  1. 檢查權(quán)限 (checkSelfPermission)
  2. 請(qǐng)求權(quán)限(requestPermissions)
  3. 回調(diào)事件處理(onRequestPermissionsResult)

而我們要申請(qǐng)的權(quán)限無非就是 Camera的權(quán)限,還有讀寫外部存儲(chǔ)的權(quán)限。

2.2.1.2 如何點(diǎn)擊按鈕后可以跳到相應(yīng)界面(拍照 or 圖庫):

我們先來看拍照:

2.2.1.2.1 設(shè)置打開相機(jī)Intent的Action

我們知道打開某個(gè)界面,就是startActivity(Intent);平常比如跳到撥號(hào)界面等。只要對(duì)Intent設(shè)定相應(yīng)的Action即可。
具體我們可以看谷歌的Android官方教程網(wǎng)頁即可:

Android指南 - 通用 Intent

我們可以看到有這些:


我們可以這個(gè)目錄中看到了相機(jī),我們具體看相機(jī)的介紹:

:當(dāng)您使用 ACTION_IMAGE_CAPTURE拍攝照片時(shí),相機(jī)可能還會(huì)在結(jié)果 Intent 中返回縮小尺寸的照片副本(縮略圖),這個(gè)副本以 Bitmap 形式保存在名為 dataextra 字段中。

所以我們這里跳到拍照界面也是一樣,只要建立跳到相機(jī)界面的Intent即可:

Intent captureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
2.2.1.2.2 設(shè)置相機(jī)拍攝的照片的存儲(chǔ)位置

因?yàn)橛行┤诵枰谧约旱腁PP中調(diào)用拍照的功能,存在自己指定的目錄下面,所以需要在startActivity啟動(dòng)相機(jī)界面時(shí)候同時(shí)傳遞過去信息,告訴拍照了之后照片存的位置。

1.我們先指定我們的要存儲(chǔ)的照片的路徑Uri:

其實(shí)很簡單,設(shè)定我們接下去要拍的照片的完整存儲(chǔ)路徑,然后得到File對(duì)象,再通過Uri.fromFile方法再通過剛才我們的File對(duì)象來獲得Uri。

(當(dāng)然如果這里你只需要打開系統(tǒng)相機(jī),以下第二部分可以忽略)

2.獲取所有相機(jī)的Intent集合:

因?yàn)槲覀兪謾C(jī)上面可能有很多個(gè)相機(jī)軟件,所以我們需要先找到能打開各自相機(jī)軟件的Intent,我們通過PackageManager.queryIntentActivities的方式來進(jìn)行符合拍照Action的Intent的軟件,然后得到它們具體的詳細(xì)信息,比如包名及對(duì)應(yīng)的activity名字等,然后把相應(yīng)的Intent變得更加詳細(xì)即可。

3.把uri賦值給Intent:

在上面貼出的Android 官方網(wǎng)頁上面的相機(jī)部分其實(shí)也提到過了如何設(shè)置存儲(chǔ)位置:

所以這里我們只需要找到相應(yīng)的Intent,然后把我們的Uri位置賦值給Intent即可:

intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);

最后只需要startActivity去啟動(dòng)我們這個(gè)指定了打開相機(jī)的特定Intent即可。


拍照說完了,我們再來看在圖庫界面選擇圖片:
其實(shí)總體思路和拍照是一模一樣,無非就是指定Intent是打開了圖庫的Intent。

還是在剛才的Android 官網(wǎng)我們可以看到:

因?yàn)槲覀兪遣榭幢镜氐膱D片,所以我們要使用 ACTION_GET_CONTENT,同時(shí)指定MIME類型是圖片類型,如果要進(jìn)行圖片多選,就再指定EXTRA_ALLOW_MULTIPLEtrue。

同時(shí)也給出了實(shí)例代碼:

這里我要提一下,我們在設(shè)置IntentAction的時(shí)候不只是可以使用ACTION_GET_CONTENT,還可以使用ACTION_PICK。

我們可以看到上面寫著可以用來選擇數(shù)據(jù),然后返回被選中的選項(xiàng)。

但是在具體手機(jī)操作上有點(diǎn)不同(不知道不同的手機(jī)系統(tǒng)會(huì)不會(huì)結(jié)果不同,我只測了模擬器):


ps:最坑的是用ACTION_GET_CONTEN時(shí)候多選圖片要長按操作,一直以為沒成功,以為多選圖片功能沒實(shí)現(xiàn),后來在 Android: Intent.EXTRA_ALLOW_MULTIPLE allows only single picking 上面找到別人的答案,說需要長按

2.2.2 出現(xiàn)選擇框讓用戶選擇

我們可以看到可以自定義彈框,比如我們設(shè)定固定的按鈕,然后再點(diǎn)擊特定按鈕后啟動(dòng)我們的上面提過的特定的Intent即可。

這里我們講如果只是給定我們想要啟動(dòng)的多個(gè)Intent的選項(xiàng),讓系統(tǒng)幫我們彈出彈框及相關(guān)按鈕,關(guān)鍵字就是Intent.createChooser方法

直接看圖片即可,寫的很詳細(xì)了,或者大家搜相關(guān)的關(guān)鍵字也是有很多文章的。比如:Android createChooser方法源碼簡析等。

2.2.3 獲取用戶在相機(jī)或者圖庫選擇的圖片Uri


因?yàn)槲覀儾皇菃渭兊奶搅讼鄼C(jī)界面或者是圖庫界面就可以了,我們還需要獲取用戶在那些應(yīng)用外的界面到底選了什么圖片,所以單純的startActivity肯定不夠,所以大家肯定想到了使用startActivityForResult來啟動(dòng),這樣才能根據(jù)用戶不同的操作來進(jìn)行相應(yīng)的處理。

我們知道需要復(fù)寫onActivityResult來處理,主要也就三個(gè)參數(shù)(int requestCode, int resultCode, Intent data)。具體的內(nèi)容圖片里面也寫的很清楚。


2.3 Uri 和 ValueCallback

所以我們ValueCallback實(shí)例在 WebChromeClient的方法里面拿到了,Uri也通過相機(jī)或者圖庫的選擇下獲取到了。最終調(diào)用把獲取的Uri 賦值給ValueCallback.onReceiveValue()即可。

PS: 取消這次網(wǎng)頁點(diǎn)擊選取圖片的請(qǐng)求,只需要調(diào)用onReceiveValue(null)即可。

結(jié)語:

emm.......大家輕噴即可。。。。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,351評(píng)論 25 708
  • ¥開啟¥ 【iAPP實(shí)現(xiàn)進(jìn)入界面執(zhí)行逐一顯】 〖2017-08-25 15:22:14〗 《//首先開一個(gè)線程,因...
    小菜c閱讀 7,390評(píng)論 0 17
  • 前幾天偶然看見有人在網(wǎng)上發(fā)了帖子,唐僧西天取經(jīng)最不能缺少的徒弟是哪一個(gè)或者說公司如果有悟空,八戒,沙僧這三種員工必...
    10d017953849閱讀 433評(píng)論 0 0
  • CQRS本身就是一個(gè)非常簡單的模式。他只是規(guī)定了應(yīng)用程序的命令處理的組件(增,刪,改)應(yīng)該和查詢的組件進(jìn)行分離。盡...
    water_lang閱讀 4,290評(píng)論 0 2
  • 此時(shí)此刻,在一個(gè)無法想像的遙遠(yuǎn)宇宙中,一個(gè)跟你一毛一樣的人正不經(jīng)意間點(diǎn)開了這篇文章,看到了這幾個(gè)字。你看到這個(gè)字的...
    clfcool閱讀 699評(píng)論 2 1

友情鏈接更多精彩內(nèi)容