170

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

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

4

14 に答える 14

147

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

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

于 2009-12-19T07:35:19.463 に答える
51

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

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

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」のファイルをコピーする必要はありません。

于 2011-02-20T01:17:07.793 に答える
31

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

またMediaStore.EXTRA_OUTPUT 、まだ作成されていないディレクトリにあるファイルを指定する場合は、最初に作成する必要があります。カメラ アプリは mkdir を実行しません。

于 2010-10-06T05:39:14.797 に答える
28

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

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
    }
  }
}

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

于 2009-12-15T21:56:14.320 に答える
7

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

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

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

于 2010-12-14T17:45:37.030 に答える
4

カメラにSDカードに書き込みますが、ギャラリーアプリの新しいアルバムに保存するには、これを使用します:

 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 を置き換える必要があることに注意してください。これは nexus one でテストされました。uri はプライベート クラスで宣言されているため、アクティビティの結果で、必要に応じてプレビュー用に uri から imageView に画像を読み込むことができます。

于 2010-06-29T08:01:37.320 に答える
1

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

于 2012-06-27T08:53:30.920 に答える
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] ボタンは正常に機能します:)

于 2011-03-28T16:08:50.847 に答える
0

Activity Result Code Simple を使用してこの問題を解決するのは非常に簡単です。この方法を試してください。

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();
   }
}
于 2016-05-01T13:51:18.317 に答える
0

さまざまなソース (ギャラリー、カメラ) からの画像の選択を管理する単純なライブラリを作成し、それをどこかの場所 (SD カードまたは内部メモリ) に保存して、画像を元に戻すので、自由に使用して改善してください - Android-ImageChooser .

于 2012-11-24T19:20:28.577 に答える
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()ファイルをコピーするためのメソッドです。

于 2014-06-16T15:37:25.237 に答える
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;
}
于 2015-10-21T04:18:59.267 に答える