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


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

23 432

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 Take Screenshot of 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 как Bitmap и использовать по вашему желанию.

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

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

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


на 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 / Pictures / Screenshots

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

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

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

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

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

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

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

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

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

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

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

кроме того, давая 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 Screenshot Library (ASL) позволяет программно захватывать скриншоты с устройств Android без необходимости иметь привилегии корневого доступа. Вместо этого 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;
}

параметр качества принимает константу растрового изображения.Конфиг, как правило либо 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

просто расширяя ответ таралоки. Вы должны добавить следующие строки, чтобы заставить его работать. Я сделал имя изображения статическим. Пожалуйста, убедитесь, что вы используете переменную timestamp 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). Вот пример кода Котлина для получения 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
        }
    }
}

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

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