web-dev-qa-db-fra.com

Comment faire une capture d'écran par programme?

Comment puis-je prendre une capture d'écran d'une zone sélectionnée de l'écran du téléphone, non pas par un programme mais par un code?

450
korovaisdead

Voici le code qui a permis à ma capture d'écran d'être stockée sur une carte SD et utilisée plus tard, quels que soient vos besoins:

Tout d'abord, vous devez ajouter une autorisation appropriée pour enregistrer le fichier:

<uses-permission Android:name="Android.permission.WRITE_EXTERNAL_STORAGE"/>

Et voici le code (exécuté dans une activité):

private void takeScreenshot() {
    Date now = new Date();
    Android.text.format.DateFormat.format("yyyy-MM-dd_hh:mm:ss", now);

    try {
        // image naming and path  to include sd card  appending name you choose for file
        String mPath = Environment.getExternalStorageDirectory().toString() + "/" + now + ".jpg";

        // create bitmap screen capture
        View v1 = getWindow().getDecorView().getRootView();
        v1.setDrawingCacheEnabled(true);
        Bitmap bitmap = Bitmap.createBitmap(v1.getDrawingCache());
        v1.setDrawingCacheEnabled(false);

        File imageFile = new File(mPath);

        FileOutputStream outputStream = new FileOutputStream(imageFile);
        int quality = 100;
        bitmap.compress(Bitmap.CompressFormat.JPEG, quality, outputStream);
        outputStream.flush();
        outputStream.close();

        openScreenshot(imageFile);
    } catch (Throwable e) {
        // Several error may come out with file handling or DOM
        e.printStackTrace();
    }
}

Et voici comment vous pouvez ouvrir l'image récemment générée:

private void openScreenshot(File imageFile) {
    Intent intent = new Intent();
    intent.setAction(Intent.ACTION_VIEW);
    Uri uri = Uri.fromFile(imageFile);
    intent.setDataAndType(uri, "image/*");
    startActivity(intent);
}

Si vous voulez utiliser ceci sur la vue fragmentée, utilisez:

View v1 = getActivity().getWindow().getDecorView().getRootView();

au lieu de

View v1 = getWindow().getDecorView().getRootView();

sur la fonction takeScreenshot ()

Remarque:

Cette solution ne fonctionne pas si votre boîte de dialogue contient une vue de surface. Pour plus de détails, veuillez vérifier la réponse à la question suivante:

Capture d'écran de Surface View Shows Black Screen

412
taraloca

Appelez cette méthode en passant dans le groupe de vues le plus externe dont vous souhaitez une capture d'écran:

public Bitmap screenShot(View view) {
    Bitmap bitmap = Bitmap.createBitmap(view.getWidth(),
            view.getHeight(), Config.ARGB_8888);
    Canvas canvas = new Canvas(bitmap);
    view.draw(canvas);
    return bitmap;
}
114
JustinMorris

Remarque: fonctionne uniquement pour le téléphone enraciné

Par programme, vous pouvez exécuter adb Shell /system/bin/screencap -p /sdcard/img.png comme ci-dessous

Process sh = Runtime.getRuntime().exec("su", null,null);
OutputStream os = sh.getOutputStream();
os.write(("/system/bin/screencap -p " + "/sdcard/img.png").getBytes("ASCII"));
os.flush();
os.close();
sh.waitFor();    

puis lisez img.png comme Bitmap et utilisez-le comme bon vous semble.

42

EDIT: ayez pitié des votes négatifs. C'était vrai en 2010 quand j'ai répondu à la question.

Tous les programmes qui autorisent les captures d'écran ne fonctionnent que sur les téléphones rootés.

32
GDR

Aucune autorisation root ou no big coding n'est requis pour cette méthode.


Sur adb Shell, utilisez la commande ci-dessous pour prendre une capture d’écran.

input keyevent 120

