android 录屏功能的实现(带悬浮框)

1、权限申请

权限包括最基本的读写权限,使用相机的权限,还有有两个系统权限,一个是SD创建和删除文件的权限,另一个是悬浮窗的权限。具体如下:

android6.0以上需要动态申请,部分手机需要在系统设置中手动开启悬浮窗权限。

2、具体实现代码

2.1 创建服务,后台进行录屏操作

package comvoice.example.zhangbin.videorecorddemo;

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.io.File;
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("yyyy-MM-dd-HH-mm-ss");
        String filePath=Environment.getExternalStorageDirectory()+"/ScreenVideo/";
        if(!new File(filePath).exists()){
            new File(filePath).mkdirs();
        }
        String filePathName= filePath+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;
    }
}

2.2 MainActivity中代码实现

 创建悬浮窗

在onDestroy()中移除悬浮窗布局:

windowManager.removeView(floatWindowView);

悬浮窗的布局文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <Button
        android:id="@+id/bt_play"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="录屏"
        android:textSize="10dp"
        android:background="@drawable/bg_shape"/>
</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
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
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();
}

MainActivity的完整代码:

package comvoice.example.zhangbin.videorecorddemo;

import android.Manifest;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.PixelFormat;
import android.media.projection.MediaProjectionManager;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.Settings;
import android.support.annotation.RequiresApi;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.Toast;

import com.tbruyelle.rxpermissions.RxPermissions;

import rx.functions.Action1;

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;
    private WindowManager windowManager;
    private View floatWindowView;
    private RxPermissions rxPermissions;
    private Button bt_play;
    private float lastX;
    private float lastY;
    private float mTouchStartX;
    private float mTouchStartY;
    /**
     * 浮动窗原始位置
     */
    private float startPositionX = 0;
    private float startPositionY = 0;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        rxPermissions=new RxPermissions(this);
        getScreenBaseInfo();
        initView();
        initClick();

    }

    @Override
    protected void onStart() {
        super.onStart();
        rxPermissions.request(Manifest.permission.CAMERA,Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS,Manifest.permission.WRITE_EXTERNAL_STORAGE,
                Manifest.permission.SYSTEM_ALERT_WINDOW)
                .subscribe(new Action1<Boolean>() {
                    @Override
                    public void call(Boolean aBoolean) {
                        if(aBoolean){

                        }
                    }
                });
    }

    private void initWindow() {
        windowManager= (WindowManager) getSystemService(Context.WINDOW_SERVICE);
        //设置悬浮窗布局属性
        final WindowManager.LayoutParams layoutParams=new WindowManager.LayoutParams();
        //设置类型
        if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.O){
            layoutParams.type=WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
        }else {
            layoutParams.type=WindowManager.LayoutParams.TYPE_PHONE;
        }
        //设置行为选项
        layoutParams.flags=WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
        //设置悬浮窗的显示位置
        layoutParams.gravity= Gravity.LEFT;
        //设置x周的偏移量
        layoutParams.x=0;
        //设置y轴的偏移量
        layoutParams.y=0;
        //如果悬浮窗图片为透明图片,需要设置该参数为PixelFormat.RGBA_8888
        layoutParams.format= PixelFormat.RGBA_8888;
        //设置悬浮窗的宽度
        layoutParams.width=WindowManager.LayoutParams.WRAP_CONTENT;
        //设置悬浮窗的高度
        layoutParams.height=WindowManager.LayoutParams.WRAP_CONTENT;
        //设置悬浮窗的布局
        floatWindowView= LayoutInflater.from(this).inflate(R.layout.float_window,null);
        //加载显示悬浮窗
        windowManager.addView(floatWindowView,layoutParams);
        bt_play=floatWindowView.findViewById(R.id.bt_play);
        bt_play.setOnClickListener(new View.OnClickListener() {
            @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
            @Override
            public void onClick(View view) {
                if(isRecord){
                    bt_play.setText("开始录屏");
                    stopScreenRecord();
                }else{
                    bt_play.setText("停止录屏");
                    startScreenRecord();
                }
            }
        });
        bt_play.setOnLongClickListener(new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(View view) {
                windowManager.removeView(floatWindowView);
                return false;
            }
        });
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        windowManager.removeView(floatWindowView);
    }

    private void initClick() {
        buttonRecord.setOnClickListener(new View.OnClickListener() {
            @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
            @Override
            public void onClick(View view) {
                if(isRecord){
                    stopScreenRecord();
                }else{
                    startScreenRecord();
                }
            }
        });
        buttonCapture.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                initWindow();
            }
        });

    }

    private void initView() {
        linearLayout=(LinearLayout)findViewById(R.id.linearLayout);
        buttonRecord=(Button)findViewById(R.id.buttonRecord);
        buttonCapture=findViewById(R.id.buttonCapture);
    }

    @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
    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    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();
    }

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

上面实现了简单的录屏功能:

完整代码链接:

https://download.csdn.net/download/u011897782/10677004

  • 5
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
Android录屏功能实现需要通过使用MediaProjection API来实现。首先,我们需要获取屏幕的图像数据,可以通过MediaProjectionManager类的getMediaProjection()方法来获取用户授权的MediaProjection对象。然后,我们可以使用MediaProjection对象创建VirtualDisplay对象,该对象将屏幕图像数据渲染到一个Surface上。 在创建VirtualDisplay对象时,我们还需要指定渲染图像数据的Surface的参数,比如图像的宽高、dpi等。接下来,我们需要创建一个MediaCodec对象用于对图像数据进行编码,可以选择使用H.264或H.265编码格式。编码过程中,可以选择设置视频的码率、帧率等参数。 在编码完图像数据后,我们可以将编码后的数据保存为一个视频文件。可以使用MediaMuxer类创建一个包含音频轨道和视频轨道的mp4文件。我们需要使用MediaMuxer的addTrack()方法给音频轨道和视频轨道添加数据。编码后的图像数据可以通过MediaCodec的getOutputBuffer()方法获取到,然后写入视频轨道。音频数据可以通过AudioRecord对象获取到,然后写入音频轨道。 最后,我们要记得释放资源。需要调用VirtualDisplay、MediaCodec、MediaMuxer等对象的release()方法释放资源。此外,我们还需要关闭MediaProjection对象。为了保证录屏正常结束,我们可以监听用户按下Home键或其他影响屏幕显示的操作,然后停止录屏并保存视频文件。 总结来说,实现Android录屏功能需要通过获取图像数据、编码、写入文件等步骤来完成。使用MediaProjection API可以方便地获取屏幕图像数据,而MediaCodec和MediaMuxer类可以帮助我们对图像数据进行编码和保存。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值