Android 如何在数据文件夹中将相机结果作为uri获取?

Android 如何在数据文件夹中将相机结果作为uri获取?,android,android-camera,android-contentprovider,Android,Android Camera,Android Contentprovider,我正在创建一个应用程序,我想在其中捕获一个图像,然后将该图像作为附件发送到电子邮件中 我正在使用android.provider.MediaStore.ACTION\u IMAGE\u CAPTURE打开一个摄像头intent ACTION,并将文件的Uri作为参数EXTRA\u OUTPUT传递,以将图像返回到文件。如果我使用外部存储uri作为额外输出的话,它工作得很好,我能够获得拍摄的图像,但是如果我使用数据文件夹uri,它不工作,相机不关闭,它的所有按钮都不工作 下面是我在外部存储目录中获

我正在创建一个应用程序,我想在其中捕获一个图像,然后将该图像作为附件发送到电子邮件中

我正在使用android.provider.MediaStore.ACTION\u IMAGE\u CAPTURE打开一个摄像头intent ACTION,并将文件的Uri作为参数
EXTRA\u OUTPUT
传递,以将图像返回到文件。如果我使用
外部存储uri
作为
额外输出
的话,它工作得很好,我能够获得拍摄的图像,但是如果我使用数据文件夹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);
我知道第三个应用程序无法访问数据文件夹,因此这可能会导致问题,因此我创建了一个内容提供商来共享该文件

这是我的课程内容

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);

    }
}
现在,如果我将url传递给其他外部存储目录,相机将打开,但在emulator中它没有关闭,但在设备中相机将关闭,但我没有得到结果

我已在清单文件中声明此内容

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

此外,我还允许写入外部存储器,并允许使用相机


我可以使用外部存储器捕获图像,但我希望将图像存储在数据目录而不是外部存储器中,因为如果外部存储器不可用,我希望捕获图像并发送邮件

如果我创建内容,那么我还可以将我的图像共享到电子邮件应用程序


如果我们没有为附加功能提供摄像头,它将在“活动结果”中以字节[]的形式返回图像作为附加数据,但这是用于缩略图,因此我无法使用这种方式获取高分辨率图像。

首先将照片保存在外部存储器中,然后尝试-

@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);

有意打电话拍摄照片

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");  
 }   
然后将其写入内存

下一次读那个文件的时候

Bitmap bitmap = BitmapFactory.decodeFile(file); 

有两种方法可以解决这个问题

1。保存从onActivityResult方法收到的位图

您可以使用下面的代码通过intent启动相机以捕获照片

Intent cameraIntent=new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(cameraIntent, CAMERA_REQUEST);
Intent i = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
i.putExtra(MediaStore.EXTRA_OUTPUT, MyFileContentProvider.CONTENT_URI);
startActivityForResult(i, CAMERA_RESULT);
捕获照片后,您将在onActivityResult方法中获得位图

if (requestCode == CAMERA_REQUEST) {  
    Bitmap photo = (Bitmap) data.getExtras().get("data"); 
 }
现在,您只需将此位图保存到内部存储器即可

注意:此处位图对象由thumb图像组成,它不会具有全分辨率图像

2。使用内容提供程序将位图直接保存到内部存储器

在这里,我们将创建content provider类,以允许本地存储目录对摄影机活动的权限

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);
示例提供程序示例如下所示

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());
    }
}

如果您不想创建自己的提供程序,则可以使用from support-library-v4。有关详细帮助,您可以查看

,您也可以不需要内容提供商就可以执行此操作,因为您仍然需要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));


我找到的最佳解决方案是:FileProvider(需要支持-library-v4) 它使用内部存储器!

  • 在应用程序元素的清单中定义文件提供程序:

    <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>
    
  • 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():


    在研究这个bug一段时间后,我注意到调用摄像头意图的活动只有在手机内存不足时才会重新启动。 因此,由于活动已重新启动,因此保存捕获映像的路径或Uri的对象将被刷新(空)


    因此,我建议您捕获/检测onActivityResult中的空对象,并提示用户释放设备空间或重新启动手机进行快速临时修复。

    我可以使用外部存储器捕获图像,但我不想将图像存储在数据目录而不是外部存储器中,因为如果外部存储器不可用,我想捕获图像并发送邮件。如果我创建内容,那么我也可以将我的图像共享到电子邮件应用程序。使用此路径保存图像-感谢您的快速回复。这是将图像保存到数据文件夹中的一种可能方法,但此图像用于缩略图,因此此图像的质量非常低,因此我无法使用此图像。我必须使用此图像出于处方目的,因此我不能使用此缩略图bmp.compress(Bitmap.CompressFormat.PNG,100,stream);这是为了保持质量。因此,您可以修改它。使用50或40而不是100%压缩。我喜欢第一个解决方案的简单性,并在我的应用程序中实现了它。但是,它只能获得低分辨率的图像…@DiscoS2它将为您提供低分辨率的图像。要获取图像的实际大小,请在外部存储器中创建一个文件,然后将文件uri传递给相机,以便在完成捕获后,相机将以原始分辨率将图像写入文件。@Dharmendra我正在尝试获取实际大小的图像,并在onActivityResult()上获取空意图;下面是我的代码:final String timeString=new SimpleDateFormat(“HH_mm_ss_SSS”).format(cal.getTime());image=new文件(imagesFolder,“ec_u”+timeString+“.jpg”);uriurisavedimage=Uri.fromFile(图像);intent=newintent(android.provider.MediaStore.ACTION\u IMAGE\u CAPTURE);
    <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>
    
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-feature android:name="android.hardware.camera" android:required="false" />
    
    <paths xmlns:android="http://schemas.android.com/apk/res/android">
    <files-path name="captured_image" path="your/path/"/>
    </paths>
    
    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";
    
        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);
    
        @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);
        }