web-dev-qa-db-fra.com

Comment capturer le contenu de l'écran de l'appareil Android?

Duplicata possible:
Comment faire une capture d'écran par programmation sur Android?

Comment capturer le contenu de l'écran de l'appareil Android et créer un fichier image à l'aide des données d'instantané? Quelle API dois-je utiliser ou où puis-je trouver des ressources connexes?

BTW: pas un instantané de la caméra, mais l'écran de l'appareil

49
Jagie

Selon ce lien , il est possible d'utiliser ddms dans le répertoire tools du Android sdk pour faire des captures d'écran.

Pour ce faire dans une application (et non pendant le développement), il existe également des applications pour le faire. Mais comme le souligne @ zed_0xff, cela nécessite certainement root.

13
Joubarc

Utilisez le code suivant:

Bitmap bitmap;
View v1 = MyView.getRootView();
v1.setDrawingCacheEnabled(true);
bitmap = Bitmap.createBitmap(v1.getDrawingCache());
v1.setDrawingCacheEnabled(false);

Ici MyView est le View à travers lequel nous devons inclure à l'écran. Vous pouvez également obtenir DrawingCache de n'importe quel View de cette façon (sans getRootView()).


Il y a aussi une autre façon ..
Si nous avons ScrollView comme vue racine, il vaut mieux utiliser le code suivant,

LayoutInflater inflater = (LayoutInflater) this.getSystemService(LAYOUT_INFLATER_SERVICE);
FrameLayout root = (FrameLayout) inflater.inflate(R.layout.activity_main, null); // activity_main is UI(xml) file we used in our Activity class. FrameLayout is root view of my UI(xml) file.
root.setDrawingCacheEnabled(true);
Bitmap bitmap = getBitmapFromView(this.getWindow().findViewById(R.id.frameLayout)); // here give id of our root layout (here its my FrameLayout's id)
root.setDrawingCacheEnabled(false);

Voici la méthode getBitmapFromView()

public static Bitmap getBitmapFromView(View view) {
        //Define a bitmap with the same size as the view
        Bitmap returnedBitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(),Bitmap.Config.ARGB_8888);
        //Bind a canvas to it
        Canvas canvas = new Canvas(returnedBitmap);
        //Get the view's background
        Drawable bgDrawable =view.getBackground();
        if (bgDrawable!=null) 
            //has background drawable, then draw it on the canvas
            bgDrawable.draw(canvas);
        else 
            //does not have background drawable, then draw white background on the canvas
            canvas.drawColor(Color.WHITE);
        // draw the view on the canvas
        view.draw(canvas);
        //return the bitmap
        return returnedBitmap;
    }

Il affichera tout l'écran, y compris le contenu caché dans votre ScrollView


MISE À JOUR AU 20-04-2016

Il existe une autre meilleure façon de prendre une capture d'écran.
Ici, j'ai pris une capture d'écran de WebView.

