2018-01-08 android Webview支持input type=file

http://blog.csdn.net/earbao/article/details/50716747

http://david-wei.github.io/2015/07/07/Webview-%E6%94%AF%E6%8C%81-input-type-file/

在一個(gè)帶有input tpye=file標(biāo)簽的Html頁面岩喷,通過WebView,上傳android手機(jī)上的圖片监憎,發(fā)現(xiàn)不工作纱意。(在Ios和微信上完全正常工作)所以,需要研究一下Android的WebView鲸阔,來支持type=file的標(biāo)簽偷霉。

WebView設(shè)置WebChromeClient

重寫WebChromeClient中關(guān)于文件選擇的方法迄委,onShowFileChooser和openFileChooser。(項(xiàng)目中只需要選擇圖片类少,所以加上了圖片過濾叙身。)

public static final int INPUT_FILE_REQUEST_CODE = 1;

private ValueCallback mUploadMessage;

private final static int FILECHOOSER_RESULTCODE = 2;

private ValueCallback mFilePathCallback;

private String mCameraPhotoPath;

private WebChromeClient mWebChromeClient = new WebChromeClient() {

? ? // android 5.0

? ? public boolean onShowFileChooser(

? ? ? ? ? ? WebView webView, ValueCallback filePathCallback,

? ? ? ? ? ? WebChromeClient.FileChooserParams fileChooserParams) {

? ? ? ? if (mFilePathCallback != null) {

? ? ? ? ? ? mFilePathCallback.onReceiveValue(null);

? ? ? ? }

? ? ? ? mFilePathCallback = filePathCallback;

? ? ? ? Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);

? ? ? ? if (takePictureIntent.resolveActivity(getPackageManager()) != null) {

? ? ? ? ? ? // Create the File where the photo should go

? ? ? ? ? ? File photoFile = null;

? ? ? ? ? ? try {

? ? ? ? ? ? ? ? photoFile = createImageFile();

? ? ? ? ? ? ? ? takePictureIntent.putExtra("PhotoPath", mCameraPhotoPath);

? ? ? ? ? ? } catch (IOException ex) {

? ? ? ? ? ? ? ? // Error occurred while creating the File

? ? ? ? ? ? ? ? Log.e("WebViewSetting", "Unable to create Image File", ex);

? ? ? ? ? ? }

? ? ? ? ? ? // Continue only if the File was successfully created

? ? ? ? ? ? if (photoFile != null) {

? ? ? ? ? ? ? ? mCameraPhotoPath = "file:" + 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("image/*");

? ? ? ? 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, INPUT_FILE_REQUEST_CODE);

? ? ? ? return true;

? ? }

? ? //The undocumented magic method override

? ? //Eclipse will swear at you if you try to put @Override here

? ? // For Android 3.0+

? ? public void openFileChooser(ValueCallback uploadMsg) {

? ? ? ? mUploadMessage = uploadMsg;

? ? ? ? Intent i = new Intent(Intent.ACTION_GET_CONTENT);

? ? ? ? i.addCategory(Intent.CATEGORY_OPENABLE);

? ? ? ? i.setType("image/*");

? ? ? ? WebViewActivity.this.startActivityForResult(Intent.createChooser(i, "Image Chooser"), FILECHOOSER_RESULTCODE);

? ? }

? ? // For Android 3.0+

? ? public void openFileChooser(ValueCallback uploadMsg, String acceptType) {

? ? ? ? mUploadMessage = uploadMsg;

? ? ? ? Intent i = new Intent(Intent.ACTION_GET_CONTENT);

? ? ? ? i.addCategory(Intent.CATEGORY_OPENABLE);

? ? ? ? i.setType("image/*");

? ? ? ? WebViewActivity.this.startActivityForResult(

? ? ? ? ? ? ? ? Intent.createChooser(i, "Image Chooser"),

? ? ? ? ? ? ? ? FILECHOOSER_RESULTCODE);

? ? }

? ? //For Android 4.1

? ? public void openFileChooser(ValueCallback uploadMsg, String acceptType, String capture) {

? ? ? ? mUploadMessage = uploadMsg;

? ? ? ? Intent i = new Intent(Intent.ACTION_GET_CONTENT);

? ? ? ? i.addCategory(Intent.CATEGORY_OPENABLE);

? ? ? ? i.setType("image/*");

? ? ? ? WebViewActivity.this.startActivityForResult(Intent.createChooser(i, "Image Chooser"), WebViewActivity.FILECHOOSER_RESULTCODE);

? ? }

};

