Android ACTION_IMAGE_CAPTUREインテント


163

ユーザーが新しい写真を撮れるように、ネイティブカメラアプリを使用しようとしています。を省略しEXTRA_OUTPUT extraて小さなビットマップ画像を返す場合は、問題なく動作します。ただし、putExtra(EXTRA_OUTPUT,...)開始する前に意図した場合は、カメラアプリの[OK]ボタンを押すまですべてが機能します。「OK」ボタンは何もしません。カメラアプリは開いたままで、何もロックされません。キャンセルできますが、ファイルは書き込まれません。ACTION_IMAGE_CAPTURE撮影した写真をファイルに書き込むには、具体的に何をしなければならないのでしょうか。

編集:MediaStore.ACTION_IMAGE_CAPTURE明確にするために、これは意図を介して行われます

回答:


144

これは、Androidの一部のバージョンで十分に文書化されたバグです。つまり、AndroidのGoogleエクスペリエンスビルドでは、画像のキャプチャがドキュメントどおりに機能しません。私が一般的に使用しているのは、ユーティリティクラスでこのようなものです。

public boolean hasImageCaptureBug() {

    // list of known devices that have the bug
    ArrayList<String> devices = new ArrayList<String>();
    devices.add("android-devphone1/dream_devphone/dream");
    devices.add("generic/sdk/generic");
    devices.add("vodafone/vfpioneer/sapphire");
    devices.add("tmobile/kila/dream");
    devices.add("verizon/voles/sholes");
    devices.add("google_ion/google_ion/sapphire");

    return devices.contains(android.os.Build.BRAND + "/" + android.os.Build.PRODUCT + "/"
            + android.os.Build.DEVICE);

}

次に、イメージキャプチャを起動すると、バグをチェックするインテントが作成されます。

Intent i = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
if (hasImageCaptureBug()) {
    i.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File("/sdcard/tmp")));
} else {
    i.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
}
startActivityForResult(i, mRequestCode);

次に、アクティビティに戻るときに、デバイスに基づいてさまざまなことを行います。

protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
     switch (requestCode) {
         case GlobalConstants.IMAGE_CAPTURE:
             Uri u;
             if (hasImageCaptureBug()) {
                 File fi = new File("/sdcard/tmp");
                 try {
                     u = Uri.parse(android.provider.MediaStore.Images.Media.insertImage(getContentResolver(), fi.getAbsolutePath(), null, null));
                     if (!fi.delete()) {
                         Log.i("logMarker", "Failed to delete " + fi);
                     }
                 } catch (FileNotFoundException e) {
                     e.printStackTrace();
                 }
             } else {
                u = intent.getData();
            }
    }

これにより、新しいカメラアプリを作成する手間が省けますが、このコードもすばらしいものではありません。大きな問題は

  1. バグのあるデバイスからフルサイズの画像を取得することはありません。画像コンテンツプロバイダーに挿入される幅512pxの画像を取得します。バグのないデバイスでは、すべてがドキュメントとして機能し、大きな通常の画像が得られます。

  2. リストを維持する必要があります。書かれているように、バグが修正されたバージョンのAndroid(たとえばcyanogenmodのビルド)でデバイスがフラッシュされる可能性があります。その場合、コードはクラッシュします。修正は、デバイス全体のフィンガープリントを使用することです。


21
Galaxy S:intent.getData()はnullを返します。さらに、Galaxy sのカメラアプリ自体が新しい写真をギャラリーに挿入しますが、uriは返されません。そのため、ファイルからギャラリーに写真を挿入すると、写真が複製されます。
Arseniy 2011年

1
ちょっと私のデバイスはここにリストされていませんが、私はウルウェイによって実装しましたが、アプリケーションはまだクラッシュします。理由はわかりません。助けてください
Geetanjali

このコードをdroid-xおよびsony xpheriaデバイスで使用します。どちらのデバイスもインテント値をnullとして返します。また、droidxは結果コードを0として返しますが、xpheriaは結果コードを-1として返します。誰もがそれが事実であるべき理由を知っています。
rOrlig 2009