Cette commande ne nécessite aucune autorisation root, vous pouvez donc également exécuter à partir du code Java de l'application Android.

Process process;
process = Runtime.getRuntime().exec("input keyevent 120");

Pour en savoir plus sur le code Keyevent sous Android, consultez http://developer.Android.com/reference/Android/view/KeyEvent.html

Ici nous avons utilisé. KEYCODE_SYSRQ sa valeur est 120 et est utilisé pour la touche demande système/impression d'écran. 


Comme l'a dit CJBS, l'image de sortie sera enregistrée dans / sdcard/Pictures/Screenshots

19
Jeegar Patel

La réponse de Mualig est très bonne, mais j'avais le même problème que décrit Ewoks, je ne comprends pas le fond. Donc, parfois, cela suffit et parfois, le texte est noir sur fond noir (selon le thème).

Cette solution est fortement basée sur le code Mualig et le code que j'ai trouvé dans Robotium. Je rejette l'utilisation du cache de dessin en appelant directement à la méthode de dessin. Avant cela, je vais essayer de dessiner le fond de l'activité actuelle pour le dessiner en premier.

// Some constants
final static String SCREENSHOTS_LOCATIONS = Environment.getExternalStorageDirectory().toString() + "/screenshots/";

// Get device dimmensions
Display display = getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);

// Get root view
View view = mCurrentUrlMask.getRootView();

// Create the bitmap to use to draw the screenshot
final Bitmap bitmap = Bitmap.createBitmap(size.x, size.y, Bitmap.Config.ARGB_4444);
final Canvas canvas = new Canvas(bitmap);

// Get current theme to know which background to use
final Activity activity = getCurrentActivity();
final Theme theme = activity.getTheme();
final TypedArray ta = theme
    .obtainStyledAttributes(new int[] { Android.R.attr.windowBackground });
final int res = ta.getResourceId(0, 0);
final Drawable background = activity.getResources().getDrawable(res);

// Draw background
background.draw(canvas);

// Draw views
view.draw(canvas);

// Save the screenshot to the file system
FileOutputStream fos = null;
try {
    final File sddir = new File(SCREENSHOTS_LOCATIONS);
    if (!sddir.exists()) {
        sddir.mkdirs();
    }
    fos = new FileOutputStream(SCREENSHOTS_LOCATIONS
            + System.currentTimeMillis() + ".jpg");
    if (fos != null) {
        if (!bitmap.compress(Bitmap.CompressFormat.JPEG, 90, fos)) {
            Log.d(LOGTAG, "Compress/Write failed");
        }
        fos.flush();
        fos.close();
    }

} catch (FileNotFoundException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
} catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}
17
xamar
private void captureScreen() {
    View v = getWindow().getDecorView().getRootView();
    v.setDrawingCacheEnabled(true);
    Bitmap bmp = Bitmap.createBitmap(v.getDrawingCache());
    v.setDrawingCacheEnabled(false);
    try {
        FileOutputStream fos = new FileOutputStream(new File(Environment
                .getExternalStorageDirectory().toString(), "SCREEN"
                + System.currentTimeMillis() + ".png"));
        bmp.compress(CompressFormat.PNG, 100, fos);
        fos.flush();
        fos.close();
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

Ajouter la permission dans le manifeste

<uses-permission Android:name="Android.permission.WRITE_EXTERNAL_STORAGE"/>

Pour Supporting Marshmallow ou versions supérieures, veuillez ajouter le code ci-dessous dans la méthode activity onCreate

ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},00);
16
Crazy Coder

À titre de référence, un moyen de capturer l'écran (et pas uniquement l'activité de votre application) consiste à capturer le framebuffer (périphérique/dev/graphics/fb0). Pour ce faire, vous devez avoir des privilèges root ou votre application doit être une application avec signature des autorisations ("Une autorisation accordée par le système uniquement si l'application demandeuse est signée avec le même certificat que l'application ayant déclaré l'autorisation". ) - ce qui est très peu probable si vous n'avez pas compilé votre propre ROM.

