1. Prérequis

Voici les prérequis pour ce tutoriel :

  • un nouveau projet tout beau tout propre sous Android 2.2 ;
  • un téléphone ou une tablette.

2. Permissions

Nous devons ajouter des droits pour pouvoir utiliser la caméra :

 
Sélectionnez
<uses-permission android:name="android.permission.CAMERA"></uses-permission>
<uses-feature android:name="android.hardware.camera"/>
<uses-feature android:name="android.hardware.camera.autofocus"/>

3. Orientation de l'activité

Il est tout de même plus facile de filmer ou prendre une photo en mode paysage.

 
Sélectionnez
android:screenOrientation="landscape"

Contenu global de notre fichier Manifest.xml

 
Sélectionnez
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.aceart.FormationCamera"
     android:versionCode="1"
     android:versionName="1.0" android:installLocation="auto">
   <uses-sdk android:minSdkVersion="10" />
   <uses-permission android:name="android.permission.CAMERA"></uses-permission>
   <uses-feature android:name="android.hardware.camera"/>
   <uses-feature android:name="android.hardware.camera.autofocus"/>

   <application android:icon="@drawable/icon" android:label="@string/app_name">
       <activity android:name=".FormationCameraActivity"
                 android:label="@string/app_name" android:screenOrientation="landscape">
           <intent-filter>
               <action android:name="android.intent.action.MAIN" />
               <category android:name="android.intent.category.LAUNCHER" />
           </intent-filter>
       </activity>

   </application>
</manifest>

4. Commençons à coder

Tout d'abord, nous allons avoir besoin d'une surface pour prévisualiser l'image que nous renvoie l'appareil photo. Il faut ajouter une SurfaceView à notre layout, comme ceci :

 
Sélectionnez
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:orientation="vertical"
   android:layout_width="fill_parent"
   android:layout_height="fill_parent"
   >
   <SurfaceView android:id="@+id/surfaceViewCamera" android:layout_width="fill_parent" android:layout_height="fill_parent"></SurfaceView>
</LinearLayout>

Ensuite, nous allons implémenter une interface nommée SurfaceHolder.Callback pour permettre à notre activité d'avoir les retours sur notre SurfaceView et nous permettre d'afficher en temps réel notre prévisualisation.

 
Sélectionnez
public class FormationCameraActivity extends Activity implements SurfaceHolder.Callback

Une fois l'interface implémentée et à l'aide d'Eclipse nous devons avoir trois fonctions supplémentaires.

 
Sélectionnez
public void surfaceChanged(SurfaceHolder holder, int format, int width,int height)
public void surfaceCreated(SurfaceHolder holder)
public void surfaceDestroyed(SurfaceHolder holder)

Créons maintenant trois variables locales dans notre activité.

 
Sélectionnez
private Camera camera;
private SurfaceView surfaceCamera;
private Boolean isPreview;

La première contiendra notre Camera, la seconde notre surface pour la prévisualisation et, pour terminer, connaître l'état de la prévisualisation.

Dirigeons nous vers méthode onCreate où nous allons ajouter de nouvelles fonctionnalités.

 
Sélectionnez
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // Nous mettons l'application en plein écran et sans barre de titre
    getWindow().setFormat(PixelFormat.TRANSLUCENT);
    requestWindowFeature(Window.FEATURE_NO_TITLE);
    getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
            WindowManager.LayoutParams.FLAG_FULLSCREEN);

    isPreview = false;

    // Nous appliquons notre layout
    setContentView(R.layout.main);

    // Nous récupérons notre surface pour le preview
    surfaceCamera = (SurfaceView) findViewById(R.id.surfaceViewCamera);

    // Méthode d'initialisation de la caméra
    InitializeCamera();
}

Nous commençons par définir les propriétés de notre activité, sans barre de titre et en plein écran.

Nous initialisons notre prévisualisation à faux et nous récupérons notre SurfaceView.

