web-dev-qa-db-ja.com

データフォルダ内のURIとしてカメラの結果を取得する方法は?

画像をキャプチャし、その画像を添付ファイルとして電子メールで送信するアプリケーションを作成しています。

Android.provider.MediaStore.ACTION_IMAGE_CAPTUREインテントアクションを使用してカメラを開き、ファイルのUriをパラメーターとして渡してEXTRA_OUTPUTを取得し、画像をファイルに戻します。これは完全に機能しており、external storage uriEXTRA_OUTPUTとして使用するとキャプチャされた画像を取得できますが、データフォルダーuriを使用すると機能せず、カメラが閉じず、すべてが閉じませんボタンが機能していません。

これは、外部ストレージディレクトリに結果を取得するための私のコードです

Intent i = new Intent(Android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
File out = Environment.getExternalStorageDirectory();
out = new File(out, imagename);
i.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(out));
startActivityForResult(i, CAMERA_RESULT);

このコードは、データフォルダー内の画像を取得するためのものです

Intent i = new Intent(Android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
File out = getFilesDir();
out = new File(out, MyPharmacyOptions.PRESCRIPTION_IMAGE_NAME);
i.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(out));
startActivityForResult(i, CAMERA_RESULT);

データフォルダーは3番目のアプリケーションにアクセスできないであるため、これが問題を引き起こす可能性があるため、ファイルを共有する1つのコンテンツプロバイダーを作成しました。

ここに私のコンテンツ提供クラスがあります

public class MyContentProvider extends ContentProvider {
    private static final String Tag = RingtonContentProvider.class.getName();
    public static final Uri CONTENT_URI = Uri
            .parse("content://x.y.z/");
    private static final HashMap<String, String> MIME_TYPES = new HashMap<String, String>();

    static {
        MIME_TYPES.put(".mp3", "audio/mp3");
        MIME_TYPES.put(".wav", "audio/mp3");
        MIME_TYPES.put(".jpg", "image/jpeg");
    }

    @Override
    public boolean onCreate() {
        return true;
    }

    @Override
    public String getType(Uri uri) {
        String path = uri.toString();

        for (String extension : MIME_TYPES.keySet()) {
            if (path.endsWith(extension)) {
                return (MIME_TYPES.get(extension));
            }
        }

        return (null);
    }

    @Override
    public ParcelFileDescriptor openFile(Uri uri, String mode)
            throws FileNotFoundException {
        File f = new File(getContext().getFilesDir(), uri.getPath());

        if (f.exists()) {
            return (ParcelFileDescriptor.open(f, ParcelFileDescriptor.MODE_READ_ONLY));
        }

        throw new FileNotFoundException(uri.getPath());
    }

    @Override
    public Cursor query(Uri url, String[] projection, String selection,
            String[] selectionArgs, String sort) {
        throw new RuntimeException("Operation not supported");
    }