選擇結(jié)果的回調(diào)

在onActivityResult中獲取對(duì)應(yīng)的選取文件的返回結(jié)果

public void onActivityResult(int requestCode, int resultCode, Intent data) {

? ? if (requestCode == FILECHOOSER_RESULTCODE) {

? ? ? ? if (null == mUploadMessage) return;

? ? ? ? Uri result = data == null || resultCode != RESULT_OK ? null

? ? ? ? ? ? ? ? : data.getData();

? ? ? ? if (result != null) {

? ? ? ? ? ? String imagePath = ImageFilePath.getPath(this, result);

? ? ? ? ? ? if (!StrUtils.isEmpty(imagePath)) {

? ? ? ? ? ? ? ? result = Uri.parse("file:///" + imagePath);

? ? ? ? ? ? }

? ? ? ? }

? ? ? ? mUploadMessage.onReceiveValue(result);

? ? ? ? mUploadMessage = null;

? ? } else if (requestCode == INPUT_FILE_REQUEST_CODE && mFilePathCallback != null) {

? ? ? ? // 5.0的回調(diào)

? ? ? ? Uri[] results = null;

? ? ? ? // Check that the response is a good one

? ? ? ? if (resultCode == Activity.RESULT_OK) {

? ? ? ? ? ? if (data == null) {

? ? ? ? ? ? ? ? // If there is not data, then we may have taken a photo

? ? ? ? ? ? ? ? if (mCameraPhotoPath != null) {

? ? ? ? ? ? ? ? ? ? Logger.d("camera_photo_path", mCameraPhotoPath);

? ? ? ? ? ? ? ? ? ? results = new Uri[]{Uri.parse(mCameraPhotoPath)};

? ? ? ? ? ? ? ? }

? ? ? ? ? ? } else {

? ? ? ? ? ? ? ? String dataString = data.getDataString();

? ? ? ? ? ? ? ? Logger.d("camera_dataString", dataString);

? ? ? ? ? ? ? ? if (dataString != null) {

? ? ? ? ? ? ? ? ? ? results = new Uri[]{Uri.parse(dataString)};

? ? ? ? ? ? ? ? }

? ? ? ? ? ? }

? ? ? ? }

? ? ? ? mFilePathCallback.onReceiveValue(results);

? ? ? ? mFilePathCallback = null;

? ? } else {

? ? ? ? super.onActivityResult(requestCode, resultCode, data);

? ? ? ? return;

? ? }

}

文件路徑的獲取

返回文件的解析,因?yàn)閔tml頁面需要的是文件硫狞,所以客戶端需要返回的是對(duì)應(yīng)文件的路徑信轿。這樣,就會(huì)存在一個(gè)問題残吩,在Android 4.4上财忽,通過文件選擇返回的結(jié)果都是對(duì)應(yīng)以content開頭格式的對(duì)應(yīng)的路徑。這就得需要咱們來進(jìn)行判斷世剖,最終都需要轉(zhuǎn)回成以file開頭對(duì)應(yīng)的格式文件定罢。下面,我封裝成了一個(gè)ImageFilePath的類旁瘫,通過調(diào)用getPath方法來獲取最終的結(jié)果。這個(gè)類的方法如下:

/**

* Method for return file path of Gallery image

*

* @param context

* @param uri

* @return path of the selected image file from gallery

*/

