Как программно сделать скриншот?

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

23 ответов


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

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

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

и это код (работает в действии):

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() функции

Примечание:

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

Android сделать снимок экрана Surface View показывает черный экран


вызовите этот метод, передавая во внешнюю самую 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;
}

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

программно, вы можете запустить 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 as Bitmap и использовать по вашему желанию.


EDIT: сжальтесь над downvotes. Это было правдой в 2010 году, когда я ответил на вопрос.

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


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


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

input keyevent 120

эта команда не требует каких-либо корневых разрешений, так же вы можете выполнить из java-кода приложения android также.

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

подробнее о коде keyevent в android см. http://developer.android.com/reference/android/view/KeyEvent.html

здесь мы использовали. KEYCODE_SYSRQ свое значение 120 и использовано для ключа экрана запроса / печати системы.


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


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

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

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

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

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

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

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

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

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

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

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

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

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

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


мое решение:

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

изображения сохраняются во внешней папке хранения.


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


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

}

ДОБАВИТЬ РАЗРЕШЕНИЕ

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

для тех, кто хочет захватить GLSurfaceView, метод getDrawingCache или drawing to canvas не будет работать.

вы должны прочитать содержимое фреймбуфера OpenGL после того, как кадр был визуализирован. Есть хороший ответ здесь


на основе ответа @JustinMorris выше и @NiravDangi здесь https://stackoverflow.com/a/8504958/2232148 мы должны взять фон и передний план вида и собрать их следующим образом:

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

параметр quality принимает константу растрового изображения.Конфигурация, как правило, либо Bitmap.Config.RGB_565 или Bitmap.Config.ARGB_8888.


вы можете попробовать сделать что-то вроде этого,

получение растрового кэша из макета или вид путем? Сначала вы должны setDrawingCacheEnabled в макет (linearlayout или relativelayout, или представление)

затем

Bitmap bm = layout.getDrawingCache()

затем вы делаете все, что хотите с растровым изображением. Либо превратить его в файл изображения, либо отправить uri растрового изображения в другое место.


Я создал простую библиотеку, которая берет скриншот из View и либо дает вам растровый объект, либо сохраняет его непосредственно на любой путь, который вы хотите

https://github.com/abdallahalaraby/Blink


короткий путь-это

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

если вы хотите сделать скриншот из fragment чем следовать этой:

  1. переопределить 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. логика для снятия скриншота:

     button.setOnClickListener(new View.OnClickListener() {
     @Override
     public void onClick(View v) {
         View view =  mView.findViewById(R.id.scrollView1);
          shareScreenShotM(view, (NestedScrollView) view); 
     }
    
  3. метод 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. способ 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. метод 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;
       }
    

для активности, вы можете просто использовать View v1 = getWindow().getDecorView().getRootView(); вместо mView


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

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

и в AndroidManifest.xml-файл следующие записи должны:

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

вместе с захватом скриншота, если мы хотим также воспроизвести тон.Мы можем использовать следующий код

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

        }
    }

только для системных приложений!

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

Примечание: системные приложения не должны запускать " su " для выполнения этой команды.


представление параметров является корневым объектом макета.

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

большинство ответов на этот вопрос используйте тег Canvas метод рисования или метод кэша рисования. Однако View.setDrawingCache() метод устарел в API 28. В настоящее время рекомендуемым API для создания скриншотов является PixelCopy класс доступен из API 24 (но методы, которые принимают Window параметр доступен из API 26 = = Android 8.0 Oreo). Вот пример кода Kotlin для получения 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
        }
    }
}

Если вы хотите захватить представление или макет, например RelativeLayout или LinearLayout и т. д. просто используйте код :

LinearLayout llMain = (LinearLayout) findViewById(R.id.linearlayoutMain);
Bitmap bm = loadBitmapFromView(llMain);

теперь вы можете сохранить растровое изображение на устройства :

FileOutputStream outStream = null;
File f=new File(Environment.getExternalStorageDirectory()+"/Screen Shots/");
f.mkdir();
String extStorageDirectory = f.toString();
File file = new File(extStorageDirectory, "my new screen shot");
pathOfImage = file.getAbsolutePath();
try {
    outStream = new FileOutputStream(file);
    bm.compress(Bitmap.CompressFormat.PNG, 100, outStream);
    Toast.makeText(getApplicationContext(), "Saved at "+f.getAbsolutePath(), Toast.LENGTH_LONG).show();
    addImageGallery(file);
    //mail.setEnabled(true);
    flag=true;
} catch (FileNotFoundException e) {e.printStackTrace();}
try {
    outStream.flush();
    outStream.close();
} catch (IOException e) {e.printStackTrace();}