    @Override
    public Uri insert(Uri uri, ContentValues initialValues) {
        File file = new File(getContext().getFilesDir(), uri.getPath());
        if(file.exists()) file.delete();
        try {
            file.createNewFile();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        return Uri.fromFile(file);
    }

    @Override
    public int update(Uri uri, ContentValues values, String where,
            String[] whereArgs) {
        throw new RuntimeException("Operation not supported");
    }

    @Override
    public int delete(Uri uri, String where, String[] whereArgs) {
        File f = new File(getContext().getFilesDir(), "image1.jpg");
        if(f.exists()) f.delete();
        f = new File(getContext().getFilesDir(), "image2.jpg");
        if(f.exists()) f.delete();

        getContext().getContentResolver().notifyChange(CONTENT_URI, null);

    }
}

したがって、このコンテンツを使用するには、次のコードを使用してuriをカメラアクティビティに渡します

Intent i = new Intent(Android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
Uri uri = MyContentProvider.CONTENT_URI;
uri = Uri.withAppendedPath(uri, imagename);
getContentResolver().insert(uri, null);
getContentResolver().notifyChange(RingtonContentProvider.CONTENT_URI, null);
Log.d(Tag, uri.toString());
i.putExtra(MediaStore.EXTRA_OUTPUT, uri);

startActivityForResult(i, CAMERA_RESULT);

今、外部ストレージディレクトリ以外のURLを渡すと、カメラは開いていますが、エミュレータでは閉じていませんが、デバイスではカメラは閉じられますが、結果は得られません。

マニフェストファイルでこのコンテンツの提供を宣言しました

<provider
Android:name=".contentproviders.MyContentProvider"
Android:authorities="x.y.z" />

また、外部ストレージに書き込むおよびカメラを使用するの許可も与えました。

外部ストレージを使用して画像をキャプチャできますが、外部ストレージが利用できない場合は画像をキャプチャしてメールを送信したいので、外部ストレージではなくデータディレクトリに画像を保存します。

コンテンツプロバイダを作成すると、画像をメールアプリケーションと共有することもできます。

カメラインテントにエクストラを提供しない場合、アクティビティの結果として画像をbyte []としてデータエクストラとして返しますが、これはサムネイルのためであるため、高解像度を取得できませんこの方法を使用した画像。

65
Dharmendra

この問題を解決するには2つの方法があります。

1。 onActivityResultメソッドから受け取ったビットマップを保存します

以下のコードを使用して写真をキャプチャする目的でカメラを起動できます

Intent cameraIntent=new Intent(Android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(cameraIntent, CAMERA_REQUEST);

写真をキャプチャした後、onActivityResultメソッドでビットマップを取得します

if (requestCode == CAMERA_REQUEST) {  
    Bitmap photo = (Bitmap) data.getExtras().get("data"); 
 }

これで、このビットマップを内部ストレージに保存できます

注:ここでは、ビットマップオブジェクトはサムイメージで構成され、フル解像度のイメージはありません

2。コンテンツプロバイダーを使用してビットマップを内部ストレージに直接保存します

ここで、コンテンツプロバイダークラスを作成して、カメラのアクティビティに対するローカルストレージディレクトリの許可を許可します。

以下のサンプルプロバイダーの例

public class MyFileContentProvider extends ContentProvider {
    public static final Uri CONTENT_URI = Uri.parse
                                    ("content://com.example.camerademo/");
    private static final HashMap<String, String> MIME_TYPES = 
                                     new HashMap<String, String>();

    static {
        MIME_TYPES.put(".jpg", "image/jpeg");
        MIME_TYPES.put(".jpeg", "image/jpeg");
    }

    @Override
    public boolean onCreate() {

        try {
            File mFile = new File(getContext().getFilesDir(), "newImage.jpg");
            if(!mFile.exists()) {
                mFile.createNewFile();
            }
            getContext().getContentResolver().notifyChange(CONTENT_URI, null);
            return (true);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }

    }

    @Override
    public String getType(Uri uri) {
        String path = uri.toString();

        for (String extension : MIME_TYPES.keySet()) {
            if (path.endsWith(extension)) {
                return (MIME_TYPES.get(extension));
            }
        }
        return (null);
    }

    @Override
    public ParcelFileDescriptor openFile(Uri uri, String mode)
    throws FileNotFoundException {

        File f = new File(getContext().getFilesDir(), "newImage.jpg");
        if (f.exists()) {
            return (ParcelFileDescriptor.open(f,
                    ParcelFileDescriptor.MODE_READ_WRITE));
        }
        throw new FileNotFoundException(uri.getPath());
    }
}

その後、次のコードを使用してURIを使用してカメラアクティビティに渡すことができます

Intent i = new Intent(Android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
i.putExtra(MediaStore.EXTRA_OUTPUT, MyFileContentProvider.CONTENT_URI);
startActivityForResult(i, CAMERA_RESULT);

独自のプロバイダーを作成したくない場合は、support-library-v4の FileProvider を使用できます。詳細なヘルプについては、 この投稿 をご覧ください。

68
Dharmendra

私が見つけた最良の解決策は次のとおりです。FileProvider(support-library-v4が必要)内部ストレージを使用します! https://developer.Android.com/reference/Android/support/v4/content/FileProvider.html

  1. Application要素のManifestでFileProviderを定義します。

    <provider
          Android:name="Android.support.v4.content.FileProvider"
          Android:authorities="your.package.name.fileprovider"
          Android:exported="false"
          Android:grantUriPermissions="true" >
          <meta-data
                     Android:name="Android.support.FILE_PROVIDER_PATHS"
                     Android:resource="@xml/image_path" />
    </provider>
    
  2. マニフェストルート要素にアクセス許可を追加します。

    <uses-permission Android:name="Android.permission.CAMERA" />
    <uses-feature Android:name="Android.hardware.camera" Android:required="false" />
    
  3. たとえばres/xml/image_path.xmlでイメージパスを定義します。

    <paths xmlns:Android="http://schemas.Android.com/apk/res/Android">
    <files-path name="captured_image" path="your/path/"/>
    </paths>
    
  4. Java:

    private static final int IMAGE_REQUEST_CODE = 1;
    // your authority, must be the same as in your manifest file 
    private static final String CAPTURE_IMAGE_FILE_PROVIDER = "your.package.name.fileprovider";
    

4.1キャプチャの意図:

    File path = new File(activity.getFilesDir(), "your/path");
    if (!path.exists()) path.mkdirs();
    File image = new File(path, "image.jpg");
    Uri imageUri = FileProvider.getUriForFile(activity, CAPTURE_IMAGE_FILE_PROVIDER, image);
    Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
    startActivityForResult(intent, IMAGE_REQUEST_CODE);

4.2 onActivityResult():

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent intent) {
        if (requestCode == IMAGE_REQUEST_CODE) {
            if (resultCode == Activity.RESULT_OK) {
                File path = new File(getFilesDir(), "your/path");
                if (!path.exists()) path.mkdirs();
                File imageFile = new File(path, "image.jpg");
                // use imageFile to open your image
            }
        }
        super.onActivityResult(requestCode, resultCode, intent);
    }
20
Harry

写真をキャプチャするために電話する意図、

Intent cameraIntent = new Intent(Android.provider.MediaStore.ACTION_IMAGE_CAPTURE);  
startActivityForResult(cameraIntent, CAMERA_REQUEST);  

次に、ActivityResultのビットマップを取得します

 if (requestCode == CAMERA_REQUEST) {   
            Bitmap photo = (Bitmap) data.getExtras().get("data");  
 }   

次に、それを内部メモリに書き込みます これを参照

// The openfileOutput() method creates a file on the phone/internal storage in the context of your application  
final FileOutputStream fos = openFileOutput("my_new_image.jpg", Context.MODE_PRIVATE); 

// Use the compress method on the BitMap object to write image to the OutputStream 
bm.compress(CompressFormat.JPEG, 90, fos); 

次に、そのファイルを読み込むために、

Bitmap bitmap = BitmapFactory.decodeFile(file); 
5
MKJParekh

最初に外部ストレージに写真を保存して試してみてください-

@Override
public void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   setContentView(R.layout.main);
   this.imageView = (ImageView)this.findViewById(R.id.imageView1);
   Button photoButton = (Button) this.findViewById(R.id.button1);
   photoButton.setOnClickListener(new View.OnClickListener() {

    @Override
    public void onClick(View v) {
        Intent cameraIntent = new Intent(Android.provider.MediaStore.ACTION_IMAGE_CAPTURE); 
        startActivityForResult(cameraIntent, CAMERA_REQUEST); 
    }
});
}

protected void onActivityResult(int requestCode, int resultCode, Intent data) {  
   if (requestCode == CAMERA_REQUEST) {  
        Bitmap bmp = intent.getExtras().get("data");
        ByteArrayOutputStream stream = new ByteArrayOutputStream();

         bmp.compress(Bitmap.CompressFormat.PNG, 100, stream);
         byte[] byteArray = stream.toByteArray(); // convert camera photo to byte array

         // save it in your external storage.
        FileOutputStream fo = new FileOutputStream(new File(Environment.getExternalStorageDirectory() + "/_camera.png"));

        fo.write(byteArray);
        fo.flush();
        fo.close();
   }  
} 

次のターゲット-

File cameraFile = new File(Environment.getExternalStorageDirectory() + "/_camera.png");                 
startActivityForResult(Intent.createChooser(new Intent(Intent.ACTION_SEND)
        .setType("image/jpg")
        .putExtra(Intent.EXTRA_SUBJECT, "Subject")
        .putExtra(Intent.EXTRA_STREAM, Uri.fromFile(cameraFile))
        .putExtra(Intent.EXTRA_TEXT, textBody), "Send your message using"), Constant.EMAIL);
3
Suvam Roy

とにかくカメラ画像キャプチャインテントを開くにはsdカードが必要になるため、コンテンツプロバイダーを必要とせずにこれを行うこともできます。もちろん、SDカードの存在をハックすることはできますが、カメラインテントキャプチャを使用することはできません。デバイス間で十分にサポートされていないため、ネイティブを使用する代わりにトリミング画像。

File mediaStorageDir;
String photoFileName = "photo.jpg";


    public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // page = getArguments().getInt("someInt", 0);
    // title = getArguments().getString("someTitle");
    // Get safe storage directory for photos
        mediaStorageDir = new File(
                Environment                          .getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),
                APP_TAG);
        // Create the storage directory if it does not exist
        if (!mediaStorageDir.exists() && !mediaStorageDir.mkdirs()) {
            Log.d(APP_TAG, "Directory exists: " + mediaStorageDir.isDirectory());
            Log.d(APP_TAG, "Directory exists: " + mediaStorageDir.getPath());
            Log.d(APP_TAG,
                    "Directory exists: "
                            + Environment.getExternalStorageState());
            Log.d(APP_TAG, "failed to create directory");
        }

}

あなたの「写真を撮る」コードで:

            Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
            intent.putExtra(MediaStore.EXTRA_OUTPUT, getPhotoFileUri(photoFileName));

...

public Uri getPhotoFileUri(String fileName) {

    return Uri.fromFile(new File(mediaStorageDir.getPath() + File.separator
            + fileName));
}
1
Droid Teahouse

このバグをしばらく調査した後、カメラのインテントを呼び出したアクティビティは、電話のメモリが不足しているときにのみ再起動されることに気付きました。そのため、アクティビティが再開されるため、キャプチャされた画像へのパスまたはUriを保持するオブジェクトが更新されます(null)

したがって、onActivityResultでnullオブジェクトをキャッチ/検出し、ユーザーにデバイスの空き容量を増やすか、電話を再起動して一時的な修正を促すことをお勧めします。

1
kc ochibili