W tym wpisie przedstawiałem, w jaki sposób zrobić zrzuty ekranu aplikacji bez dodatkowych bibliotek. Wspomniałem tam, aby wykonać pełny zrzut ekranu trzeba skorzystać z MediaProjection. W tym artykule właśnie pokaże Ci dokładnie, w jaki sposób to wykonać. Przechwytywanie ekranu za pomocą MediaProjection nie jest zbyt skomplikowane. Za jego pomocą możemy nagrywać obraz, jak i wykonać zdjęcie, zatem zapraszam do lektury 🙂
1. Robienie screenshot’a.
Na początku przyda nam się funkcja, która zmierzy nam wymiary ekranu:
private void getSize(){
DisplayMetrics metrics = activity.getResources().getDisplayMetrics();
screenWidth = metrics.widthPixels;
screenHeight = metrics.heightPixels;
screenDensity = metrics.densityDpi;
}Metoda do pobierania obrazu:
@Override
public void onImageAvailable(ImageReader reader) {
Image image;
image = reader.acquireLatestImage();
if (image == null) return;
final Image.Plane[] planes = image.getPlanes();
final Buffer buffer = planes[0].getBuffer().rewind();
int pixelStride = planes[0].getPixelStride();
int rowStride = planes[0].getRowStride();
int rowPadding = rowStride - pixelStride * screenWidth;
// create bitmap
bitmap = Bitmap.createBitmap(screenWidth+rowPadding/pixelStride, screenHeight, Bitmap.Config.ARGB_8888);
bitmap.copyPixelsFromBuffer(buffer);
if (bitmap != null) {
image.close();
clean();
}
}Główna metoda może wyglądać tak:
public void takescreenshot(){
mMediaProjectionManager = (MediaProjectionManager) activity.getSystemService(Context.MEDIA_PROJECTION_SERVICE);
if (mMediaProjection != null) {
try {
getSize();
mImageReader = ImageReader.newInstance(screenWidth, screenHeight, PixelFormat.RGBA_8888, 2);
mMediaProjection.createVirtualDisplay(
"Screenshot",
screenWidth,
screenHeight,
screenDensity,
DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
mImageReader.getSurface(),
null,
null );
mImageReader.setOnImageAvailableListener( this, null);
} catch (Exception e) {
e.printStackTrace();
}
}
else {
Intent intent = mMediaProjectionManager.createScreenCaptureIntent();
activity.startActivityForResult(intent,REQUEST_CODE_CAPTURE_IMAGE);
}
}Na początku musimy wywołać metodę getSystemService z parametrem MEDIA_PROJECTION_SERVICE, która zapewnia uzyskiwania pozwolenia na przechwytywanie obrazu. Dalej mMediaProjectionManager.createScreenCaptureIntent() jest stosowana, aby uruchomić intencji.

Metoda onActivityResult obsługuje wywołania zwrotne i może wyglądać następująco:
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode != REQUEST_CODE_CAPTURE_IMAGE) {
Log.e(TAG, "Unknown request code: " + requestCode);
return;
}
if (resultCode != Activity.RESULT_OK) {
Log.e(TAG, "Screen Cast Permission Denied" );
return;
}
mMediaProjection = mMediaProjectionManager.getMediaProjection(resultCode, data);
takescreenshot();
}Po udostępnieniu mMediaProjection możliwe jest teraz utworzenie wirtualnego ekranu (złożonego ze wszystkich widocznych powierzchni) i wskazanie Androidowi, na którą powierzchnię chcesz skopiować ekran – w naszym przypadku jest to mImageReader.getSurface(). Do przechwytywania ekranu i zapisaniu do obrazu posłuży nam wywowałanie – mImageReader.setOnImageAvailableListener( this, null); Na koniec metoda czyszcząca:
public void clean(){
if (mMediaProjectionManager != null) {
mMediaProjectionManager = null;
}
if (mMediaProjection != null) {
mMediaProjection.stop();
}
if (mImageReader != null) {
mImageReader.setOnImageAvailableListener(null, null);
mImageReader.close();
}
if (bitmap != null) {
bitmap = null;
}
}2. Nagrywanie ekranu.
Do nagrywania obrazu skorzystamy z MediaRecorder. Metoda główna zmieni się na coś takiego:
public void startRecord(){
mMediaProjectionManager = (MediaProjectionManager) activity.getSystemService(Context.MEDIA_PROJECTION_SERVICE);
if (mMediaProjection != null) {
try {
getSize();
mMediaRecorder = new MediaRecorder();
initRecorder();
mMediaProjection.createVirtualDisplay(
"Recording Screen",
screenWidth,
screenHeight,
screenDensity,
DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
mMediaRecorder.getSurface(),
null,
null);
mMediaRecorder.start();
} catch (Exception e) {
e.printStackTrace();
}
}
else {
Intent intent = mMediaProjectionManager.createScreenCaptureIntent();
activity.startActivityForResult(intent,REQUEST_CODE_CAPTURE_RECORDING);
}
}Kod jest prosty i podobny do wcześniejszego zamiast ImageReader mamy MediaRecoder. Funkcja do zatrzymywania nagrywania może wyglądać tak:
public void stopRecord(){
if (mMediaRecorder != null) {
try{
SystemClock.sleep(500);
mMediaRecorder.stop();
mMediaRecorder.reset();
mMediaRecorder.release();
mMediaRecorder = null;
}catch(RuntimeException e){
e.printStackTrace();
}
}Zastosowałem tutaj celowo SystemClock.sleep(500), ponieważ gdy rozpoczniemy i od razu zatrzymamy nagrywanie, dostaniemy wyjątek. Błąd spowodowany jest tym, że mediarecoder nie otrzymał żadnych informacji o ekranie.
Metoda do zainicjowania mediarecodera:
private void initRecorder() {
try {
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
mMediaRecorder.setOutputFile(getPath(".mp4"));
mMediaRecorder.setVideoSize(screenWidth, screenHeight);
mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
mMediaRecorder.setVideoEncodingBitRate(512 * 1000);
mMediaRecorder.setVideoFrameRate(30);
mMediaRecorder.prepare();
} catch (IOException e) {
e.printStackTrace();
}
}Jeżeli nie chcesz nagrywać dźwięku, możesz usunąć wpisy związane z audio. Dźwięk jest zbierany przez mikrofon, także otoczenie też się zarejestruje, Ostatnia metoda, która pobiera lokalizację do zapisu pliku wideo:
private String getPath(String format ) {
String path = Environment.getExternalStorageDirectory() + "/Recorded Screen/";
File dir = new File(path);
if (!dir.exists()) {
dir.mkdirs();
}
String date = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss", Locale.getDefault()).format(new Date());
file = path + date + format;
return file;
}3. Podsumowanie.
Krótko i na temat. Powyższy kod można też zmienić i nie koniecznie musisz korzystać z tego rozwiązania, które przedstawiłem. Na przykład, zamiast MediaRecorder można zastosować MediaMuxer + MediaCodec, ale uznałem, że powyższy sposób jest prostszy i też skuteczny. Przykładową aplikację można znaleźć tutaj.
