169 votes

Intent Android ACTION_IMAGE_CAPTURE

Nous essayons d'utiliser l'application native de l'appareil photo pour permettre à l'utilisateur de prendre une nouvelle photo. Cela fonctionne très bien si nous laissons de côté l'élément EXTRA_OUTPUT extra et renvoie la petite image Bitmap. Cependant, si nous putExtra(EXTRA_OUTPUT,...) sur l'intention avant de le démarrer, tout fonctionne jusqu'à ce que vous essayiez d'appuyer sur le bouton "Ok" dans l'application caméra. Le bouton "Ok" ne fait rien. L'application caméra reste ouverte et rien ne se verrouille. Nous pouvons annuler l'opération, mais le fichier n'est jamais écrit. Que devons-nous faire exactement pour que ACTION_IMAGE_CAPTURE pour écrire la photo prise dans un fichier ?

Edit : Cela se fait via le MediaStore.ACTION_IMAGE_CAPTURE l'intention, juste pour être clair

146voto

yanokwa Points 1004

Il s'agit d'un bogue bien documenté dans certaines versions d'Android, c'est-à-dire sur les versions d'Android de Google Experience, la capture d'image ne fonctionne pas comme indiqué dans la documentation. ce que j'ai généralement utilisé est quelque chose comme ceci dans une classe d'utilitaires.

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

}

ensuite, lorsque je lance la capture d'image, je crée une intention qui vérifie la présence du bogue.

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

ensuite, dans les activités auxquelles je retourne, je fais des choses différentes en fonction de l'appareil.

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

cela vous évite d'avoir à écrire une nouvelle application caméra, mais ce code n'est pas génial non plus. les gros problèmes sont les suivants

  1. vous n'obtenez jamais d'images de taille normale à partir les appareils présentant le bug. Vous obtenez des images de 512px de large qui qui sont insérées dans le fournisseur de contenu d'image. Sur les appareils sans le bogue, tout fonctionne comme dans le document, vous obtenez une grande image normale.

  2. vous devez maintenir la liste. comme écrit, il est possible que des dispositifs soient flashés avec une version de Android (disons de cyanogenmod builds ) qui a le bug corrigé. Si cela arrive, votre code va code se plantera. La solution est d'utiliser l'empreinte l'empreinte du dispositif.

51voto

Donn Felker Points 3501

Je sais que la réponse à cette question a déjà été donnée, mais je sais que beaucoup de personnes sont déconcertées par cette question, alors je vais ajouter un commentaire.

J'ai eu exactement le même problème sur mon Nexus One. Cela venait du fait que le fichier n'existait pas sur le disque avant le démarrage de l'application appareil photo. Par conséquent, je me suis assuré que le fichier existe avant de lancer l'application caméra. Voici un exemple de code que j'ai utilisé :

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

Je crée d'abord un nom de fichier unique (en quelque sorte) en utilisant un hachage MD5 et je le place dans le dossier approprié. Je vérifie ensuite s'il existe (il ne devrait pas exister, mais c'est une bonne pratique de vérifier quand même). S'il n'existe pas, je récupère le répertoire parent (un dossier) et crée la hiérarchie des dossiers jusqu'à lui (donc si les dossiers menant à l'emplacement du fichier n'existent pas, ils existeront après cette ligne). Ensuite, après cela, je crée le fichier. Une fois le fichier créé, je récupère l'Uri et le passe à l'intention, puis le bouton OK fonctionne comme prévu et tout est parfait.

Maintenant, lorsque vous appuyez sur le bouton Ok de l'application caméra, le fichier sera présent à l'emplacement indiqué. Dans cet exemple, ce serait /sdcard/Android/data/com.example.myapp/files/234asdioue23498ad.jpg.

Il n'est pas nécessaire de copier le fichier dans le "onActivityResult" comme indiqué ci-dessus.

36voto

deepwinter Points 1280

Je suis passé par un certain nombre de stratégies de capture de photos, et il semble toujours y avoir un cas, une plate-forme ou certains appareils, où certaines ou toutes les stratégies ci-dessus échoueront de manière inattendue. J'ai pu trouver une stratégie qui utilise le code de génération d'URI ci-dessous qui semble fonctionner dans la plupart des cas, sinon tous.

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

Pour contribuer à la discussion et aider les nouveaux venus, j'ai créé un exemple d'application de test qui montre plusieurs stratégies différentes pour la mise en œuvre de la capture de photos. Les contributions d'autres mises en œuvre sont vivement encouragées pour enrichir la discussion.

https://github.com/deepwinter/AndroidCameraTester

31voto

Yenchi Points 742

J'ai eu le même problème où le bouton OK dans l'application appareil photo ne faisait rien, à la fois sur l'émulateur et sur le nexus one.

Le problème a disparu après avoir spécifié un nom de fichier sûr, sans espaces blancs, sans caractères spéciaux, en MediaStore.EXTRA_OUTPUT En outre, si vous spécifiez un fichier qui réside dans un répertoire qui n'a pas encore été créé, vous devez d'abord le créer. Camera app ne fait pas mkdir pour vous.

28voto

Reto Meier Points 55904

Le flux de travail que vous décrivez devrait fonctionner comme vous l'avez décrit. Il serait utile que vous nous montriez le code entourant la création de l'intention. En général, le modèle suivant devrait vous permettre de faire ce que vous essayez de faire.

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

Notez que c'est le Appareil photo L'activité qui créera et sauvegardera le fichier, et qui ne fait pas partie de votre application, n'aura donc pas le droit d'écrire dans votre dossier d'application. Pour enregistrer un fichier dans votre dossier d'application, créez un fichier temporaire sur la carte SD et déplacez-le vers votre dossier d'application dans le dossier de l'activité. onActivityResult manipulateur.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X