Android ACTION_IMAGE_CAPTURE Amaç


163

Kullanıcının yeni bir resim çekmesine izin vermek için yerel kamera uygulamasını kullanmaya çalışıyoruz. EXTRA_OUTPUT extraKüçük Bitmap görüntüsünü dışarıda bırakırsak iyi çalışır . Ancak, putExtra(EXTRA_OUTPUT,...)başlamadan önce niyetteysek, kamera uygulamasındaki "Tamam" düğmesine basmaya çalışana kadar her şey çalışır. "Tamam" düğmesi hiçbir şey yapmaz. Kamera uygulaması açık kalır ve hiçbir şey kilitlenmez. İptal edebiliriz, ancak dosya asla yazılmaz. ACTION_IMAGE_CAPTUREÇekilen bir dosyayı bir dosyaya yazmak için tam olarak ne yapmamız gerekiyor ?

Düzenleme: Bu MediaStore.ACTION_IMAGE_CAPTUREsadece net olmak gerekirse, niyet yoluyla yapılır

Yanıtlar:


144

Bu android bazı sürümlerinde iyi belgelenmiş bir hatadır . yani, android deneyimi Google android yapılarda, görüntü yakalama belgelendiği gibi çalışmaz. genel olarak kullandığım bir yardımcı program sınıfında böyle bir şeydir.

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

}

görüntü yakalamayı başlattığımda, hatayı kontrol eden bir niyet oluşturuyorum.

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

sonra geri döndüğüm aktivitede, cihaza göre farklı şeyler yaparım.

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