Pour terminer, nous appelons notre méthode pour initialiser notre caméra.

 
Sélectionnez
public void InitializeCamera() {
// Nous attachons nos retours du holder à notre activité
surfaceCamera.getHolder().addCallback(this);
// Nous spécifiions le type du holder en mode SURFACE_TYPE_PUSH_BUFFERS
surfaceCamera.getHolder().setType(
SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}

Nous récupérons le holder de notre surface et nous attachons son callback à l'activité, c'est d'ailleurs pour ça que nous avons implémenté notre interface.

Nous définissons ensuite le type du holder, si vous l'oubliez vous aurez droit à un magnifique crash de votre application.

Définissons maintenant nos méthodes implémentées par l'interface.

 
Sélectionnez
public void surfaceDestroyed(SurfaceHolder holder) {
    // Nous arrêtons la camera et nous rendons la main
    if (camera != null) {
        camera.stopPreview();
        isPreview = false;
        camera.release();
    }
}

Quand la surface est détruite, nous arrêtons la prévisualisation, nous mettons notre jeton isPreview à « false » puis nous libérons le périphérique (c'est important).

 
Sélectionnez
public void surfaceCreated(SurfaceHolder holder) {
    // Nous prenons le contrôle de la camera
    if (camera == null)
        camera = Camera.open();
}

Quand la surface est créée nous ouvrons le périphérique pour pouvoir capturer.

 
Sélectionnez
// Quand la surface change
public void surfaceChanged(SurfaceHolder holder, int format, int width,
        int height) {

    // Si le mode preview est lancé alors nous le stoppons
    if (isPreview) {
        camera.stopPreview();
    }
    // Nous récupérons les paramètres de la caméra
    Camera.Parameters parameters = camera.getParameters();

    // Nous changeons la taille
    parameters.setPreviewSize(width, height);

    // Nous appliquons nos nouveaux paramètres
    camera.setParameters(parameters);

    try {
        // Nous attachons notre prévisualisation de la caméra au holder de la
        // surface
        camera.setPreviewDisplay(surfaceCamera.getHolder());
    } catch (IOException e) {
    }

    // Nous lançons la preview
    camera.startPreview();

    isPreview = true;
}
  • Nous commençons par vérifier si nous sommes en train de capturer la prévisualisation, si oui nous stoppons le processus.
  • Nous récupérons les paramètres actuels de notre objet Camera, ensuite nous lui attribuons la nouvelle taille de la surface pour projeter notre prévisualisation.
  • Nous initialisons avec les nouveaux paramètres.
  • Nous indiquons à notre caméra qu'il faut rediriger la prévisualisation sur notre surface.
  • Pour finir nous lançons la prévisualisation.
  • Nous mettons notre jeton à « true ».

Pour terminer n'oublions pas ces deux parties :

 
Sélectionnez
// Retour sur l'application
@Override
public void onResume() {
    super.onResume();
    camera = Camera.open();
}

// Mise en pause de l'application
@Override
public void onPause() {
    super.onPause();

    if (camera != null) {
        camera.release();
        camera = null;
    }
}

Elles vont vous permettre de gérer la mise en arrière-plan et le retour au premier plan de l'application, en libérant la caméra de votre emprise (bande de tyrans).

Pour terminer notre petit tutoriel nous allons voir comment enregistrer une image (c'est le but en fin de compte).

5. Prendre une photo

Nous allons rajouter un peu de code dans notre méthode onCreate, pour que nous puissions prendre une photo lorsque nous appuyons sur l'écran.

 
Sélectionnez
// Quand nous cliquons sur notre surface
surfaceCamera.setOnClickListener(new OnClickListener() {

    public void onClick(View v) {
        // Nous prenons une photo
        if (camera != null) {
            SavePicture();
        }

    }
});

Nous attachons l'événement onClick de notre SurfaceView à un listener.

Si l'événement est levé nous appelons notre méthode SavePicture.

 
Sélectionnez
private void SavePicture() {
    try {
        SimpleDateFormat timeStampFormat = new SimpleDateFormat(
                "yyyy-MM-dd-HH.mm.ss");
        String fileName = "photo_" + timeStampFormat.format(new Date())
                + ".jpg";

        // Metadata pour la photo
        ContentValues values = new ContentValues();
        values.put(Media.TITLE, fileName);
        values.put(Media.DISPLAY_NAME, fileName);
        values.put(Media.DESCRIPTION, "Image prise par FormationCamera");
        values.put(Media.DATE_TAKEN, new Date().getTime());
        values.put(Media.MIME_TYPE, "image/jpeg");

        // Support de stockage
        Uri taken = getContentResolver().insert(Media.EXTERNAL_CONTENT_URI,
                values);

        // Ouverture du flux pour la sauvegarde
        stream = (FileOutputStream) getContentResolver().openOutputStream(
                taken);

        camera.takePicture(null, pictureCallback, pictureCallback);
    } catch (Exception e) {
        // TODO: handle exception
    }

}
  • Nous commençons par définir un format de nommage pour nos photos.
  • Puis nous définissons toutes les propriétés pour les mettre dans la base de données des photos.
  • Nous récupérons le chemin d'insertion.
  • Nous ouvrons le flux d'écriture.
  • Nous lançons le camera.takePicture pour prendre une photo.

Pour finir, il faut implémenter le pictureCallback :

 
Sélectionnez
// Callback pour la prise de photo
Camera.PictureCallback pictureCallback = new Camera.PictureCallback() {

    public void onPictureTaken(byte[] data, Camera camera) {
        if (data != null) {
            // Enregistrement de votre image
            try {
                if (stream != null) {
                    stream.write(data);
                    stream.flush();
                    stream.close();
                }
            } catch (Exception e) {
                // TODO: handle exception
            }

            // Nous redémarrons la prévisualisation
            camera.startPreview();
        }
    }
};
  • Nous enregistrons nos données dans le stream.
  • Nous écrivons en dur sur la sdcard.
  • Nous fermons le flux.
  • Nous relançons la prévisualisation.

Et pour terminer :

 
Sélectionnez
<uses-permission android:name="android.permision.WRITE_EXTERNAL_STORAGE"></uses-permission>

6. Conclusion

Si jamais vous en avez besoin.

Voilà désormais nous allons chercher notre photo dans la galerie et nous regardons son détail :

Et voilà tout est là !

Image non disponible

J'espère que ce tutoriel vous aura plu.

7. Annexes

8. Remerciements

Je tiens à remercier tout particulièrement Feanorin qui a mis ce tutoriel au format Developpez.com. 
Merci également à _Max_ d'avoir pris le temps de le relire et de le corriger.

9. Liens