5
ヤノクワの答えの冒頭にある「よく文書化されたバグ」へのリンクは正しくありません。そのバグは、putExtra(EXTRA_OUTPUT、...)
フランクハーパー

code.google.com/p/android/issues/detail?id=1480さて、それでは、これをどのように説明しますか?
ペンシルチェック2013年

50

私はこれが以前に回答されたことを知っていますが、多くの人がこれにつまずくのを知っているので、コメントを追加します。

Nexus Oneでもまったく同じ問題が発生しました。これは、カメラアプリが起動する前にディスクに存在しないファイルからのものでした。そのため、カメラアプリを起動する前にファイルが存在することを確認しました。ここに私が使用したいくつかのサンプルコードがあります:

String storageState = Environment.getExternalStorageState();
        if(storageState.equals(Environment.MEDIA_MOUNTED)) {

            String path = Environment.getExternalStorageDirectory().getName() + File.separatorChar + "Android/data/" + MainActivity.this.getPackageName() + "/files/" + md5(upc) + ".jpg";
            _photoFile = new File(path);
            try {
                if(_photoFile.exists() == false) {
                    _photoFile.getParentFile().mkdirs();
                    _photoFile.createNewFile();
                }

            } catch (IOException e) {
                Log.e(TAG, "Could not create file.", e);
            }
            Log.i(TAG, path);

            _fileUri = Uri.fromFile(_photoFile);
            Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE );
            intent.putExtra( MediaStore.EXTRA_OUTPUT, _fileUri);
            startActivityForResult(intent, TAKE_PICTURE);
        }   else {
            new AlertDialog.Builder(MainActivity.this)
            .setMessage("External Storeage (SD Card) is required.\n\nCurrent state: " + storageState)
            .setCancelable(true).create().show();
        }

まず、MD5ハッシュを使用して一意の(やや)ファイル名を作成し、それを適切なフォルダーに配置します。次に、それが存在するかどうかを確認します(存在しないはずですが、とにかく確認することをお勧めします)。存在しない場合は、親ディレクトリ(フォルダー)を取得し、それまでのフォルダー階層を作成します(そのため、ファイルの場所に至るフォルダーが存在しない場合は、この行の後に配置されます。その後、ファイルを作成します。ファイルが作成されると、Uriを取得してインテントに渡し、[OK]ボタンは期待どおりに機能し、すべてが正常に機能します。

これで、カメラアプリで[OK]ボタンを押すと、ファイルが指定した場所に表示されます。この例では、/ sdcard / Android / data / com.example.myapp / files / 234asdioue23498ad.jpgになります。

上記の「onActivityResult」にファイルをコピーする必要はありません。


13
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />Android 1.5よりも新しいバージョンを使用している場合は、マニフェストにあることを確認してください。数時間かけてカメラのデバッグを行いましたが、これは含まれていなかったため、保存は常に失敗します。
スワンソン、2011

これは私にとってはうまくいきますが、約10枚の画像ごとに、空のファイルは上書きされず、画像があるはずの場所に0バイトのファイルができます。これは非常に煩わしく、追跡するのが困難でした。
joey_g216 2011年

2
ジェリーBeanを使用したネクサス7などの一部のデバイスでは、Environment.getExternalStorageDirectory()。getName()を変更して、.getName()ではなく.getAbsolutePath()を使用する必要があります
Keith

35

私はさまざまな写真キャプチャ戦略を経験してきましたが、プラットフォームや特定のデバイスでは、上記の戦略の一部またはすべてが予期しない方法で失敗するケースが常にあるようです。すべてではないにしてもほとんどの場合に機能すると思われる以下のURI生成コードを使用する戦略を見つけることができました。

mPhotoUri = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, 
            new ContentValues());
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, mPhotoUri);
startActivityForResult(intent,CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE_CONTENT_RESOLVER);

ディスカッションにさらに貢献し、新規参入者を助けるために、写真キャプチャの実装に関するいくつかの異なる戦略を示すサンプル/テストアプリを作成しました。他の実装の貢献は議論に追加することが絶対に推奨されます。