public static String getPath(final Context context, final Uri uri) {

? ? // check here to KITKAT or new version

? ? final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;

? ? // DocumentProvider

? ? if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {

? ? ? ? // ExternalStorageProvider

? ? ? ? if (isExternalStorageDocument(uri)) {

? ? ? ? ? ? final String docId = DocumentsContract.getDocumentId(uri);

? ? ? ? ? ? final String[] split = docId.split(":");

? ? ? ? ? ? final String type = split[0];

? ? ? ? ? ? if ("primary".equalsIgnoreCase(type)) {

? ? ? ? ? ? ? ? return Environment.getExternalStorageDirectory() + "/"

? ? ? ? ? ? ? ? ? ? ? ? + split[1];

? ? ? ? ? ? }

? ? ? ? }

? ? ? ? // DownloadsProvider

? ? ? ? else if (isDownloadsDocument(uri)) {

? ? ? ? ? ? final String id = DocumentsContract.getDocumentId(uri);

? ? ? ? ? ? final Uri contentUri = ContentUris.withAppendedId(

? ? ? ? ? ? ? ? ? ? Uri.parse("content://downloads/public_downloads"),

? ? ? ? ? ? ? ? ? ? Long.valueOf(id));

? ? ? ? ? ? return getDataColumn(context, contentUri, null, null);

? ? ? ? }

? ? ? ? // MediaProvider

? ? ? ? else if (isMediaDocument(uri)) {

? ? ? ? ? ? final String docId = DocumentsContract.getDocumentId(uri);

? ? ? ? ? ? final String[] split = docId.split(":");

? ? ? ? ? ? final String type = split[0];

? ? ? ? ? ? Uri contentUri = null;

? ? ? ? ? ? if ("image".equals(type)) {

? ? ? ? ? ? ? ? contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;

? ? ? ? ? ? } else if ("video".equals(type)) {

? ? ? ? ? ? ? ? contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;

? ? ? ? ? ? } else if ("audio".equals(type)) {

? ? ? ? ? ? ? ? contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;

? ? ? ? ? ? }

? ? ? ? ? ? final String selection = "_id=?";

? ? ? ? ? ? final String[] selectionArgs = new String[] { split[1] };

? ? ? ? ? ? return getDataColumn(context, contentUri, selection,

? ? ? ? ? ? ? ? ? ? selectionArgs);

? ? ? ? }

? ? }

? ? // MediaStore (and general)

? ? else if ("content".equalsIgnoreCase(uri.getScheme())) {

? ? ? ? // Return the remote address

? ? ? ? if (isGooglePhotosUri(uri))

? ? ? ? ? ? return uri.getLastPathSegment();

? ? ? ? return getDataColumn(context, uri, null, null);

? ? }

? ? // File

? ? else if ("file".equalsIgnoreCase(uri.getScheme())) {

? ? ? ? return uri.getPath();

? ? }

? ? return null;

}

/**

* Get the value of the data column for this Uri. This is useful for

* MediaStore Uris, and other file-based ContentProviders.

*

* @param context

*? ? ? ? ? ? The context.

* @param uri

*? ? ? ? ? ? The Uri to query.

* @param selection

*? ? ? ? ? ? (Optional) Filter used in the query.

* @param selectionArgs

*? ? ? ? ? ? (Optional) Selection arguments used in the query.

* @return The value of the _data column, which is typically a file path.

*/

public static String getDataColumn(Context context, Uri uri,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? String selection, String[] selectionArgs) {

? ? Cursor cursor = null;

? ? final String column = "_data";

? ? final String[] projection = { column };

? ? try {

? ? ? ? cursor = context.getContentResolver().query(uri, projection,

? ? ? ? ? ? ? ? selection, selectionArgs, null);

? ? ? ? if (cursor != null && cursor.moveToFirst()) {

? ? ? ? ? ? final int index = cursor.getColumnIndexOrThrow(column);

? ? ? ? ? ? return cursor.getString(index);

? ? ? ? }

? ? } finally {

? ? ? ? if (cursor != null)

? ? ? ? ? ? cursor.close();

? ? }

? ? return null;

}

/**

* @param uri

*? ? ? ? ? ? The Uri to check.

* @return Whether the Uri authority is ExternalStorageProvider.

*/

public static boolean isExternalStorageDocument(Uri uri) {

? ? return "com.android.externalstorage.documents".equals(uri

? ? ? ? ? ? .getAuthority());

}

/**

* @param uri

*? ? ? ? ? ? The Uri to check.

* @return Whether the Uri authority is DownloadsProvider.

*/

public static boolean isDownloadsDocument(Uri uri) {

? ? return "com.android.providers.downloads.documents".equals(uri

? ? ? ? ? ? .getAuthority());

}

/**

* @param uri

*? ? ? ? ? ? The Uri to check.

* @return Whether the Uri authority is MediaProvider.

*/

