最近两天研究了一下安卓截屏和录屏功能的实现,基本的思路如下:
截屏:通过View绘制缓冲获得Bitmap,然后写到文件中,完成截屏的功能;
录屏:通过MediaRecorder进行video record,基本过程如下:
MediaRecorder recorder = new MediaRecorder(); recorder.setAudioSource(MediaRecorder.AudioSource.MIC); recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); recorder.setOutputFile(PATH_NAME); recorder.prepare(); recorder.start(); // Recording is now started ... recorder.stop(); recorder.reset(); // You can reuse the object by going back to setAudioSource() step recorder.release(); // Now the object cannot be reused
要是想实现录屏功能,首先需要向系统申请权限,然后得到系统的反馈,分别创建录屏服务、MediaProjectionManager、MediaRecorder以及创建VirtualDisplay,最后开始录屏。录屏结束后对相关资源进行释放。
下面上代码:
首先是配置权限:
然而在实际开发中发生了如下一幕,Permission Deny.这个时候可能是相关的权限没有自动打开,需要手动去应用权限管理中将相关的权限打开,如下:
然后就是写界面XML(界面很简单):
接下来是MainActivity.java(相关说明写在注释中了,直接参考的API,准确一些,现在访问API可方便多了,哈哈哈)
package com.dzjin.screen.screenshotandrecorddemo;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.media.projection.MediaProjectionManager;
import android.os.Environment;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.view.KeyEvent;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.Toast;
import java.io.FileOutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
public class MainActivity extends AppCompatActivity {
private LinearLayout linearLayout=null;
private Button buttonRecord=null;
private Button buttonCapture=null;
private boolean isRecord=false;
private int mScreenWidth;
private int mScreenHeight;
private int mScreenDensity;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getScreenBaseInfo();
linearLayout=(LinearLayout)findViewById(R.id.linearLayout);
buttonRecord=(Button)findViewById(R.id.buttonRecord);
buttonCapture=(Button)findViewById(R.id.buttonCapture);
buttonRecord.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if(isRecord){
stopScreenRecord();
}else{
startScreenRecord();
}
}
});
buttonCapture.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
capture(linearLayout);
}
});
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(requestCode == 1000){
if(resultCode == RESULT_OK){
//获得录屏权限,启动Service进行录制
Intent intent=new Intent(MainActivity.this,ScreenRecordService.class);
intent.putExtra("resultCode",resultCode);
intent.putExtra("resultData",data);
intent.putExtra("mScreenWidth",mScreenWidth);
intent.putExtra("mScreenHeight",mScreenHeight);
intent.putExtra("mScreenDensity",mScreenDensity);
startService(intent);
Toast.makeText(this,"录屏开始",Toast.LENGTH_SHORT).show();
}else{
Toast.makeText(this,"录屏失败",Toast.LENGTH_SHORT).show();
}
}
}
//start screen record
private void startScreenRecord(){
//Manages the retrieval of certain types of MediaProjection tokens.
MediaProjectionManager mediaProjectionManager=
(MediaProjectionManager)getSystemService(Context.MEDIA_PROJECTION_SERVICE);
//Returns an Intent that must passed to startActivityForResult() in order to start screen capture.
Intent permissionIntent=mediaProjectionManager.createScreenCaptureIntent();
startActivityForResult(permissionIntent,1000);
isRecord=true;
buttonRecord.setText(new String("停止录屏"));
}
//stop screen record.
private void stopScreenRecord(){
Intent service = new Intent(this, ScreenRecordService.class);
stopService(service);
isRecord=false;
buttonRecord.setText(new String("开始录屏"));
Toast.makeText(this,"录屏成功",Toast.LENGTH_SHORT).show();
}
public void capture(View v){
//格式化时间作为截屏文件名
SimpleDateFormat simpleDateFormat=new SimpleDateFormat("YY-MM-DD-HH-MM-SS");
//在获取外部存储的时候,一定注意添加权限,如果添加权限还不能成功,则手动在应用中开启权限。
String filePathName= Environment.getExternalStorageDirectory()+"/"+simpleDateFormat.format(new Date())+".png";
//Find the topmost view in the current view hierarcht.
View view=v.getRootView();
// Enable or disable drawing cache.
view.setDrawingCacheEnabled(true);
// Calling this method is equivalent to calling buildDrawingCache(false);
// In order to force drawing cache to be buuild.
view.buildDrawingCache();
//Calling this method is equivalent to calling getDrawingCache(false);
//Return the Bitmap in which this view drawing is cached.
Bitmap bitmap=view.getDrawingCache();
try{
System.out.println(filePathName);
FileOutputStream fileOutputStream=new FileOutputStream(filePathName);
//Write a compressed version of bitmap to specified outputStream.
bitmap.compress(Bitmap.CompressFormat.PNG,100,fileOutputStream);
Toast.makeText(this,"Cpature Succeed",Toast.LENGTH_SHORT).show();
}catch (Exception e){
e.printStackTrace();
Toast.makeText(this,"Cpature Failed",Toast.LENGTH_SHORT).show();
}
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
// 在这里将BACK键模拟了HOME键的返回桌面功能(并无必要)
if (keyCode == KeyEvent.KEYCODE_BACK) {
simulateHome();
return true;
}
return super.onKeyDown(keyCode, event);
}
/**
* 获取屏幕基本信息
*/
private void getScreenBaseInfo() {
//A structure describing general information about a display, such as its size, density, and font scaling.
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
mScreenWidth = metrics.widthPixels;
mScreenHeight = metrics.heightPixels;
mScreenDensity = metrics.densityDpi;
}
/**
* 模拟HOME键返回桌面的功能
*/
private void simulateHome() {
//Intent.ACTION_MAIN,Activity Action: Start as a main entry point, does not expect to receive data.
Intent intent = new Intent(Intent.ACTION_MAIN);
//If set, this activity will become the start of a new task on this history stack.
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//This is the home activity, that is the first activity that is displayed when the device boots.
intent.addCategory(Intent.CATEGORY_HOME);
this.startActivity(intent);
}
}
package com.dzjin.screen.screenshotandrecorddemo;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.hardware.display.DisplayManager;
import android.hardware.display.VirtualDisplay;
import android.media.MediaRecorder;
import android.media.projection.MediaProjection;
import android.media.projection.MediaProjectionManager;
import android.os.Environment;
import android.os.IBinder;
import android.support.annotation.Nullable;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* Created by dzjin on 2018/1/9.
*/
public class ScreenRecordService extends Service {
private int resultCode;
private Intent resultData=null;
private MediaProjection mediaProjection=null;
private MediaRecorder mediaRecorder=null;
private VirtualDisplay virtualDisplay=null;
private int mScreenWidth;
private int mScreenHeight;
private int mScreenDensity;
private Context context=null;
@Override
public void onCreate() {
super.onCreate();
}
/**
* Called by the system every time a client explicitly starts the service by calling startService(Intent),
* providing the arguments it supplied and a unique integer token representing the start request.
* Do not call this method directly.
* @param intent
* @param flags
* @param startId
* @return
*/
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
try{
resultCode=intent.getIntExtra("resultCode",-1);
resultData=intent.getParcelableExtra("resultData");
mScreenWidth=intent.getIntExtra("mScreenWidth",0);
mScreenHeight=intent.getIntExtra("mScreenHeight",0);
mScreenDensity=intent.getIntExtra("mScreenDensity",0);
mediaProjection=createMediaProjection();
mediaRecorder=createMediaRecorder();
virtualDisplay=createVirtualDisplay();
mediaRecorder.start();
}catch (Exception e) {
e.printStackTrace();
}
/**
* START_NOT_STICKY:
* Constant to return from onStartCommand(Intent, int, int): if this service's process is
* killed while it is started (after returning from onStartCommand(Intent, int, int)),
* and there are no new start intents to deliver to it, then take the service out of the
* started state and don't recreate until a future explicit call to Context.startService(Intent).
* The service will not receive a onStartCommand(Intent, int, int) call with a null Intent
* because it will not be re-started if there are no pending Intents to deliver.
*/
return Service.START_NOT_STICKY;
}
//createMediaProjection
public MediaProjection createMediaProjection(){
/**
* Use with getSystemService(Class) to retrieve a MediaProjectionManager instance for
* managing media projection sessions.
*/
return ((MediaProjectionManager)getSystemService(Context.MEDIA_PROJECTION_SERVICE))
.getMediaProjection(resultCode,resultData);
/**
* Retrieve the MediaProjection obtained from a succesful screen capture request.
* Will be null if the result from the startActivityForResult() is anything other than RESULT_OK.
*/
}
private MediaRecorder createMediaRecorder(){
SimpleDateFormat simpleDateFormat=new SimpleDateFormat("YY-MM-DD-HH-MM-SS");
String filePathName= Environment.getExternalStorageDirectory()+"/video"+simpleDateFormat.format(new Date())+".mp4";
//Used to record audio and video. The recording control is based on a simple state machine.
MediaRecorder mediaRecorder=new MediaRecorder();
//Set the video source to be used for recording.
mediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
//Set the format of the output produced during recording.
//3GPP media file format
mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
//Sets the video encoding bit rate for recording.
//param:the video encoding bit rate in bits per second.
mediaRecorder.setVideoEncodingBitRate(5*mScreenWidth*mScreenHeight);
//Sets the video encoder to be used for recording.
mediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
//Sets the width and height of the video to be captured.
mediaRecorder.setVideoSize(mScreenWidth,mScreenHeight);
//Sets the frame rate of the video to be captured.
mediaRecorder.setVideoFrameRate(60);
try{
//Pass in the file object to be written.
mediaRecorder.setOutputFile(filePathName);
//Prepares the recorder to begin capturing and encoding data.
mediaRecorder.prepare();
}catch (Exception e){
e.printStackTrace();
}
return mediaRecorder;
}
private VirtualDisplay createVirtualDisplay(){
/**
* name String: The name of the virtual display, must be non-empty.This value must never be null.
width int: The width of the virtual display in pixels. Must be greater than 0.
height int: The height of the virtual display in pixels. Must be greater than 0.
dpi int: The density of the virtual display in dpi. Must be greater than 0.
flags int: A combination of virtual display flags. See DisplayManager for the full list of flags.
surface Surface: The surface to which the content of the virtual display should be rendered, or null if there is none initially.
callback VirtualDisplay.Callback: Callback to call when the virtual display's state changes, or null if none.
handler Handler: The Handler on which the callback should be invoked, or null if the callback should be invoked on the calling thread's main Looper.
*/
/**
* DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR
* Virtual display flag: Allows content to be mirrored on private displays when no content is being shown.
*/
return mediaProjection.createVirtualDisplay("mediaProjection",mScreenWidth,mScreenHeight,mScreenDensity,
DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,mediaRecorder.getSurface(),null,null);
}
@Override
public void onDestroy() {
super.onDestroy();
if(virtualDisplay!=null){
virtualDisplay.release();
virtualDisplay=null;
}
if(mediaRecorder!=null){
mediaRecorder.stop();
mediaRecorder=null;
}
if(mediaProjection!=null){
mediaProjection.stop();
mediaProjection=null;
}
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
OK,完成,相关的解释直接写在代码中了。
非淡泊无以明志,非宁静无以致远。共勉。
Demo下载地址:http://download.csdn.net/download/dzjin1234/10199546