bu, yeni bir kamera uygulaması yazmak zorunda kalmanızı sağlar, ancak bu kod da harika değildir. büyük problemler

  1. hata içeren cihazlardan hiçbir zaman tam boyutlu görüntüler elde edemezsiniz. resim içeriği sağlayıcısına eklenen 512 piksel genişliğinde resimler alırsınız. hata olmayan cihazlarda, her şey belge olarak çalışır, büyük bir normal resim elde edersiniz.

  2. listeyi korumak zorundasınız. Yazıldığı gibi, cihazların hatayı düzelten bir android sürümü ( cyanogenmod'un derlemeleri ) ile yanıp sönmesi mümkündür . bu olursa, kodunuz kilitlenir. çözüm, tüm cihaz parmak izini kullanmaktır.


21
Galaxy S'de: intent.getData () null döndürür ve dahası, Galaxy s'deki kamera uygulaması kendi başına galeriye yeni fotoğraf ekler, ancak hiçbir uri geri gelmez. Dolayısıyla, dosyadan galeriye fotoğraf eklerseniz, çoğaltılmış fotoğraf olacaktır.
Arseniy

1
hey benim aygıt burada listelenmemiş ve ben ur yolu ile uyguladık. ama benim uygulama hala çöküyor neden bilmiyorum neden. bana yardım edin
Geetanjali

droid-x ve sony xpheria cihazlarında bu kodu kullanarak. Her iki cihaz da niyet değerini null olarak döndürür. ayrıca droidx sonuç kodunu 0, xpheria sonuç kodunu -1 olarak döndürür. Herkes bunun neden böyle olması gerektiğini biliyor.
rOrlig

5
Yanokwa'nın cevabının başındaki "iyi belgelenmiş hata" nın bağlantısı yanlış. Bu hata kamera uygulamasını putExtra
Frank Harper

code.google.com/p/android/issues/detail?id=1480 Peki, bunu nasıl açıklıyorsunuz?
Pencilcheck

50

Bunun daha önce yanıtlandığını biliyorum, ancak birçok insanın bu konuda tetiklendiğini biliyorum, bu yüzden bir yorum ekleyeceğim.

Aynı problemi Nexus One'ımda da yaşadım. Bu, kamera uygulaması başlamadan önce diskte bulunmayan dosyadan geliyordu. Bu nedenle, daha önce var olan dosyanın kamera uygulamasını başlattığından emin oldum. İşte kullandığım bazı örnek kod:

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

İlk önce bir MD5 karma kullanarak benzersiz (biraz) bir dosya adı oluşturmak ve uygun klasöre koymak. Sonra var olup olmadığını görmek için kontrol (olmamalı, ama yine de kontrol etmek için iyi uygulama). Eğer yoksa, üst dizini (bir klasör) alıp klasör hiyerarşisini kendim oluştururum (bu nedenle dosyanın konumuna giden klasörler yoksa, bu satırdan sonra olurlar.) Dosya oluşturulduktan sonra, dosya oluşturulduktan sonra Uri'yi alıp niyete iletiyorum ve sonra Tamam düğmesi beklendiği gibi çalışıyor ve hepsi altın.

Şimdi, kamera uygulamasında Tamam düğmesine basıldığında, dosya belirtilen konumda mevcut olacaktır. Bu örnekte /sdcard/Android/data/com.example.myapp/files/234asdioue23498ad.jpg

Yukarıda açıklandığı gibi "onActivityResult" içindeki dosyayı kopyalamanıza gerek yoktur.


13
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />Android 1.5'ten daha yeni bir sürüm kullanıyorsanız, bildiriminizde bulunduğunuzdan emin olun - kamera malzemelerini hata ayıklamak için birkaç saat geçirdim ve bu dahil edilmedi, böylece kaydetmelerim her zaman başarısız olacaktı.
swanson

Bu benim için iyi çalışıyor, ancak yaklaşık her 10 görüntüde bir, boş dosyanın üzerine yazılmaz ve görüntünün olması gereken yerde 0 baytlık bir dosyayla sonuçlanırım. Çok sinir bozucu ve izini bulmak zor oldu.
joey_g216

2
jöle fasulyeli nexus 7 gibi bazı cihazlarda, .getName () yerine .getAbsolutePath () kullanmak için Environment.getExternalStorageDirectory (). getName () öğesini değiştirmeniz gerekir
Keith

35

Bir dizi fotoğraf çekim stratejisinden geçtim ve her zaman yukarıdaki stratejilerin bir kısmının veya tamamının beklenmedik şekillerde başarısız olacağı bir durum, bir platform veya belirli cihazlar var gibi görünüyor. Tüm durumlarda olmasa da çoğu durumda işe yarayan aşağıdaki URI oluşturma kodunu kullanan bir strateji bulabildim.

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

Tartışmaya daha fazla katkıda bulunmak ve yeni gelenlere yardımcı olmak için fotoğraf çekimi uygulaması için birkaç farklı strateji gösteren bir örnek / test uygulaması oluşturdum. Diğer uygulamaların katkıları tartışmaya katılması için kesinlikle teşvik edilmektedir.

https://github.com/deepwinter/AndroidCameraTester


Bu harikaydı. Bunu tüm cihazlarımda henüz test etmedim, ancak zaten çok az yaptığınızı varsayıyorum. Bu konuda çok fazla gürültü var ve bu özel cevap çok basit ve temiz. Şimdiye kadar NEXus 5 üzerinde çalışıyor, ACTION_IMAGE_CAPTURE'ün EXTRA_OUTPUT olmadan çalışmadığı birçok rapor aldım. Umarım bu tüm tahta boyunca çalışır, ama şimdiye kadar çok iyi. Thx
Zengin

1
@deepwinter - Teşekkür ederim efendim. Saçımı çekiyordum, ancak içerik çözücü çözümünüz sahip olduğum tüm sorunlu senaryolarda iyi çalışıyor.
Billy Coover

Bu partiye biraz geç kaldım ama bu benim için çalışmak için tek çözüm oldu. Çok teşekkürler!
StackJP

MediaStore.EXTRA_OUTPUT'u bir şekilde alabilir miyim onActivityResultveya gerçekten kendim hatırlamam gerekir mi? Bir fotoğraf çekilirken imha edilirse uygulamamın bile hatırlaması için
kalıcı olarak kaydetmek zorundayım

Samsung Galaxy Note 3'te lolipop ile çalışmaz: ava.lang.NullPointerException: Boş bir nesne başvurusunda 'java.lang.String android.net.Uri.getScheme ()' sanal yöntemini çağırmayı deneyin
Igor Janković

30

Kamera uygulamasındaki Tamam düğmesinin, emülatörde ve nexus one'da hiçbir şey yapmadığı aynı problemi yaşadım.

Beyaz boşluklar olmadan, özel karakterler olmadan güvenli bir dosya adı belirledikten sonra sorun ortadan kalktı. MediaStore.EXTRA_OUTPUT Ayrıca, henüz oluşturulmamış bir dizinde bulunan bir dosyayı belirlerseniz, önce onu oluşturmanız gerekir. Kamera uygulaması sizin için mkdir yapmaz.


1
Ayrıca , yolunuzda birden fazla dizin düzeyi varsa ve bunların tümünü oluşturulmasını istiyorsanız ( yalnızca sonuncuyu, 'yaprak' dizinini oluşturur) mkdirs()yerine kullandığınızdan emin olun . Bununla ilgili bazı problemlerim vardı ve bu cevap bana yardımcı oldu. mkdir()mkdir
Diana

basit zaman damgalarını kullanabilir miyiz ?? ya da bazı hatalar yapabilir ?? lütfen cevap verdiğinde beni etiketle :)
Rakeeb Rajbhandari

