Maksud Android ACTION_IMAGE_CAPTURE

163

Kami mencoba menggunakan aplikasi kamera asli untuk memungkinkan pengguna mengambil gambar baru. Ini berfungsi dengan baik jika kita meninggalkan EXTRA_OUTPUT extradan mengembalikan gambar Bitmap kecil. Namun, jika kita putExtra(EXTRA_OUTPUT,...)dengan niat sebelum memulai, semuanya berfungsi sampai Anda mencoba menekan tombol "Oke" di aplikasi kamera. Tombol "Ok" tidak melakukan apa-apa. Aplikasi kamera tetap terbuka dan tidak ada yang terkunci. Kita dapat membatalkannya, tetapi file tidak pernah ditulis. Apa sebenarnya yang harus kita lakukan ACTION_IMAGE_CAPTUREuntuk menulis gambar yang diambil ke file?

Sunting: Ini dilakukan melalui MediaStore.ACTION_IMAGE_CAPTUREniat, hanya untuk menjadi jelas

Drew
sumber

Jawaban:

144

ini adalah bug yang terdokumentasi dengan baik di beberapa versi android. yaitu, berdasarkan pengalaman Google membangun android, pengambilan gambar tidak berfungsi seperti yang didokumentasikan. apa yang saya umumnya gunakan adalah sesuatu seperti ini di kelas utilitas.

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

}

lalu ketika saya meluncurkan pengambilan gambar, saya membuat maksud yang memeriksa bug.

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

lalu dalam aktivitas yang saya kembali ke, saya melakukan hal-hal yang berbeda berdasarkan perangkat.

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