Chaque capture de framebuffer, à partir de quelques périphériques que j'ai testés, contenait exactement une capture d'écran. Les gens ont signalé qu'il en contenait plus, je suppose que cela dépend de la taille du cadre/de l'affichage.

J'ai essayé de lire le framebuffer continuellement mais il semble revenir pour un nombre fixe d'octets lus. Dans mon cas, il s’agit de (3 410 432) octets, ce qui est suffisant pour stocker une trame 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 périphérique à l'autre. Ce sera important pour vous de le décoder =)

Dans mon appareil, / dev/graphics/fb0, les autorisations permettent uniquement aux utilisateurs root et aux utilisateurs du groupe de graphiques de lire le fichier fb0. 

graphics est un groupe restreint, vous ne pourrez donc probablement accéder à fb0 qu'avec un téléphone enraciné à l'aide de la commande su.

Les applications Android ont les identifiants utilisateur (uid) = app _ ## et identifiant du groupe (guid) = app _ ##.

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

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

En outre, le fait d'autoriser les autorisations READ_FRAME_BUFFER et/ou ACCESS_SURFACE_FLINGER sur AndroidManifest.xml ne fera rien pour une application standard, car celles-ci ne fonctionneront que pour les applications 'signature'.

Vérifiez également ce fil fermé pour plus de détails.

14
Rui Marques

Ma solution est:

public static Bitmap loadBitmapFromView(Context context, View v) {
    DisplayMetrics dm = context.getResources().getDisplayMetrics(); 
    v.measure(MeasureSpec.makeMeasureSpec(dm.widthPixels, MeasureSpec.EXACTLY),
            MeasureSpec.makeMeasureSpec(dm.heightPixels, MeasureSpec.EXACTLY));
    v.layout(0, 0, v.getMeasuredWidth(), v.getMeasuredHeight());
    Bitmap returnedBitmap = Bitmap.createBitmap(v.getMeasuredWidth(),
            v.getMeasuredHeight(), Bitmap.Config.ARGB_8888);
    Canvas c = new Canvas(returnedBitmap);
    v.draw(c);

    return returnedBitmap;
}

et

public void takeScreen() {
    Bitmap bitmap = ImageUtils.loadBitmapFromView(this, view); //get Bitmap from the view
    String mPath = Environment.getExternalStorageDirectory() + File.separator + "screen_" + System.currentTimeMillis() + ".jpeg";
    File imageFile = new File(mPath);
    OutputStream fout = null;
    try {
        fout = new FileOutputStream(imageFile);
        bitmap.compress(Bitmap.CompressFormat.JPEG, 90, fout);
        fout.flush();
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        fout.close();
    }
}

Les images sont enregistrées dans le dossier de stockage externe.

13
validcat

Vous pouvez essayer la bibliothèque suivante: http://code.google.com/p/Android-screenshot-library/ Android La bibliothèque de captures d'écran (ASL) permet de capturer par programme des captures d'écran depuis Android appareils sans qu'il soit nécessaire d'avoir les privilèges d'accès root. Au lieu de cela, ASL utilise un service natif exécuté en arrière-plan, démarré via le pont de débogage Android une fois par démarrage du périphérique.