@ user2247689 "Basit zaman damgaları" tarafından oluşturduğunuz dosya adından emin değilim ama SD kartta FAT biçiminin izin vermediği özel karakterler içermediği sürece, bence iyi olmalı.
Yenchi

28

Tanımladığınız iş akışı, tanımladığınız şekilde çalışmalıdır. Bize niyetin yaratılmasıyla ilgili kodu gösterebilirseniz yardımcı olabilir. Genel olarak, aşağıdaki desen denediğiniz şeyi yapmanıza izin vermelidir.

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

Dosyayı oluşturacak ve kaydedecek olan Kamera Etkinliği olduğunu ve aslında uygulamanızın bir parçası olmadığını unutmayın, bu nedenle uygulama klasörünüze yazma iznine sahip olmayacaktır. Bir dosyayı uygulama klasörünüze kaydetmek için SD kartta geçici bir dosya oluşturun ve dosyayı onActivityResultişleyicideki uygulama klasörünüze taşıyın .


Bu / gerekir / çalışmak, ancak kamera uygulamasının ok düğmesi hiçbir şey yapmaz. SD karta yazmaya çalışırken aldık, bu yüzden bu bir dosya izinleri sorunu olduğundan şüpheleniyorum (bir uygulamanın otomatik olarak kişisel dizinine yazma izinleri olduğunu düşündüm). yardım için teşekkürler!
Drew

1
Uygulamanızın kişisel dizinine yazma izni var - kamera uygulaması (resmi çekiyor) almıyor. Uygulamayı içinde olduğu gibi, dosyayı onActivityResult öğesinde taşıyabilirsiniz. Alternatif olarak bir kamera Etkinliğini kendiniz uygulayabilirsiniz, ancak bu aşırıya kaçmış gibi görünüyor.
Reto Meier

Kamera uygulamasını yeniden yapmak yaygın ancak sıkıcı bir yaklaşım gibi görünüyor ... getDir'i ("images", MODE_WORLD_WRITEABLE) kullandığımızda, bu izin bu dizinde oluşturmasını söylediğimiz herhangi bir dosya için geçerli değil mi?
Drew

Şart değil. İzin, klasör içindeki dosyalar için değil, klasör içindir.
Reto Meier

Kodu Android 2.2 ve daha yeni Android cihazlarla test ettim ve hepsi görüntüyü sağlanan yola kaydetti. Bu yüzden görüntüleri almak için bunun standart bir davranış olması gerektiğini düşünüyorum.
Janusz

7

