專案需求討論 - WebView下拍照及圖片選擇功能

青蛙要fly發表於2018-06-23

前言:

如果覺得掘金上看圖片放大看不清楚,可以跳到另外的同步釋出的連結看,放大圖片下部有個可以檢視原圖功能,很清楚:專案需求討論 - WebView下拍照及圖片選擇功能

現在很多app裡面,都會有這麼一個需求,就是上傳圖片的按鈕,當然按了這個按鈕之後,就會出現二種選擇: 1. 直接拍照,2. 相簿選擇現有圖片。

因為現在的app這塊功能會有二個大的情況:

  1. 全部原生的 app 來實現。
  2. HyBrid 的 app 來實現。

專案需求討論 - WebView下拍照及圖片選擇功能

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

其實這種在WebView配合下實現這類功能的文章很多很多,但是大多數都是上傳一大段程式碼,然後讓大家自己看,千篇一律,所以本文主要是寫的完整的思路。

專案需求討論 - WebView下拍照及圖片選擇功能

正文:

專案需求討論 - WebView下拍照及圖片選擇功能

我們知道使用者會在網頁上點選了某個按鈕,然後呼叫起安卓方面的相關操作。然後實現完整的功能。

1. 網頁端:

專案需求討論 - WebView下拍照及圖片選擇功能

其實網頁端很簡單,只需要實現一個簡單的<input>標籤即可。

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

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

  1. 當在Android這邊拍照或者進入相簿選完照片後,把圖片資訊給了網頁端後,<input>標籤的onchange監聽到了圖片選擇好了,網頁端直接把圖片上傳到伺服器並傳回來一個地址,顯示時把地址拼接成可以找到路徑的地址放在<img>標籤中就可以了。

  2. 配合FileReader,FileReader是作為檔案API的重要成員用於讀取檔案。可以參考: h5 實現呼叫系統拍照或者選擇照片並預覽

2. Android端:

專案需求討論 - WebView下拍照及圖片選擇功能

2.1 WebChromeClient

因為Android端訪問網頁大部分使用的是WebView,所以我們這裡還是用WebView來說明。

既然使用者在網頁上點選了<input>,我們肯定需要WebView能監聽到,好比原生的Button點選我們要監聽也要寫一個OnclickListener來實現監聽。我們這裡使用的是WebChromeClient

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

        
        
}
複製程式碼

我們實現我們的類,繼承WebChromeClient。然後我們就可以把這個我們自己定義的WebChromeClient設定給我們的WebView。

webView.setWebChromeClient(new ImgWebChromeClient(this));
複製程式碼

專案需求討論 - WebView下拍照及圖片選擇功能

我們可以看到我們在WebChromeClient在監聽<input>點選事件的時候,還要根據不同的版本來區分,主要是以Android 5.0版本來進行大的劃分。

專案需求討論 - WebView下拍照及圖片選擇功能

  1. Android 5.0及以上版本:

    專案需求討論 - WebView下拍照及圖片選擇功能

  2. Android 5.0以下版本:

    專案需求討論 - WebView下拍照及圖片選擇功能

都是openFileChooser方法,不同版本的裡面引數不同。

所以我們可以看到主要是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;
}
複製程式碼

不管是什麼版本,我們看到這幾個方法的引數裡面都有ValueCallback引數。

專案需求討論 - WebView下拍照及圖片選擇功能