https://github.com/deepwinter/AndroidCameraTester


これは素晴らしかった。私はまだすべてのデバイスでこれをテストする必要はありませんが、すでにかなりの数のテストを行っていると思います。このトピックには非常に多くのノイズがあり、この特定の答えはとてもシンプルでクリーンです。これまでのところ、Nexus 5で動作します。最初に、ACTION_IMAGE_CAPTUREがEXTRA_OUTPUTなしでは機能していないという多くのレポートを受け取りました。これが全面的に機能することを願っていますが、今のところこれで十分です。THX
リッチ

1
@deepwinter-ありがとうございます。私は髪を抜いていましたが、あなたのコンテンツリゾルバーソリューションは、私が抱えていたすべての問題のあるシナリオでうまく機能します。
Billy Coover、2014

このパーティーには遅かったのですが、これが私にとって唯一の解決策でした。本当にありがとう!
StackJP 2014年

どうにかMediaStore.EXTRA_OUTPUTを取得できますonActivityResultか、それとも自分で覚えなければなりませんか?写真の撮影中にアプリが破棄された場合でもアプリがそれを記憶できるように、永続的に保存する必要があります...
prom85

それはロリポップとサムスンのギャラクシーノート3上では動作しません:ava.lang.NullPointerException:nullのオブジェクト参照の上「)java.lang.Stringでandroid.net.Uri.getScheme(」仮想メソッドを呼び出すために試み
イゴール・ヤンコヴィッチ

30

カメラアプリの[OK]ボタンがエミュレータとネクサスの両方で何もしない同じ問題がありました。

空白や特殊文字を含まない安全なファイル名を指定すると、問題は解消しました。MediaStore.EXTRA_OUTPUT また、まだ作成されていないディレクトリにあるファイルを指定する場合は、最初にファイルを作成する必要があります。カメラアプリはmkdirを実行しません。


1
また、パスに複数のディレクトリレベルがあり、それらすべてを作成する場合は、のmkdirs()代わりにを使用してmkdir()ください(mkdir最後の1つである「リーフ」ディレクトリのみを作成します)。私はそれにいくつか問題がありました、そしてこの答えは私を助けました。
ダイアナ

単純なタイムスタンプを使用できますか?または、いくつかのエラーが発生する可能性がありますか?:)
Rakeeb Rajbhandari

@ user2247689「単純なタイムスタンプ」で作成したファイル名はわかりませんが、SDカードのFAT形式で許可されていない特殊文字が含まれていない限り、問題ないはずです。
Yenchi、

28

説明したワークフローは、説明したとおりに機能するはずです。インテントの作成に関するコードを見せていただければ助かります。一般に、次のパターンでは、試行していることを実行できます。

private void saveFullImage() {
  Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
  File file = new File(Environment.getExternalStorageDirectory(), "test.jpg");
  outputFileUri = Uri.fromFile(file);
  intent.putExtra(MediaStore.EXTRA_OUTPUT, outputFileUri);
  startActivityForResult(intent, TAKE_PICTURE);
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
  if ((requestCode == TAKE_PICTURE) && (resultCode == Activity.RESULT_OK)) {
    // Check if the result includes a thumbnail Bitmap
    if (data == null) {    
      // TODO Do something with the full image stored
      // in outputFileUri. Perhaps copying it to the app folder
    }
  }
}

ファイルを作成および保存するのはカメラアクティビティであり、実際にはアプリケーションの一部ではないため、アプリケーションフォルダーへの書き込み権限がないことに注意してください。appフォルダーにファイルを保存するには、SDカードに一時ファイルを作成し、onActivityResultハンドラーのappフォルダーに移動します。


これは動作するはずですが、カメラアプリの[OK]ボタンは何もしません。SDカードへの書き込みは正常に行われたため、これはファイル権限の問題であると思われます(アプリケーションには個人用ディレクトリへの書き込み権限が自動的に付与されていると思いましたが)。助けてくれてありがとう!
ドリュー