WebView w = new WebView(this);
    w.setWebViewClient(new WebViewClient()
    {
        public void onPageFinished(final WebView webView, String url) {

            new Handler().postDelayed(new Runnable(){
                @Override
                public void run() {
                    webView.measure(View.MeasureSpec.makeMeasureSpec(
                                    View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED),
                            View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
                    webView.layout(0, 0, webView.getMeasuredWidth(),
                            webView.getMeasuredHeight());
                    webView.setDrawingCacheEnabled(true);
                    webView.buildDrawingCache();
                    Bitmap bitmap = Bitmap.createBitmap(webView.getMeasuredWidth(),
                            webView.getMeasuredHeight(), Bitmap.Config.ARGB_8888);

                    Canvas canvas = new Canvas(bitmap);
                    Paint paint = new Paint();
                    int height = bitmap.getHeight();
                    canvas.drawBitmap(bitmap, 0, height, Paint);
                    webView.draw(canvas);

                    if (bitmap != null) {
                        try {
                            String filePath = Environment.getExternalStorageDirectory()
                                    .toString();
                            OutputStream out = null;
                            File file = new File(filePath, "/webviewScreenShot.png");
                            out = new FileOutputStream(file);

                            bitmap.compress(Bitmap.CompressFormat.PNG, 50, out);
                            out.flush();
                            out.close();
                            bitmap.recycle();
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
            }, 1000);
        }
    });

J'espère que cela t'aides..!

43
Nirav Dangi

Pour les nouvelles plates-formes Android, on peut exécuter un utilitaire système screencap dans /system/bin pour obtenir la capture d'écran sans autorisation root. Tu peux essayer /system/bin/screencap -h pour voir comment l'utiliser sous adb ou n'importe quel shell.

Soit dit en passant, je pense que cette méthode n'est bonne que pour un instantané unique. Si nous voulons capturer plusieurs images pour les lire à l'écran, ce sera trop lent. Je ne sais pas s'il existe une autre approche pour une capture d'écran plus rapide.

23
Ducky Chen

AFAIK, Toutes les méthodes actuellement utilisées pour capturer une capture d'écran de Android utilisez le framebuffer/dev/graphics/fb0. Cela inclut ddms. Il faut que root lise ce flux. Ddms utilise adbd pour demander les informations, donc root n'est pas requis car adb a les autorisations nécessaires pour demander les données à/dev/graphics/fb0.

Le framebuffer contient plus de 2 "cadres" d'images RGB565. Si vous êtes capable de lire les données, vous devrez connaître la résolution d'écran pour savoir combien d'octets sont nécessaires pour obtenir l'image. chaque pixel fait 2 octets, donc si la résolution de l'écran était de 480x800, vous devriez lire 768 000 octets pour l'image, car une image 480x800 RGB565 a 384 000 pixels.

23
Ryan Conrad

[Basé sur Android code source:]

Côté C++, SurfaceFlinger implémente l'API captureScreen. Ceci est exposé sur le classeur IPC interface, renvoyant à chaque fois une nouvelle zone ashmem qui contient les pixels bruts de l'écran. La capture d'écran réelle est prise via OpenGL.

Pour les clients système C++, l'interface est exposée via la classe ScreenshotClient, définie dans <surfaceflinger_client/SurfaceComposerClient.h> pour Android <4.1; pour Android> 4.1, utilisez <gui/SurfaceComposerClient.h>

Avant JB, pour faire une capture d'écran dans un programme C++, cela suffisait:

ScreenshotClient ssc;
ssc.update();

Avec JB et plusieurs écrans, cela devient un peu plus compliqué:

ssc.update(
    Android::SurfaceComposerClient::getBuiltInDisplay(
        Android::ISurfaceComposer::eDisplayIdMain));

Ensuite, vous pouvez y accéder:

do_something_with_raw_bits(ssc.getPixels(), ssc.getSize(), ...);

En utilisant le code source Android, vous pouvez compiler votre propre bibliothèque partagée pour accéder à cette API, puis l'exposer via JNI à Java. Pour créer une capture d'écran de votre application, l'application doit avoir le READ_FRAME_BUFFER autorisation. Mais même alors, apparemment, vous ne pouvez créer des captures d'écran qu'à partir des applications système, c'est-à-dire celles qui sont signées avec la même clé que le système. (Je ne comprends toujours pas très bien cette partie, car je ne connais pas assez bien le système d'autorisations Android.)

Voici un morceau de code, pour JB 4.1/4.2:

#include <utils/RefBase.h>
#include <binder/IBinder.h>
#include <binder/MemoryHeapBase.h>
#include <gui/ISurfaceComposer.h>
#include <gui/SurfaceComposerClient.h>

static void do_save(const char *filename, const void *buf, size_t size) {
    int out = open(filename, O_RDWR|O_CREAT, 0666);
    int len = write(out, buf, size);
    printf("Wrote %d bytes to out.\n", len);
    close(out);
}

int main(int ac, char **av) {
    Android::ScreenshotClient ssc;
    const void *pixels;
    size_t size;
    int buffer_index;

    if(ssc.update(
        Android::SurfaceComposerClient::getBuiltInDisplay(
            Android::ISurfaceComposer::eDisplayIdMain)) != NO_ERROR ){
        printf("Captured: w=%d, h=%d, format=%d\n");
        ssc.getWidth(), ssc.getHeight(), ssc.getFormat());
        size = ssc.getSize();
        do_save(av[1], pixels, size);
    }
    else
        printf(" screen shot client Captured Failed");
    return 0;
}
18
Pekka Nikander

Vous pouvez essayer la bibliothèque suivante: Android Screenshot Library (ASL) permet de capturer par programme des captures d'écran à partir d'appareils Android sans qu'il soit nécessaire d'avoir des privilèges d'accès root. À la place, ASL utilise un service natif exécuté en arrière-plan, démarré via le Android Debug Bridge (ADB) une fois par démarrage de l'appareil.

15
Kuba

Framebuffer semble être le chemin à parcourir, il ne contiendra pas toujours plus de 2 images comme mentionné par Ryan Conrad. Dans mon cas, il n'en contenait qu'une seule. Je suppose que cela dépend de la taille du cadre/de l'affichage.

J'ai essayé de lire le framebuffer en continu mais il semble revenir pour une quantité fixe d'octets lus. Dans mon cas, cela fait (3 410 432) octets, ce qui est suffisant pour stocker un cadre d'affichage de 854 * 480 RGBA (3 279 360 octets). Oui, le cadre en binaire sorti de fb0 est RGBA dans mon appareil. Cela dépendra probablement d'un appareil à l'autre. Ce sera important pour vous de le décoder =)

Dans mon appareil/dev/graphics/fb0, les autorisations sont telles que niquement root et les utilisateurs de graphiques de groupe peuvent lire le fb0. graphiques est un groupe restreint donc vous n'accéderez probablement à fb0 qu'avec un téléphone rooté en utilisant la commande su.

Les applications Android ont l'ID utilisateur (uid) app _ ## et l'ID groupe (guid) app _ ##.

adb Shell a id Shell et guid Shell, qui a beaucoup plus d'autorisations qu'une application. Vous pouvez réellement vérifier ces autorisations sur / system/permissions/platform.xml

Cela signifie que vous pourrez lire fb0 dans le shell adb sans root mais vous ne le lirez pas dans l'application sans root.

En outre, l'octroi d'autorisations READ_FRAME_BUFFER et/ou ACCESS_SURFACE_FLINGER sur AndroidManifest.xml ne fera rien pour une application standard, car cela ne fonctionnera que pour les applications "signature".

7
Rui Marques

si vous voulez faire une capture d'écran à partir de Java code dans Android app AFAIK vous devez avoir des privilèges root).

5
zed_0xff