ini menghemat Anda harus menulis aplikasi kamera baru, tetapi kode ini juga tidak bagus. masalah besar adalah

  1. Anda tidak pernah mendapatkan gambar berukuran penuh dari perangkat dengan bug. Anda mendapatkan gambar dengan lebar 512px yang dimasukkan ke penyedia konten gambar. pada perangkat tanpa bug, semuanya berfungsi sebagai dokumen, Anda mendapatkan gambar normal yang besar.

  2. Anda harus menjaga daftarnya. seperti yang tertulis, adalah mungkin bagi perangkat untuk di-flash dengan versi android (katakanlah cyanogenmod's build ) yang memiliki bug diperbaiki. jika itu terjadi, kode Anda akan macet. Cara mengatasinya adalah dengan menggunakan seluruh sidik jari perangkat.

yanokwa
sumber
21
pada Galaxy S: intent.getData () mengembalikan nol, dan terlebih lagi, aplikasi kamera pada Galaxy s memasukkan foto baru ke dalam galeri dengan sendirinya, tetapi tidak ada uri yang dikembalikan. Jadi jika Anda memasukkan foto dari file ke galeri, akan ada foto yang digandakan.
Arseniy
1
hei perangkat saya tidak tercantum di sini dan saya telah menerapkannya dengan cara Anda. tetapi aplikasi saya masih macet saya tidak tahu alasannya. Tolong bantu saya
Geetanjali
menggunakan kode ini pada perangkat droid-x dan sony xpheria. Kedua perangkat mengembalikan nilai maksud sebagai nol. juga droidx mengembalikan kode hasil sebagai 0 sedangkan xpheria mengembalikan kode hasil sebagai -1. Ada yang tahu kenapa harus begitu.
rOrlig
5
Tautan ke "bug yang didokumentasikan dengan baik" yang ada di awal jawaban yanokwa salah. Bug itu adalah tentang memanggil aplikasi kamera tanpa putExtra (EXTRA_OUTPUT, ...)
Frank Harper
code.google.com/p/android/issues/detail?id=1480 Nah, bagaimana Anda menjelaskan ini?
Pencilcheck
50

Saya tahu ini telah dijawab sebelumnya tetapi saya tahu banyak orang tersandung pada ini, jadi saya akan menambahkan komentar.

Saya memiliki masalah yang sama persis ini terjadi pada Nexus One saya. Ini dari file yang tidak ada pada disk sebelum aplikasi kamera dimulai. Karena itu, saya memastikan bahwa file yang ada sebelum memulai aplikasi kamera. Berikut beberapa contoh kode yang saya gunakan:

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

Saya pertama kali membuat nama file yang unik (agak) menggunakan hash MD5 dan memasukkannya ke folder yang sesuai. Saya kemudian memeriksa untuk melihat apakah ada (tidak boleh, tetapi praktik yang baik untuk memeriksa pula). Jika tidak ada, saya mendapatkan dir induk (folder) dan membuat hierarki folder hingga itu (karena itu jika folder yang mengarah ke lokasi file tidak ada, mereka akan setelah baris ini. Kemudian setelah itu Saya membuat file. Setelah file dibuat saya mendapatkan Uri dan meneruskannya ke maksud dan kemudian tombol OK berfungsi seperti yang diharapkan dan semuanya berwarna emas.

Sekarang, ketika tombol Ok ditekan pada aplikasi kamera, file akan ada di lokasi yang diberikan. Dalam contoh ini akan menjadi /sdcard/Android/data/com.example.myapp/files/234asdioue23498ad.jpg

Tidak perlu menyalin file di "onActivityResult" seperti yang diposting di atas.

Donn Felker
sumber
13
Pastikan Anda memiliki <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />manifes jika Anda menggunakan yang lebih baru dari Android 1.5 - menghabiskan beberapa jam men-debug hal-hal kamera dan ini tidak termasuk sehingga menghemat saya akan selalu gagal.
Swanson
Ini berfungsi baik untuk saya, tetapi setiap sekitar 10 gambar atau lebih, file kosong tidak ditimpa dan saya berakhir dengan file 0 byte di mana gambar seharusnya. Ini sangat menjengkelkan dan sulit dilacak.
joey_g216
2
pada beberapa perangkat, seperti nexus 7 dengan jelly bean, Anda perlu mengubah Environment.getExternalStorageDirectory (). getName () untuk menggunakan .getAbsolutePath () alih-alih .getName ()
Keith
35

Saya telah melalui sejumlah strategi pengambilan foto, dan sepertinya selalu ada kasing, platform atau perangkat tertentu, di mana beberapa atau semua strategi di atas akan gagal dengan cara yang tidak terduga. Saya dapat menemukan strategi yang menggunakan kode generasi URI di bawah ini yang tampaknya berfungsi di sebagian besar atau tidak semua kasus.

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

Untuk berkontribusi lebih jauh dalam diskusi dan membantu pendatang baru saya telah membuat aplikasi sampel / uji yang menunjukkan beberapa strategi yang berbeda untuk implementasi pengambilan foto. Kontribusi implementasi lain pasti didorong untuk menambah diskusi.

https://github.com/deepwinter/AndroidCameraTester

musim dingin
sumber
Ini bagus. Saya belum mengujinya di semua perangkat saya, tapi saya berasumsi Anda sudah melakukannya. Ada begitu banyak kebisingan pada topik ini, dan jawaban khusus ini sangat sederhana dan bersih. Bekerja pada Nexus 5 sejauh ini, di situlah saya pertama kali mendapat banyak laporan bahwa ACTION_IMAGE_CAPTURE tidak berfungsi tanpa EXTRA_OUTPUT. Saya berharap ini bekerja di seluruh papan, tapi sejauh ini bagus. Thx
Rich
1
@ deepwinter - Terima kasih tuan. Saya mencabut rambut saya tetapi solusi penyelesai konten Anda bekerja dengan baik pada semua skenario bermasalah yang saya miliki.
Billy Coover
Agak terlambat ke pesta ini tetapi ini adalah satu-satunya solusi untuk saya. Terima kasih banyak!
StackJP
bisakah saya dapat mengambil MediaStore.EXTRA_OUTPUT onActivityResultatau saya benar-benar harus mengingatnya sendiri? Saya harus terus-menerus menyimpannya sehingga aplikasi saya bahkan mengingatnya jika dihancurkan saat foto diambil ...
prom85
Itu tidak bekerja pada samsung galaxy note 3 dengan lolipop: ava.lang.NullPointerException: Mencoba untuk memanggil metode virtual 'java.lang.String android.net.Uri.getScheme ()' pada referensi objek nol
Igor Janković
30

Saya memiliki masalah yang sama di mana tombol OK di aplikasi kamera tidak melakukan apa pun, baik pada emulator dan pada nexus satu.

Masalahnya hilang setelah menentukan nama file aman yang tanpa spasi, tanpa karakter khusus, di MediaStore.EXTRA_OUTPUT Juga, jika Anda menentukan file yang berada di direktori yang belum dibuat, Anda harus membuatnya terlebih dahulu. Aplikasi kamera tidak melakukan mkdir untuk Anda.

Yenchi
sumber
1
Dan pastikan Anda menggunakan mkdirs()alih-alih mkdir()jika jalur Anda memiliki lebih dari satu tingkat direktori dan Anda ingin semuanya dibuat ( mkdirhanya membuat yang terakhir, direktori 'daun'). Saya punya beberapa masalah dengan itu, dan jawaban ini membantu saya.
Diana
bisakah kita menggunakan cap waktu sederhana ?? atau bisakah juga membuat beberapa kesalahan ??
tolong beri
@ user2247689 Saya tidak yakin tentang nama file yang Anda buat dengan "stempel waktu sederhana" tetapi selama mereka tidak mengandung karakter khusus yang tidak diizinkan oleh format FAT pada kartu SD, saya rasa tidak masalah.
Yenchi
28

Alur kerja yang Anda gambarkan harus berfungsi seperti yang Anda gambarkan. Mungkin membantu jika Anda dapat menunjukkan kepada kami kode seputar pembuatan Intent. Secara umum, pola berikut harus membiarkan Anda melakukan apa yang Anda coba.

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

Perhatikan bahwa Aktivitas Kamera yang akan membuat dan menyimpan file, dan itu sebenarnya bukan bagian dari aplikasi Anda, jadi itu tidak akan memiliki izin menulis ke folder aplikasi Anda. Untuk menyimpan file ke folder aplikasi Anda, buat file sementara di kartu SD dan pindahkan ke folder aplikasi Anda di onActivityResulthandler.

Reto Meier
sumber
Ini / harus / berfungsi, tetapi tombol ok aplikasi kamera tidak melakukan apa-apa. Kami memang membuatnya bekerja menulis ke kartu SD, jadi saya menduga ini adalah masalah izin file (meskipun saya pikir aplikasi secara otomatis memiliki izin menulis ke direktori pribadi itu). Terima kasih untuk bantuannya!
Drew
1
Aplikasi Anda memiliki izin tulis untuk direktori pribadinya - aplikasi kamera (yang mengambil gambar) tidak. Anda dapat memindahkan file di onActivityResult, karena itu ada di dalam aplikasi Anda. Atau Anda bisa menerapkan Kegiatan kamera sendiri, tetapi itu tampaknya berlebihan.
Reto Meier
Mengulangi aplikasi kamera tampaknya merupakan pendekatan yang umum tetapi membosankan ... Ketika kita menggunakan getDir ("gambar", MODE_WORLD_WRITEABLE), apakah izin itu tidak berlaku untuk file apa pun yang kami minta dibuat di direktori itu?
Drew
Belum tentu. Izin adalah untuk folder, tidak harus untuk file di dalamnya.
Reto Meier
Saya menguji kode dengan perangkat Android dengan Android 2.2 dan yang lebih baru dan semuanya menyimpan gambar ke jalur yang disediakan. Jadi saya kira ini harus menjadi perilaku standar untuk mendapatkan gambar.
Janusz
7

Untuk menindaklanjuti komentar Yenchi di atas, tombol OK juga tidak akan melakukan apa-apa jika aplikasi kamera tidak dapat menulis ke direktori yang dimaksud.

Itu berarti bahwa Anda tidak dapat membuat file di tempat yang hanya dapat ditulisi oleh aplikasi Anda (misalnya, sesuatu di bawah getCacheDir())Sesuatu di bawah getExternalFilesDir()seharusnya berfungsi.

Akan lebih baik jika aplikasi kamera mencetak pesan kesalahan ke log jika tidak bisa menulis ke EXTRA_OUTPUTjalur yang ditentukan , tetapi saya tidak menemukannya.

Joe
sumber
4

agar kamera menulis ke kartu sd tetapi menyimpan Album baru di aplikasi galeri saya menggunakan ini:

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

Harap dicatat bahwa Anda perlu membuat nama file unik setiap kali dan mengganti teh 1111.jpg yang saya tulis. Ini diuji dengan nexus satu. uri dideklarasikan di kelas privat, jadi pada hasil aktivitas saya dapat memuat gambar dari uri ke imageView untuk pratinjau jika diperlukan.

nabula
sumber
dari mana kolom "_data" berasal? tidak ada yang konstan untuk itu?
Matthias
3
ah, ini developer.android.com/reference/android/provider/… Anda benar-benar tidak boleh menggunakan string ajaib dalam kode.
Matthias
1

Saya memiliki masalah yang sama dan saya memperbaikinya dengan yang berikut:

Masalahnya adalah ketika Anda menentukan file yang hanya dapat diakses oleh aplikasi Anda (mis. Dengan menelepon getFileStreamPath("file");)

Itu sebabnya saya hanya memastikan bahwa file yang diberikan benar-benar ada dan bahwa SEMUA ORANG memiliki akses tulis ke sana.

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

Dengan cara ini, aplikasi kamera memiliki akses tulis ke Uri yang diberikan dan tombol OK berfungsi dengan baik :)

Sialan
sumber
AndroidRuntime (2.1 emulator) melempar NoSuchMethodError: java.io.File.setWritable
jwadsack
1

Saya sarankan Anda untuk mengikuti pos pelatihan android untuk mengambil foto. Mereka menunjukkan dalam contoh bagaimana mengambil foto kecil dan besar. Anda juga dapat mengunduh kode sumber dari sini

JuanVC
sumber
0

Saya membuat pustaka sederhana yang akan mengelola pemilihan gambar dari sumber yang berbeda (Galeri, Kamera), mungkin menyimpannya ke beberapa lokasi (SD-Card atau memori internal) dan mengembalikan gambar itu jadi silakan bebas untuk menggunakannya dan memperbaikinya - Android-ImageChooser .

svenkapudija
sumber
tautan Anda tidak berfungsi lagi !! mengapa, saya melihatnya 2 minggu lalu, dan saya ingin menggunakannya :(
Mohammad Ersan
0

File perlu ditulis oleh kamera, seperti yang ditunjukkan Praveen .

Dalam penggunaan saya, saya ingin menyimpan file dalam penyimpanan internal. Saya melakukan ini dengan:

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

Berikut cacheFileadalah file global yang digunakan untuk merujuk ke file yang ditulis. Kemudian dalam metode hasil niat yang dikembalikan adalah nol. Maka metode untuk memproses maksud terlihat seperti:

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

Berikut getImageFile()adalah metode utilitas untuk memberi nama dan membuat file di mana gambar harus disimpan, dan copyFile()merupakan metode untuk menyalin file.

Duncan
sumber
0

Anda dapat menggunakan kode ini

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;
}
Ngông Cuồng
sumber
0

Sangat mudah untuk menyelesaikan masalah ini dengan Activity Result Code. Cobalah metode ini

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();
   }
}
Savita
sumber
Cuplikan tidak lengkap di atas tidak pantas dengan kesederhanaan yang diklaimnya. Tentunya, ada kerumitan tersembunyi bagi pelancong yang tidak terbiasa. Seperti memindai ada atau tidaknya file. Seperti layu itu tersedia untuk umum atau pribadi untuk aplikasi. Seperti kebutuhan penyedia atau mendapatkan ukuran thumbnail saja. Ini adalah kesenjangan informasi yang perlu diperhatikan oleh pelancong yang tidak waspada.
truthadjustr
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
    }
}
Ajesh Gupta
sumber