1
アプリには個人用ディレクトリへの書き込み権限があります-カメラアプリ(写真を撮っている)にはありません。ただし、ファイルはアプリ内にあるため、onActivityResultで移動できます。または、カメラアクティビティを自分で実装することもできますが、それはやり過ぎのようです。
Reto Meier、

カメラアプリのやり直しは一般的ですが面倒なアプローチのようです... getDir( "images"、MODE_WORLD_WRITEABLE)を使用する場合、そのアクセス許可は、そのディレクトリに作成するように指示するファイルには適用されませんか?
ドリュー

必ずしも。権限はフォルダに対するものであり、必ずしもその中のファイルに対するものではありません。
Reto Meier、

Android 2.2以降を搭載したAndroidデバイスでコードをテストしましたが、すべてのデバイスが画像を提供されたパスに保存しました。したがって、これは画像を取得するための標準的な動作であるべきだと思います。
Janusz 2013

7

上記のYenchiのコメントをフォローアップするために、カメラアプリが問題のディレクトリに書き込めない場合も、[OK]ボタンは何もしません。

つまり、アプリケーションによってのみ書き込み可能な場所にファイルを作成することはできません(たとえば、何かの下にあるgetCacheDir())ものは機能するgetExternalFilesDir()はずです)。

カメラアプリが指定されたEXTRA_OUTPUTパスに書き込めなかった場合にログにエラーメッセージを出力するとよいでしょうが、見つかりませんでした。


4

カメラにsdcardへの書き込みを行わせるが、ギャラリーアプリで新しいアルバムを保持するには、次のようにします。

 File imageDirectory = new File("/sdcard/signifio");
          String path = imageDirectory.toString().toLowerCase();
           String name = imageDirectory.getName().toLowerCase();


            ContentValues values = new ContentValues(); 
            values.put(Media.TITLE, "Image"); 
            values.put(Images.Media.BUCKET_ID, path.hashCode());
            values.put(Images.Media.BUCKET_DISPLAY_NAME,name);

            values.put(Images.Media.MIME_TYPE, "image/jpeg");
            values.put(Media.DESCRIPTION, "Image capture by camera");
           values.put("_data", "/sdcard/signifio/1111.jpg");
         uri = getContentResolver().insert( Media.EXTERNAL_CONTENT_URI , values);
            Intent i = new Intent("android.media.action.IMAGE_CAPTURE"); 

            i.putExtra(MediaStore.EXTRA_OUTPUT, uri);

            startActivityForResult(i, 0); 

毎回一意のファイル名を生成し、私が書いた1111.jpgを置き換える必要があることに注意してください。これはネクサス1でテストされました。uriはプライベートクラスで宣言されているため、アクティビティの結果では、必要に応じて画像をuriからimageViewにロードしてプレビューすることができます。


「_data」列はどこから来ますか?それのための定数はありませんか?
Matthias

3
ああ、それはdeveloper.android.com/reference/android/provider/…コードではマジックストリングを使用すべきではありません。
Matthias

1

私は同じ問題を抱えていて、私はそれを次のように修正しました:

問題は、アプリのみがアクセスできるファイルを指定すると(たとえば、 getFileStreamPath("file");

そのため、指定したファイルが実際に存在し、誰もがそのファイルに書き込みアクセスできることを確認しました。

Intent intent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
File outFile = getFileStreamPath(Config.IMAGE_FILENAME);
outFile.createNewFile();
outFile.setWritable(true, false);
intent.putExtra(android.provider.MediaStore.EXTRA_OUTPUT,Uri.fromFile(outFile));
startActivityForResult(intent, 2);

このように、カメラアプリは指定されたUriへの書き込みアクセス権を持ち、[OK]ボタンは正常に機能します:)


AndroidRuntime(2.1エミュレーター)がNoSuchMethodErrorをスロー:java.io.File.setWritable
jwadsa​​ck

1

私はあなたが写真をキャプチャするためにアンドロイドトレーニングの投稿に従うことをお勧めします。彼らは例で大小の写真を撮る方法を示しています。こちらからソースコードをダウンロードすることもできます


