录制屏幕
1、界面
2、核心代码
Kotlin
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.app.Service
import android.content.Context
import android.content.Intent
import android.graphics.BitmapFactory
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.Build
import android.os.IBinder
import android.util.DisplayMetrics
import android.util.Log
import android.view.WindowManager
import androidx.core.app.NotificationCompat
import java.io.IOException
class ScreenRecordService : Service() {
private var mContext: Context? = null
private var projectionManager: MediaProjectionManager? = null
private var mMediaProjection: MediaProjection? = null
override fun onBind(intent: Intent): IBinder {
TODO("Return the communication channel to the service.")
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
mContext = this
var resultCode = intent?.getIntExtra("resultCode", -1)
var path = intent?.getStringExtra("path")
var resultData: Intent? = intent?.getParcelableExtra("data")
Log.e("TAG", "录制的路径为:" + path)
startNotification();
projectionManager = getSystemService(MEDIA_PROJECTION_SERVICE) as MediaProjectionManager
mMediaProjection = resultCode?.let { resultData?.let { it1 -> projectionManager?.getMediaProjection(it, it1) } }
path?.let { startRecording(it) }
return super.onStartCommand(intent, flags, startId)
}
private var NOTIFICATION_CHANNEL_ID = "id";
private var NOTIFICATION_CHANNEL_NAME = "channel";
private var NOTIFICATION_CHANNEL_DESC = "desc";
private fun startNotification() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
var notificationIntent = Intent(mContext, ScreenRecordService::class.java)
var pendingIntent: PendingIntent? = null
pendingIntent = if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) {
PendingIntent.getActivity(this, 0, notificationIntent, PendingIntent.FLAG_IMMUTABLE);
} else {
PendingIntent.getActivity(
this, 0, notificationIntent,
PendingIntent.FLAG_ONE_SHOT or PendingIntent.FLAG_IMMUTABLE
);
}
var notificationBuilder = mContext?.let {
NotificationCompat.Builder(it, NOTIFICATION_CHANNEL_ID)
.setLargeIcon(BitmapFactory.decodeResource(mContext!!.resources, R.drawable.ic_launcher_foreground))
.setSmallIcon(R.drawable.ic_launcher_foreground)
.setContentTitle("start record")
.setContentText("=== start record ===")
.setContentIntent(pendingIntent)
};
var notification = notificationBuilder?.build();
var channel = NotificationChannel(
NOTIFICATION_CHANNEL_ID,
NOTIFICATION_CHANNEL_NAME,
NotificationManager.IMPORTANCE_DEFAULT
);
channel.description = NOTIFICATION_CHANNEL_DESC;
var notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.createNotificationChannel(channel)
startForeground(1, notification);
}
}
private var isScreenRecoding = false
private var mMediaRecorder: MediaRecorder? = null
private var mVirtualDisplay: VirtualDisplay? = null
private fun startRecording(filePath: String) {
if (!isScreenRecoding) {
try {
// 创建 MediaRecorder 并设置参数
val metrics = DisplayMetrics()
val windowManager: WindowManager = mContext?.getSystemService(WINDOW_SERVICE) as WindowManager
windowManager.defaultDisplay.getMetrics(metrics)
mMediaRecorder = MediaRecorder()
mMediaRecorder?.setVideoSource(MediaRecorder.VideoSource.SURFACE)
mMediaRecorder?.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4)
mMediaRecorder?.setOutputFile(filePath)
mMediaRecorder?.setVideoSize(metrics.widthPixels, metrics.heightPixels)
mMediaRecorder?.setVideoEncoder(MediaRecorder.VideoEncoder.H264)
mMediaRecorder?.setVideoEncodingBitRate(1920 * 1080 * 3)
mMediaRecorder?.setVideoFrameRate(30)
// 准备 MediaRecorder
mMediaRecorder?.prepare()
// 创建 VirtualDisplay 以获取屏幕内容
mVirtualDisplay = mMediaProjection?.createVirtualDisplay(
"ScreenRecord",
metrics.widthPixels, metrics.heightPixels, metrics.densityDpi,
DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
mMediaRecorder?.surface, null, null
)
// 开始录制
mMediaRecorder?.start()
isScreenRecoding = true
Log.i("TAG", "开始录屏 $filePath")
} catch (e: IOException) {
Log.e("TAG", "录屏失败: " + e.message)
e.printStackTrace()
}
}
}
public fun stopRecording() {
if (isScreenRecoding) {
try {
// 停止录制
mMediaRecorder?.stop()
mMediaRecorder?.reset()
mMediaRecorder?.release()
mMediaRecorder = null
// 停止 VirtualDisplay
mVirtualDisplay?.release()
// 停止 MediaProjection
mMediaProjection?.stop()
Log.i("TAG", "结束录屏")
} catch (e: Exception) {
Log.e("TAG", "停止录屏失败: " + e.message)
e.printStackTrace()
}
isScreenRecoding = false
}
}
override fun onDestroy() {
stopRecording()
super.onDestroy()
}
}
Kotlin
/**
* 注意:
* 1、需要手动给文件读写权限
* 2、保存的视频文件在/storage/emulated/0/screen.mp4
*/
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContentView(R.layout.activity_main)
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
insets
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == 500) {
val path = Environment.getExternalStorageDirectory().absolutePath
// 录屏权限
screenService = Intent(this, ScreenRecordService::class.java)
screenService?.let {
it.putExtra("resultCode", resultCode)
it.putExtra("data", data)
it.putExtra("path", "$path/screen.mp4")
startForegroundService(it)
}
}
}
/** 开始录制 **/
fun startRecord(view: View) {
start()
}
/** 停止录制 **/
fun stopRecord(view: View) {
stop()
}
private var mProjectionManager: MediaProjectionManager? = null
private var screenService: Intent? = null
private fun start() {
mProjectionManager = getSystemService(Context.MEDIA_PROJECTION_SERVICE) as MediaProjectionManager
// 请求录屏权限
mProjectionManager?.createScreenCaptureIntent()?.let { startActivityForResult(it, 500) };
}
private fun stop() {
stopService(screenService)
}
}
Kotlin
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.ScreenRecord"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name=".ScreenRecordService"
android:enabled="true"
android:exported="true"
android:foregroundServiceType="mediaProjection" />
</application>
</manifest>
3、下载地址
https://download.csdn.net/download/wy313622821/90115690
4、操作注意
点击开始录制后,切换到需要录制的界面,如果想要结束则回到当前应用点击停止录屏按钮