Yukarıdaki Yenchi'nin yorumunu takip etmek için, kamera uygulaması söz konusu dizine yazamazsa Tamam düğmesi de hiçbir şey yapmaz.

Bu, dosyayı yalnızca uygulamanız tarafından yazılabilecek bir yerde oluşturamayacağınız anlamına gelir (örneğin, altında bir getCacheDir())şey altında getExternalFilesDir()çalışması gerekir.

Kamera uygulaması, belirtilen EXTRA_OUTPUTyola yazamazsa günlüklere bir hata mesajı yazdırdıysa iyi olurdu , ancak bir tane bulamadım.


4

kamera sdcard yazmak ama galeri uygulaması üzerinde yeni bir albüm tutmak için ben bunu kullanın:

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

Her seferinde benzersiz bir dosya adı oluşturmanız ve yazdığım 1111.jpg dosyasını değiştirmeniz gerektiğini lütfen unutmayın. Bu nexus one ile test edildi. uri özel sınıfta bildirilir, bu nedenle etkinlik sonucu gerekirse uri'den imageView'a görüntüyü yükleyebilirim.


"_data" sütunu nereden geliyor? bunun için sabit yok mu?
Matthias

3
ah, bu developer.android.com/reference/android/provider/… Kodda sihirli dizeler kullanmamalısınız.
Matthias

1

Ben aynı sorunu vardı ve ben aşağıdaki ile düzeltti:

Sorun, yalnızca uygulamanızın erişebileceği bir dosya belirttiğinizde (ör. getFileStreamPath("file"); )

Bu yüzden verilen dosyanın gerçekten var olduğundan ve HERKES'in ona yazma erişimi olduğundan emin oldum.

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

Bu şekilde, kamera uygulaması verilen Uri'ye yazma erişimine sahiptir ve OK düğmesi iyi çalışır :)


AndroidRuntime (2.1 emulator) NoSuchMethod atıyor: java.io.File.setWritable
jwadsack

1

Fotoğraf çekmek için android eğitim mesajını takip etmenizi öneririm. Bir örnekte küçük ve büyük fotoğrafların nasıl çekileceğini gösterirler. Ayrıca kaynak kodunu indirebilirsiniz burada


0

Farklı kaynaklardan (Galeri, Kamera) resim seçmeyi, belki bir yere kaydetmeyi (SD Kart veya dahili bellek) yönetecek ve görüntüyü geri döndürecek basit bir kütüphane oluşturdum, bu yüzden lütfen kullanmaktan ve geliştirmekten çekinmeyin - Android-ImageChooser .


bağlantınız artık çalışmıyor !! neden, 2 hafta önce gördüm ve kullanmak istedim :(
Mohammad Ersan

0

Praveen'nin işaret ettiği gibi, dosyanın kamera tarafından yazılabilir olması gerekir .

Kullanımımda dosyayı dahili depolama biriminde saklamak istedim. Bunu şu şekilde yaptım:

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

İşte cacheFileyazılır dosyaya başvurmak için kullanılan küresel bir dosyadır. Sonra sonuç yönteminde döndürülen niyet null olur. Sonra amacı işleme yöntemi şöyle görünür:

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

Burada getImageFile()görüntünün saklanması gereken dosyayı adlandırmak ve oluşturmak için bir yardımcı yöntem ve copyFile()bir dosyayı kopyalamak için bir yöntemdir.


0

Bu kodu kullanabilirsiniz

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

Etkinlik Sonuç Kodu ile bu sorunu çözmek çok basit Bu yöntemi deneyin

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

Yukarıdaki eksik parçacık iddia ettiği basitliği hak etmiyor. Elbette, yabancı gezgin için gizli karmaşıklıklar var. Dosyanın varlığı veya yokluğu için tarama gibi. Kalmamak gibi bu uygulama için kamuya açık veya özel. Bir sağlayıcı ihtiyacı veya sadece küçük resim boyutu gibi. Bunlar, dikkatsiz gezginlerin bilmesi gereken bilgi boşluklarıdır.
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
    }
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.