/**
 * 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);
};
複製程式碼

所以我們知道了,我們最後是呼叫openFileChooseronShowFileChooser方法裡面的ValueCallback引數,呼叫它的onReceiveValue方法把我們的選擇的圖片的Uri傳入,網頁端那邊就會收到資訊了,就知道使用者到底選擇了什麼圖片。

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

2.2 獲取相關圖片的Uri

專案需求討論 - WebView下拍照及圖片選擇功能

上面我們提到了,我們最終目標是獲取使用者選取的圖片Uri,然後傳給ValueCallback就可以。 所以我們這裡就要講二大塊:

  1. 使用者怎麼跳到自己想要的介面(相機 or 相簿)
  2. 使用者在自己想要的介面選擇好了圖片後 (拍好了照片 or 在相簿選擇好了圖片),如何獲取相關圖片的Uri

根據這二點,我們一步步來分析。

2.2.1 相機 or 相簿

我們肯定想到是使用者點選了某個按鈕後,我們需要跳出一個彈框,然後上面有拍照和相簿按鈕: 比如我使用系統自帶的選擇框(不同手機顯示的彈框不同):

專案需求討論 - WebView下拍照及圖片選擇功能

所以我們這裡知道了這個又要細分任務:

  1. 獲取相關許可權
  2. 如何點選按鈕後可以跳到相應介面(拍照 or 相簿)。
  3. 如何建立彈框,把上面的按鈕顯示在上面
2.2.1.1 獲取相關許可權

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

  1. 檢查許可權 (checkSelfPermission)
  2. 請求許可權(requestPermissions)
  3. 回撥事件處理(onRequestPermissionsResult)

而我們要申請的許可權無非就是 Camera的許可權,還有讀寫外部儲存的許可權。

2.2.1.2 如何點選按鈕後可以跳到相應介面(拍照 or 相簿):

我們先來看拍照:

專案需求討論 - WebView下拍照及圖片選擇功能

2.2.1.2.1 設定開啟相機Intent的Action

專案需求討論 - WebView下拍照及圖片選擇功能

我們知道開啟某個介面,就是startActivity(Intent);平常比如跳到撥號介面等。只要對Intent設定相應的Action即可。 具體我們可以看谷歌的Android官方教程網頁即可:

Android指南 - 通用 Intent

我們可以看到有這些:

專案需求討論 - WebView下拍照及圖片選擇功能
專案需求討論 - WebView下拍照及圖片選擇功能

我們可以這個目錄中看到了相機,我們具體看相機的介紹:

專案需求討論 - WebView下拍照及圖片選擇功能

:當您使用 ACTION_IMAGE_CAPTURE拍攝照片時,相機可能還會在結果 Intent 中返回縮小尺寸的照片副本(縮圖),這個副本以 Bitmap 形式儲存在名為 dataextra 欄位中。

所以我們這裡跳到拍照介面也是一樣,只要建立跳到相機介面的Intent即可:

Intent captureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
複製程式碼
2.2.1.2.2 設定相機拍攝的照片的儲存位置

專案需求討論 - WebView下拍照及圖片選擇功能

因為有些人需要在自己的APP中呼叫拍照的功能,存在自己指定的目錄下面,所以需要在startActivity啟動相機介面時候同時傳遞過去資訊,告訴拍照了之後照片存的位置。

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

專案需求討論 - WebView下拍照及圖片選擇功能

其實很簡單,設定我們接下去要拍的照片的完整儲存路徑,然後得到File物件,再通過Uri.fromFile方法再通過剛才我們的File物件來獲得Uri

(當然如果這裡你只需要開啟系統相機,以下第二部分可以忽略)

2.獲取所有相機的Intent集合:

因為我們手機上面可能有很多個相機軟體,所以我們需要先找到能開啟各自相機軟體的Intent,我們通過PackageManager.queryIntentActivities的方式來進行符合拍照Action的Intent的軟體,然後得到它們具體的詳細資訊,比如包名及對應的activity名字等,然後把相應的Intent變得更加詳細即可。

專案需求討論 - WebView下拍照及圖片選擇功能

3.把uri賦值給Intent:

在上面貼出的Android 官方網頁上面的相機部分其實也提到過了如何設定儲存位置:

專案需求討論 - WebView下拍照及圖片選擇功能

所以這裡我們只需要找到相應的Intent,然後把我們的Uri位置賦值給Intent即可:

intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
複製程式碼

最後只需要startActivity去啟動我們這個指定了開啟相機的特定Intent即可。


拍照說完了,我們再來看在相簿介面選擇圖片: 其實總體思路和拍照是一模一樣,無非就是指定Intent是開啟了相簿的Intent。

專案需求討論 - WebView下拍照及圖片選擇功能

還是在剛才的Android 官網我們可以看到:

專案需求討論 - WebView下拍照及圖片選擇功能

因為我們是檢視本地的圖片,所以我們要使用 ACTION_GET_CONTENT,同時指定MIME型別是圖片型別,如果要進行圖片多選,就再指定EXTRA_ALLOW_MULTIPLEtrue

專案需求討論 - WebView下拍照及圖片選擇功能

同時也給出了例項程式碼:

專案需求討論 - WebView下拍照及圖片選擇功能

這裡我要提一下,我們在設定IntentAction的時候不只是可以使用ACTION_GET_CONTENT,還可以使用ACTION_PICK

專案需求討論 - WebView下拍照及圖片選擇功能

我們可以看到上面寫著可以用來選擇資料,然後返回被選中的選項。

但是在具體手機操作上有點不同(不知道不同的手機系統會不會結果不同,我只測了模擬器):

專案需求討論 - WebView下拍照及圖片選擇功能

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

2.2.2 出現選擇框讓使用者選擇

專案需求討論 - WebView下拍照及圖片選擇功能

我們可以看到可以自定義彈框,比如我們設定固定的按鈕,然後再點選特定按鈕後啟動我們的上面提過的特定的Intent即可。

這裡我們講如果只是給定我們想要啟動的多個Intent的選項,讓系統幫我們彈出彈框及相關按鈕,關鍵字就是Intent.createChooser方法

專案需求討論 - WebView下拍照及圖片選擇功能

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

2.2.3 獲取使用者在相機或者相簿選擇的圖片Uri

專案需求討論 - WebView下拍照及圖片選擇功能
因為我們不是單純的跳到了相機介面或者是相簿介面就可以了,我們還需要獲取使用者在那些應用外的介面到底選了什麼圖片,所以單純的startActivity肯定不夠,所以大家肯定想到了使用startActivityForResult來啟動,這樣才能根據使用者不同的操作來進行相應的處理。

我們知道需要複寫onActivityResult來處理,主要也就三個引數(int requestCode, int resultCode, Intent data)。具體的內容圖片裡面也寫的很清楚。


2.3 Uri 和 ValueCallback

所以我們ValueCallback例項在 WebChromeClient的方法裡面拿到了,Uri也通過相機或者相簿的選擇下獲取到了。最終呼叫把獲取的Uri 賦值給ValueCallback.onReceiveValue()即可。

PS: 取消這次網頁點選選取圖片的請求,只需要呼叫onReceiveValue(null)即可。

結語:

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

相關文章