Как программно сделать скриншот на Android? - PullRequest
470 голосов
/ 18 апреля 2010

Как сделать снимок экрана выбранной области экрана телефона не какой-либо программой, а кодом?

Ответы [ 24 ]

416 голосов
/ 13 апреля 2011

Вот код, который позволил сохранить мой скриншот на SD-карте и использовать позже для любых ваших нужд:

Для начала необходимо добавить правильное разрешение для сохранения файла:

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

А это код (запущенный в Activity):

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

И вот как вы можете открыть недавно созданное изображение:

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

Если вы хотите использовать это при просмотре фрагмента, используйте:

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

вместо

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

on takeScreenshot () function

Примечание

Это решение не работает, если ваш диалог содержит вид поверхности. Для получения подробной информации, пожалуйста, проверьте ответ на следующий вопрос:

Снимок экрана Android с поверхности экрана показывает черный экран

117 голосов
/ 19 апреля 2013

Вызовите этот метод, передав внешнюю группу ViewGroup, для которой требуется снимок экрана:

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;
}
42 голосов
/ 04 марта 2013

Примечание: работает только для рутованного телефона

Программно вы можете запустить adb shell /system/bin/screencap -p /sdcard/img.png, как показано ниже

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

затем прочитайте img.png как Bitmap и используйте как хотите.

33 голосов
/ 18 апреля 2010

РЕДАКТИРОВАТЬ: помиловать с downvotes. Это было верно в 2010 году, когда я ответил на вопрос.

Все программы, позволяющие делать скриншоты, работают только на рутованных телефонах.

20 голосов
/ 13 января 2015

Нет корневого разрешения или для большого метода не требуется большая кодировка .


В оболочке adb с помощью приведенной ниже команды вы можете сделать снимок экрана.

input keyevent 120

Эта команда не требует каких-либо прав root, так что вы также можете выполнить из Java-кода приложения Android.

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

Подробнее о коде ключевого события в Android см. http://developer.android.com/reference/android/view/KeyEvent.html

Здесь мы использовали. KEYCODE_SYSRQ его значение равно 120 и используется для клавиши System Request / Print Screen.


Как сказал CJBS, выходное изображение будет сохранено в / sdcard / Pictures / Screenshots

17 голосов
/ 25 июня 2012

Mualig ответ очень хороший, но у меня была та же проблема, что описывает Ewoks, я не понимаю фон. Так что иногда это достаточно хорошо, а иногда я получаю черный текст на черном фоне (в зависимости от темы).

Это решение в значительной степени основано на коде Mualig и коде, который я нашел в Robotium. Я отказываюсь от использования кэша рисования, вызывая непосредственно метод рисования. Перед этим я попытаюсь сделать фон, который можно нарисовать из текущей активности, чтобы нарисовать его первым.

// 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();
}
16 голосов
/ 27 августа 2014
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();
    }
}

Добавить разрешение в манифест

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

Для поддержки Зефир или более поздних версий, пожалуйста, добавьте приведенный ниже код в методе onCreate действия

ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},00);
15 голосов
/ 12 декабря 2012

Для справки, один из способов захвата экрана (а не только активности вашего приложения) - захват кадрового буфера (device / dev / graphics / fb0). Для этого у вас должны быть либо привилегии root, либо ваше приложение должно быть приложением с разрешениями для подписи («Разрешение, которое система предоставляет, только если запрашивающее приложение подписано тем же сертификатом, что и приложение, которое объявило разрешение ") - что очень маловероятно, если вы не скомпилировали свой собственный ROM.

Каждый захват кадров кадрового буфера с пары протестированных мной устройств содержал ровно один снимок экрана. Люди сообщают, что он содержит больше, я думаю, это зависит от размера кадра / дисплея.

Я пытался читать кадровый буфер непрерывно, но, похоже, он возвращает фиксированное количество прочитанных байтов. В моем случае это (3 410 432) байта, что достаточно для хранения кадра дисплея 854 * 480 RGBA (3 279 360 байтов). Да, кадр в двоичном формате, выводимый из fb0, в моем устройстве RGBA . Скорее всего, это будет зависеть от устройства к устройству. Это будет важно для вас, чтобы расшифровать его =)

В моем устройстве разрешения / dev / graphics / fb0 таковы, что только root и пользователи из групповой графики могут читать fb0.

graphics - группа с ограниченным доступом, поэтому вы, вероятно, получите доступ к fb0 только с рутированным телефоном с помощью команды su.

Приложения Android имеют идентификатор пользователя (uid) = приложение _ ## и идентификатор группы (guid) = приложение _ ## .

adb shell имеет uid = shell и guid = shell , который имеет гораздо больше разрешений, чем приложение. Вы можете проверить эти разрешения в /system/permissions/platform.xml

.

Это означает, что вы сможете читать fb0 в оболочке adb без root, но вы не будете читать его в приложении без root.

Кроме того, предоставление разрешений READ_FRAME_BUFFER и / или ACCESS_SURFACE_FLINGER для AndroidManifest.xml ничего не изменит для обычного приложения, поскольку они будут работать только для приложений signature .

Также проверьте это закрытая нить для более подробной информации.

13 голосов
/ 25 сентября 2013

Мое решение:

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

и

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

Изображения сохраняются в папке внешнего хранилища.

11 голосов
/ 07 сентября 2010

Вы можете попробовать следующую библиотеку: http://code.google.com/p/android-screenshot-library/ Библиотека скриншотов Android (ASL) позволяет программно снимать скриншоты с устройств Android без необходимости иметь права root-доступа. Вместо этого ASL использует собственную службу, работающую в фоновом режиме, запускаемую через Android Debug Bridge (ADB) один раз за загрузку устройства.

...