11
Kuba
public class ScreenShotActivity extends Activity{

private RelativeLayout relativeLayout;
private Bitmap myBitmap;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    relativeLayout = (RelativeLayout)findViewById(R.id.relative1);
    relativeLayout.post(new Runnable() {
        public void run() {

            //take screenshot
            myBitmap = captureScreen(relativeLayout);

            Toast.makeText(getApplicationContext(), "Screenshot captured..!", Toast.LENGTH_LONG).show();

            try {
                if(myBitmap!=null){
                    //save image to SD card
                    saveImage(myBitmap);
                }
                Toast.makeText(getApplicationContext(), "Screenshot saved..!", Toast.LENGTH_LONG).show();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

        }
    });

}

public static Bitmap captureScreen(View v) {

    Bitmap screenshot = null;
    try {

        if(v!=null) {

            screenshot = Bitmap.createBitmap(v.getMeasuredWidth(),v.getMeasuredHeight(), Config.ARGB_8888);
            Canvas canvas = new Canvas(screenshot);
            v.draw(canvas);
        }

    }catch (Exception e){
        Log.d("ScreenShotActivity", "Failed to capture screenshot because:" + e.getMessage());
    }

    return screenshot;
}

public static void saveImage(Bitmap bitmap) throws IOException{

    ByteArrayOutputStream bytes = new ByteArrayOutputStream();
    bitmap.compress(Bitmap.CompressFormat.PNG, 40, bytes);
    File f = new File(Environment.getExternalStorageDirectory() + File.separator + "test.png");
    f.createNewFile();
    FileOutputStream fo = new FileOutputStream(f);
    fo.write(bytes.toByteArray());
    fo.close();
}

}

AJOUTER UNE PERMISSION

<uses-permission Android:name="Android.permission.WRITE_EXTERNAL_STORAGE"/>
8
Vaishali Sutariya

Basé sur la réponse de @JustinMorris ci-dessus et de @NiravDangi ici https://stackoverflow.com/a/8504958/2232148 nous devons prendre l'arrière-plan et le premier plan d'une vue et les assembler comme ceci:

public static Bitmap takeScreenshot(View view, Bitmap.Config quality) {
    Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(), quality);
    Canvas canvas = new Canvas(bitmap);

    Drawable backgroundDrawable = view.getBackground();
    if (backgroundDrawable != null) {
        backgroundDrawable.draw(canvas);
    } else {
        canvas.drawColor(Color.WHITE);
    }
    view.draw(canvas);

    return bitmap;
}

Le paramètre quality prend une constante de Bitmap.Config, généralement Bitmap.Config.RGB_565 ou Bitmap.Config.ARGB_8888.

7
Oliver Hausler

Pour ceux qui veulent capturer un GLSurfaceView, la méthode getDrawingCache ou Drawing to canvas ne fonctionnera pas.

Vous devez lire le contenu du framebuffer OpenGL après le rendu du frame. Il y a une bonne réponse ici

6
rockeye

Vous pouvez essayer de faire quelque chose comme ça,

Obtenir un cache bitmap à partir d'une mise en page ou d'une vue en effectuant une opération telle que Tout d'abord, vous devez attribuer setDrawingCacheEnabled à une mise en page (un affichage linéaire ou relatif, ou une vue)

puis 

Bitmap bm = layout.getDrawingCache()

Ensuite, vous faites ce que vous voulez avec le bitmap. Soit le transformer en un fichier image, soit envoyer l'URI du bitmap ailleurs.

6
Kevin Tan

Chemin court est 

FrameLayout layDraw = (FrameLayout) findViewById(R.id.layDraw); /*Your root view to be part of screenshot*/
layDraw.buildDrawingCache();
Bitmap bmp = layDraw.getDrawingCache();
6
Chintan Khetiya

J'ai créé une bibliothèque simple qui prend une capture d'écran d'une View et vous donne un objet Bitmap ou l'enregistre directement dans le chemin que vous souhaitez

https://github.com/abdallahalaraby/Blink

5
Abdallah Alaraby

