您的位置:首页 > 移动开发 > Android开发

Android截屏和录屏Demo

2018-01-10 22:51 239 查看
最近两天研究了一下安卓截屏和录屏功能的实现,基本的思路如下:

截屏:通过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,最后开始录屏。录屏结束后对相关资源进行释放。

下面上代码:

首先是配置权限:

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

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

然而在实际开发中发生了如下一幕,Permission Deny.这个时候可能是相关的权限没有自动打开,需要手动去应用权限管理中将相关的权限打开,如下:





然后就是写界面XML(界面很简单):

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/linearLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">

<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">

<Button
android:id="@+id/buttonCapture"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginEnd="20dp"
android:layout_marginStart="20dp"
android:text="截屏"
android:textSize="24sp" />

</LinearLayout>

<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">

<Button
android:id="@+id/buttonRecord"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="20dp"
android:layout_marginStart="20dp"
android:text="录屏"
android:textSize="24sp" />

</LinearLayout>

</LinearLayout>


效果如下:



接下来是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);
}

}


录屏ScreenRecordService.java

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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Android 录屏 截图 Demo