0

さまざまなソース(ギャラリー、カメラ)からの画像の選択を管理するシンプルなライブラリを作成し、それをいくつかの場所(SDカードまたは内部メモリ)に保存して画像を返却するため、自由に使用して改善してください-Android-ImageChooser


あなたのリンクはもう機能していません!! なぜ、私は2週間前にそれを見て、それを使いたかったのです:(
Mohammad Ersan

0

Praveenが指摘したように、ファイルはカメラから書き込み可能である必要があります。

私の使用法では、ファイルを内部ストレージに保存したいと考えていました。私はこれをしました:

Intent i = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (i.resolveActivity(getPackageManager()!=null)){
    try{
        cacheFile = createTempFile("img",".jpg",getCacheDir());
        cacheFile.setWritavle(true,false);
    }catch(IOException e){}
    if(cacheFile != null){
        i.putExtra(MediaStore.EXTRA_OUTPUT,Uri.fromFile(cacheFile));
        startActivityForResult(i,REQUEST_IMAGE_CAPTURE);
    }
}

以下cacheFileは、書き込まれるファイルを参照するために使用されるグローバルファイルです。次に、結果メソッドで返されるインテントはnullです。その場合、インテントを処理するメソッドは次のようになります。

protected void onActivityResult(int requestCode,int resultCode,Intent data){
    if(requestCode != RESULT_OK){
        return;
    }
    if(requestCode == REQUEST_IMAGE_CAPTURE){
        try{
            File output = getImageFile();
            if(output != null && cacheFile != null){
                copyFile(cacheFile,output);

                //Process image file stored at output

                cacheFile.delete();
                cacheFile=null;
            }
        }catch(IOException e){}
    }
}

ここではgetImageFile()画像が保存されるべきファイルを指定して作成するためのユーティリティメソッドで、copyFile()ファイルをコピーする方法です。


0

このコードを使用できます

private Intent getCameraIntent() {

    PackageManager packageManager = mContext.getPackageManager();
    List<ApplicationInfo> list = packageManager.getInstalledApplications(PackageManager.GET_UNINSTALLED_PACKAGES);
    Intent main = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
    List<ResolveInfo> launchables = packageManager.queryIntentActivities(main, 0);
    if (launchables.size() == 1)
        return packageManager.getLaunchIntentForPackage(launchables.get(0).activityInfo.packageName);
    else
        for (int n = 0; n < list.size(); n++) {
            if ((list.get(n).flags & ApplicationInfo.FLAG_SYSTEM) == 1) {
                Log.d("TAG", "Installed Applications  : " + list.get(n).loadLabel(packageManager).toString());
                Log.d("TAG", "package name  : " + list.get(n).packageName);
                String defaultCameraPackage = list.get(n).packageName;


                if (launchables.size() > 1)
                    for (int i = 0; i < launchables.size(); i++) {
                        if (defaultCameraPackage.equals(launchables.get(i).activityInfo.packageName)) {
                            return packageManager.getLaunchIntentForPackage(defaultCameraPackage);
                        }
                    }
            }
        }
    return null;
}

0

アクティビティ結果コードでこの問題を解決するのは非常に簡単です。

if (reqCode == RECORD_VIDEO) {
   if(resCode == RESULT_OK) {
       if (uri != null) {
           compress();
       }
    } else if(resCode == RESULT_CANCELED && data!=null){
       Toast.makeText(MainActivity.this,"No Video Recorded",Toast.LENGTH_SHORT).show();
   }
}

上記の不完全なスニペットは、それが主張した単純さには値しません。不慣れな旅行者には、確かに複雑さが隠されています。ファイルの有無のスキャンなど。ウィザーなど、アプリで公開または非公開になっています。プロバイダーの必要性やサムネイルのみのサイズの取得など。これらは、不注意な旅行者が知っておく必要がある情報ギャップです。
TruthAdjuster

0
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (resultCode == RESULT_CANCELED)
    {
        //do not process data, I use return; to resume activity calling camera intent
        enter code here
    }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.