Si vous voulez prendre une capture d'écran de fragment, suivez ceci:

  1. Remplacer onCreateView()

             @Override
            public View onCreateView(LayoutInflater inflater, ViewGroup container,
                                     Bundle savedInstanceState) {
                // Inflate the layout for this fragment
                View view = inflater.inflate(R.layout.fragment_one, container, false);
                mView = view;
            }
    
  2. Logique pour prendre une capture d'écran:

     button.setOnClickListener(new View.OnClickListener() {
     @Override
     public void onClick(View v) {
         View view =  mView.findViewById(R.id.scrollView1);
          shareScreenShotM(view, (NestedScrollView) view); 
     }
    
  3. méthode shareScreenShotM)():

    public void shareScreenShotM(View view, NestedScrollView scrollView){
    
         bm = takeScreenShot(view,scrollView);  //method to take screenshot
        File file = savePic(bm);  // method to save screenshot in phone.
        }
    
  4. méthode takeScreenShot ():

             public Bitmap takeScreenShot(View u, NestedScrollView z){
    
                u.setDrawingCacheEnabled(true);
                int totalHeight = z.getChildAt(0).getHeight();
                int totalWidth = z.getChildAt(0).getWidth();
    
                Log.d("yoheight",""+ totalHeight);
                Log.d("yowidth",""+ totalWidth);
                u.layout(0, 0, totalWidth, totalHeight);
                u.buildDrawingCache();
                Bitmap b = Bitmap.createBitmap(u.getDrawingCache());
                u.setDrawingCacheEnabled(false);
                u.destroyDrawingCache();
                 return b;
            }
    
  5. méthode savePic ():

     public static File savePic(Bitmap bm){
    
            ByteArrayOutputStream bytes = new ByteArrayOutputStream();
            bm.compress(Bitmap.CompressFormat.JPEG, 100, bytes);
             File sdCardDirectory =  new File(Environment.getExternalStorageDirectory() + "/Foldername");
    
           if (!sdCardDirectory.exists()) {
                sdCardDirectory.mkdirs();
          }
           //  File file = new File(dir, fileName);
          try {
             file = new File(sdCardDirectory, Calendar.getInstance()
                .getTimeInMillis() + ".jpg");
            file.createNewFile();
            new FileOutputStream(file).write(bytes.toByteArray());
            Log.d("Fabsolute", "File Saved::--->" + file.getAbsolutePath());
             Log.d("Sabsolute", "File Saved::--->" + sdCardDirectory.getAbsolutePath());
         } catch (IOException e) {
              e.printStackTrace();
          }
         return file;
       }
    

Pour l'activité, vous pouvez simplement utiliser View v1 = getWindow().getDecorView().getRootView(); au lieu de mView

4
Parsania Hardik

Étendre juste la réponse de Taraloca. Vous devez ajouter les lignes suivantes pour que cela fonctionne. J'ai rendu le nom de l'image statique. Assurez-vous d'utiliser la variable timestamp de taraloca si vous avez besoin d'un nom d'image dynamique.

    // Storage Permissions
private static final int REQUEST_EXTERNAL_STORAGE = 1;
private static String[] PERMISSIONS_STORAGE = {
        Manifest.permission.READ_EXTERNAL_STORAGE,
        Manifest.permission.WRITE_EXTERNAL_STORAGE
};

private void verifyStoragePermissions() {
    // Check if we have write permission
    int permission = ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE);

    if (permission != PackageManager.PERMISSION_GRANTED) {
        // We don't have permission so Prompt the user
        ActivityCompat.requestPermissions(this, PERMISSIONS_STORAGE, REQUEST_EXTERNAL_STORAGE);
    }else{
        takeScreenshot();
    }
}

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);

    if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
        if (requestCode == REQUEST_EXTERNAL_STORAGE) {
            takeScreenshot();
        }
    }
}

Et dans le fichier AndroidManifest.xml, les entrées suivantes sont obligatoires:

<uses-permission Android:name="Android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission Android:name="Android.permission.WRITE_EXTERNAL_STORAGE" />
4
TechBee