public static boolean isMediaDocument(Uri uri) {

? ? return "com.android.providers.media.documents".equals(uri

? ? ? ? ? ? .getAuthority());

}

/**

* @param uri

*? ? ? ? ? ? The Uri to check.

* @return Whether the Uri authority is Google Photos.

*/

public static boolean isGooglePhotosUri(Uri uri) {

? ? return "com.google.android.apps.photos.content".equals(uri

? ? ? ? ? ? .getAuthority());

}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末琼蚯,一起剝皮案震驚了整個(gè)濱河市酬凳,隨后出現(xiàn)的幾起案子梁丘,更是在濱河造成了極大的恐慌不皆,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,695評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件锤悄,死亡現(xiàn)場(chǎng)離奇詭異峦睡,居然都是意外死亡翎苫,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門榨了,熙熙樓的掌柜王于貴愁眉苦臉地迎上來煎谍,“玉大人,你說我怎么就攤上這事龙屉∧耪常” “怎么了?”我有些...
    開封第一講書人閱讀 168,130評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵转捕,是天一觀的道長(zhǎng)作岖。 經(jīng)常有香客問我,道長(zhǎng)五芝,這世上最難降的妖魔是什么痘儡? 我笑而不...
    開封第一講書人閱讀 59,648評(píng)論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮枢步,結(jié)果婚禮上沉删,老公的妹妹穿的比我還像新娘渐尿。我一直安慰自己,他們只是感情好丑念,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,655評(píng)論 6 397
  • 文/花漫 我一把揭開白布涡戳。 她就那樣靜靜地躺著,像睡著了一般脯倚。 火紅的嫁衣襯著肌膚如雪渔彰。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,268評(píng)論 1 309
  • 那天推正,我揣著相機(jī)與錄音恍涂,去河邊找鬼。 笑死植榕,一個(gè)胖子當(dāng)著我的面吹牛再沧,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播尊残,決...
    沈念sama閱讀 40,835評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼炒瘸,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了寝衫?” 一聲冷哼從身側(cè)響起顷扩,我...
    開封第一講書人閱讀 39,740評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎慰毅,沒想到半個(gè)月后隘截,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,286評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡汹胃,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,375評(píng)論 3 340
  • 正文 我和宋清朗相戀三年婶芭,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片着饥。...
    茶點(diǎn)故事閱讀 40,505評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡犀农,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出贱勃,到底是詐尸還是另有隱情井赌,我是刑警寧澤,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布贵扰,位于F島的核電站仇穗,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏戚绕。R本人自食惡果不足惜纹坐,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,873評(píng)論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望舞丛。 院中可真熱鬧耘子,春花似錦果漾、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,357評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至捍歪,卻和暖如春户辱,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背糙臼。 一陣腳步聲響...
    開封第一講書人閱讀 33,466評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工庐镐, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人变逃。 一個(gè)月前我還...
    沈念sama閱讀 48,921評(píng)論 3 376
  • 正文 我出身青樓必逆,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親揽乱。 傳聞我的和親對(duì)象是個(gè)殘疾皇子名眉,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,515評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容

  • ¥開啟¥ 【iAPP實(shí)現(xiàn)進(jìn)入界面執(zhí)行逐一顯】 〖2017-08-25 15:22:14〗 《//首先開一個(gè)線程,因...
    小菜c閱讀 6,444評(píng)論 0 17
  • 問題 前幾天接到的一個(gè)需求凰棉,是關(guān)于第三方理財(cái)產(chǎn)品的H5上傳照片問題璧针。對(duì)方說他們的新的需求,需要接入方配合上傳資產(chǎn)照...
    指間沙似流年閱讀 3,252評(píng)論 3 49
  • (1)鬧鐘 創(chuàng)建鬧鐘(ACTION_SET_ALARM)示例Intent: 注:為了調(diào)用ACTION_SET_AL...
    sunnygarden閱讀 1,634評(píng)論 0 10
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理渊啰,服務(wù)發(fā)現(xiàn),斷路器申屹,智...
    卡卡羅2017閱讀 134,701評(píng)論 18 139
  • #Android 基礎(chǔ)知識(shí)點(diǎn)總結(jié) ---------- ##1.adb - android debug bridg...
    Mythqian閱讀 3,299評(píng)論 2 11