WebViewの<input type="file">でカメラ、画像からデータを取得する [Android]
目次
1. 作るもの
2. 初期設定
3. 画面設計
4. HTMLファイル
5. コーディング
1. 作るもの
WebViewで表示するHTMLの<input type="file">で「カメラ or 画像ファイル」の選択ダイアログを表示します。
何も設定しない初期状態の<input type="file">ではカメラ撮影や画像ファイルを選択できません。それ所か、ボタンを押しても何も動作しません。
それで、ネットで色々探して、後述する参考URLのGoogleサイトにあるコードがありましたが、次のようになっていました。
という状況でしたが元コードを改変してAndroid5/7/8で動作確認しました。
2. 初期設定
2-1. APIレベル
今回はWebViewコントロールを扱うのでAndroidの動作可能なAPIレベルは21(Android5.0/Lollipop)にしています。
設定方法はAndroid Studioで左のTreeViewにあるGradle Scriptsのbuild.gradleの「minSdkVersionを21」にします。
2-2. 権限
[AndroidManifest.xml]
ファイルを扱うので次のストレージ権限を追加します。
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
3. 画面設計
WebViewを1つ配置します。
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<WebView
android:id="@+id/webView"
android:layout_width="368dp"
android:layout_height="447dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
4. HTMLファイル
HTMLをリソースに追加するにはWebViewの基本操作をご覧下さい。
[index.html]
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<script>
function onAddFile(event) {
var reader = new FileReader();
var files = event.target.files;
var image = document.getElementById("img_source");
reader.onload = function (event) {
image.onload = function (){
document.getElementById("msg").innerHTML =
image.width+"x" +image.height;
};
image.onerror = function (){
alert('この画像は読み込めません。');
};
image.src = reader.result;
};
reader.onerror = function (){
alert('このファイルは読み込めません。');
}
if (files[0]){
reader.readAsDataURL(files[0]);
}
}
</script>
</head>
<body>
<br>
<input type="file" id="inputfile" accept="image/*" onchange="onAddFile(event);"><br>
<br>
<div id="msg"></div>
<img id="img_source">
</body>
</html>
5. コーディング
import android.Manifest;
import android.app.Activity;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Environment;
import android.provider.MediaStore;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.webkit.ValueCallback;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
// URIリスト(テンポラリ)
private ArrayList<Uri> UriList = new ArrayList<>();
// INIファイルのアクセス用
private SharedPreferences.Editor editor;
// カメラ & 画像選択ダイアログ用
public static final int INPUT_FILE_REQUEST_CODE = 1;
private ValueCallback<Uri[]> mFilePathCallback;
private String mCameraPhotoPath;
////////////////////////////////////////////////////////////////////////////////////////////////////
// 汎用メソッド
////////////////////////////////////////////////////////////////////////////////////////////////////
// パーミッションダイアログ
public boolean CheckPermission(Activity actibity, String permission, int requestCode){
// 権限の確認
if (ActivityCompat.checkSelfPermission(actibity, permission) !=
PackageManager.PERMISSION_GRANTED) {
// 権限の許可を求めるダイアログを表示する
ActivityCompat.requestPermissions(actibity, new String[]{permission},requestCode);
return false;
}
return true;
}
// リソースからファイルを生成する(/data/data/パッケージ名/files/)
public boolean setRawResources(Context context , int resourcesID, String fileName){
boolean result = false;
// リソースの読み込み
InputStream is = context.getResources().openRawResource(resourcesID);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte [] buffer = new byte[1024];
try{
// 1024バイト毎、ファイルを読み込む
while(true) {
int len = is.read(buffer);
if(len < 0) break;
baos.write(buffer, 0, len);
}
}catch (Exception e){
e.printStackTrace();
return result;
}
// ファイルの生成
File file = new File(context.getFilesDir() + "/" + fileName);
FileOutputStream fos = null;
try {
fos = new FileOutputStream(file);
fos.write(baos.toByteArray());
result = true;
} catch (Exception e) {
e.printStackTrace();
} finally {
if(fos != null){
try{
fos.close();
}catch (Exception e){
e.printStackTrace();
}
}
}
return result;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// イベント
////////////////////////////////////////////////////////////////////////////////////////////////////
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// ***************
// INI設定
// ***************
// INIファイルのアクセス用
// ※「カメラ or 画像」の選択画面を表示する度に
// ※Picturesフォルダに0バイトのファイルが作成されるので,それを削除する為にです。
SharedPreferences prefs = getSharedPreferences("MyINI", MODE_PRIVATE);
editor = prefs.edit();
// INIファイルからURIリストの情報を読み込む
int count = prefs.getInt("count", 0);
for (int i = 0; i < count; i++) {
try {
String str = prefs.getString("uri" + i, "");
UriList.add(Uri.parse(str));
} catch (Exception e) {
e.printStackTrace();
}
}
// ***************
// 権限設定
// ***************
// ストレージの権限確認
CheckPermission(MainActivity.this,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
1000);
// ***************
// リソース
// ***************
// リソースからファイルを生成する(/data/data/パッケージ名/files/に作成)
setRawResources(MainActivity.this, R.raw.index, "index.html");
// ***************
// WebView
// ***************
WebView webView = findViewById(R.id.webView);
// キャッシュクリア
// ※開発時のみ有効にする
webView.clearCache(true);
// JavaScriptを有効にする
webView.getSettings().setJavaScriptEnabled(true);
// <input type="file">で「カメラ、画像の選択」を表示する
// ※元ソースはGoogleです。参考URLを参照してください。
// ※そのままでは動作しないので改変しています。
webView.setWebChromeClient(new WebChromeClient() {
public boolean onShowFileChooser(
WebView webView, ValueCallback<Uri[]> filePathCallback,
WebChromeClient.FileChooserParams fileChooserParams) {
if (mFilePathCallback != null) {
mFilePathCallback.onReceiveValue(null);
}
mFilePathCallback = filePathCallback;
mCameraPhotoPath = null;
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (takePictureIntent.resolveActivity(MainActivity.this.getPackageManager()) != null) {
String filename = Environment.getExternalStorageDirectory() + "/dummy.jpg";
ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.TITLE, filename);
values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
Uri uri = getContentResolver().
insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
UriList.add(uri);
// INIファイルにURIリストの情報を書き込む
for (int i = 0; i < UriList.size(); i++) {
editor.putString("uri" + i, UriList.get(i).toString());
}
editor.putInt("count", UriList.size());
editor.apply();
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
mCameraPhotoPath = uri.toString();
}
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, "選択");
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentArray);
startActivityForResult(chooserIntent, INPUT_FILE_REQUEST_CODE);
return true;
}
});
// ファイルを読み込む
webView.loadUrl("file:///" + MainActivity.this.getFilesDir() + "/index.html");
}
// <input type="file">で「カメラ、画像の選択」を表示する
// ※元ソースはGoogleです。参考URLを参照してください。
// ※そのままでは動作しないので改変しています。
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode != INPUT_FILE_REQUEST_CODE || mFilePathCallback == null) {
super.onActivityResult(requestCode, resultCode, data);
return;
}
Uri[] results = null;
if (resultCode == Activity.RESULT_OK) {
if (data == null) {
if (mCameraPhotoPath != null) {
results = new Uri[]{Uri.parse(mCameraPhotoPath)};
}
} else {
String dataString = data.getDataString();
if (dataString != null) {
results = new Uri[]{Uri.parse(dataString)};
// このコードがないとAndroid8ではカメラ写真を取れない
} else {
if (mCameraPhotoPath != null) {
results = new Uri[]{Uri.parse(mCameraPhotoPath)};
}
}
}
}
mFilePathCallback.onReceiveValue(results);
mFilePathCallback = null;
return;
}
@Override
protected void onDestroy() {
// URIリストをクリア
for (Uri uri : UriList) {
// File.exists()はアクセス権限がないファイルが"false"になるので使用しない
try {
getContentResolver().delete(uri, null, null);
} catch (Exception e) {
}
}
super.onDestroy();
}
}
記事を書いている時点で気づいたのですが「ファイルの複数選択」は確認していません。でも、1つのファイルは確実に取得できているので、このコードで通らなくても多少の修正でいけるはずです。
追記:WebViewの<input type="file" multiple>でファイルを複数選択する の記事も書きました。
参考URL(Google)
https://gauntface.com/blog/2014/10/17/what-you-need-to-know-about-the-webview-in-l
https://github.com/googlearchive/chromium-webview-samples/blob/master/input-file-example/app/src/main/java/inputfilesample/android/chrome/google/com/inputfilesample/MainFragment.java
関連記事
プチモンテ ※この記事を書いた人
![]() | |
![]() | 💻 ITスキル・経験 サーバー構築からWebアプリケーション開発。IoTをはじめとする電子工作、ロボット、人工知能やスマホ/OSアプリまで分野問わず経験。 画像処理/音声処理/アニメーション、3Dゲーム、会計ソフト、PDF作成/編集、逆アセンブラ、EXE/DLLファイルの書き換えなどのアプリを公開。詳しくは自己紹介へ |
| 🎵 音楽制作 BGMは楽器(音源)さえあれば、何でも制作可能。歌モノは主にロック、バラード、ポップスを制作。歌詞は抒情詩、抒情的な楽曲が多い。楽曲制作は🔰2023年12月中旬 ~ | |