Avec la capture d'écran, si nous voulons jouer le ton aussi.Nous pouvons utiliser le code suivant

    MediaPlayer _shootMP = null;
    AudioManager manager = (AudioManager) 
    getActivity().getSystemService(Context.AUDIO_SERVICE);
    manager.setStreamVolume(AudioManager.STREAM_NOTIFICATION, 
    manager.getStreamMaxVolume(AudioManager.STREAM_MUSIC), 0);
    if (_shootMP == null)
        _shootMP = MediaPlayer
                .create(getActivity(),
                        Uri.parse("file:///system/media/audio/ui/camera_click.ogg"));
    if (_shootMP != null) {
        try {

            _shootMP.start();
            _shootMP.setOnCompletionListener(new OnCompletionListener() {
                @Override
                public void onCompletion(MediaPlayer arg0) {
                    // release the media
                    _shootMP.stop();
                    _shootMP.reset();
                    _shootMP.release();
                    _shootMP = null;

                }
            });
        } catch (IllegalStateException e) {
            Log.w(TAG, "Exception takeScreenShot" + e.getMessage());

        }
    }
3
Shinoo Goyal

La vue de paramètre est l'objet de mise en page racine. 

public static Bitmap screenShot(View view) {
                    Bitmap bitmap = null;
                    if (view.getWidth() > 0 && view.getHeight() > 0) {
                        bitmap = Bitmap.createBitmap(view.getWidth(),
                                view.getHeight(), Bitmap.Config.ARGB_8888);
                        Canvas canvas = new Canvas(bitmap);
                        view.draw(canvas);
                    }
                    return bitmap;
                }
2
Anil Singhania

Pour les applications système uniquement!

Process process;
process = Runtime.getRuntime().exec("screencap -p " + outputPath);
process.waitFor();

Remarque: Les applications système n'ont pas besoin de lancer "su" pour exécuter cette commande.

1
GilCol

Prendre une capture d'écran d'une vue sous Android.

public static Bitmap getViewBitmap(View v) {
    v.clearFocus();
    v.setPressed(false);

    boolean willNotCache = v.willNotCacheDrawing();
    v.setWillNotCacheDrawing(false);

    int color = v.getDrawingCacheBackgroundColor();
    v.setDrawingCacheBackgroundColor(0);

    if (color != 0) {
        v.destroyDrawingCache();
    }
    v.buildDrawingCache();
    Bitmap cacheBitmap = v.getDrawingCache();
    if (cacheBitmap == null) {
        return null;
    }

    Bitmap bitmap = Bitmap.createBitmap(cacheBitmap);

    v.destroyDrawingCache();
    v.setWillNotCacheDrawing(willNotCache);
    v.setDrawingCacheBackgroundColor(color);

    return bitmap;
}
0
Mohit Singh

La plupart des réponses à cette question utilisent la méthode de dessin Canvas ou la méthode du cache de dessin. Cependant, la méthode View.setDrawingCache() est obsolète dans l'API 28 . Actuellement, l'API recommandée pour réaliser des captures d'écran est la classe PixelCopy disponible à partir de l'API 24 (mais les méthodes acceptant le paramètre Window sont disponibles à partir de l'API 26 == Android 8.0 Oreo). Voici un exemple de code Kotlin permettant de récupérer une Bitmap:

@RequiresApi(Build.VERSION_CODES.O)
fun saveScreenshot(view: View) {
    val window = (view.context as Activity).window
    if (window != null) {
        val bitmap = Bitmap.createBitmap(view.width, view.height, Bitmap.Config.ARGB_8888)
        val locationOfViewInWindow = IntArray(2)
        view.getLocationInWindow(locationOfViewInWindow)
        try {
            PixelCopy.request(window, Rect(locationOfViewInWindow[0], locationOfViewInWindow[1], locationOfViewInWindow[0] + view.width, locationOfViewInWindow[1] + view.height), bitmap, { copyResult ->
                if (copyResult == PixelCopy.SUCCESS) {
                    saveBitmap(bitmap)
                }
                // possible to handle other result codes ...
            }, Handler())
        } catch (e: IllegalArgumentException) {
            // PixelCopy may throw IllegalArgumentException, make sure to handle it
        }
    }
}
0