rendering

Principle that

Use SurfaceView preview video using the system’s own MediaRecorder video recording on the MediaRecoreder use steps in the source notes written very clear

  1. instantiation
  2. Setting audio Input
  3. Format the output
  4. Set the video encoding format
  5. Setting the Output Path
  6. Call prepare() to initialize the resource
  7. Call start() to start recording note: the sequence of steps here is very important, if you are not so familiar with MediaRecorder, or follow the steps better

Method of use

    // Record video
    private void toRecordVideo(a) {
        RecordConfig
                .getInstance()
                .with(this)
                .setQuality(RecordConfig.Quality.QUALITY_480P)
                .setMaxDuration(6*1000)
                .setFocusMode(RecordConfig.FocusMode.FOCUS_MODE_CONTINUOUS_VIDEO)
                .setOutputPath("/smallvideo/")
                .obtainVideo(REQUEST_CODE_VIDEO);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode==REQUEST_CODE_VIDEO&&resultCode==RESULT_OK){
            // Receive video output path
            String videoPath=RecordConfig.obtainVideoPath(data);
            int duration=RecordConfig.obtainVideoDuration(data);
            Log.i(this.getClass().getSimpleName(),"obtainVideoPath="+videoPath+" duration="+generateTime(duration)); }}Copy the code

Implementing Video recording

Create a recording and playing screen

Recording interface

<?xml version="1.0" encoding="utf-8"? >
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".RecordVideoActivity">
    <! -- Preview video -->
    <SurfaceView
        android:id="@+id/surfaceView"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
    <! Record button -->
    <Button
        android:id="@+id/btnRecord"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:layout_marginBottom="60dp"
        android:background="@drawable/selector_record_point"
        app:layout_constraintBottom_toBottomOf="@+id/surfaceView"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="parent" />
    <! -- Record progress bar -->
    <com.junt.videorecorderlib.CustomProgressBar
        android:id="@+id/progressBar"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_margin="5dp"
        android:visibility="gone"
        app:layout_constraintBottom_toBottomOf="@+id/btnRecord"
        app:layout_constraintEnd_toEndOf="@+id/btnRecord"
        app:layout_constraintStart_toStartOf="@+id/btnRecord"
        app:layout_constraintTop_toTopOf="@+id/btnRecord"
        app:ringWidth="10"
        app:style="ring" />
    <! -- Cancel button -->
    <ImageView
        android:id="@+id/ivBack"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:paddingStart="5dp"
        android:paddingTop="10dp"
        android:paddingEnd="5dp"
        android:paddingBottom="10dp"
        android:scaleType="fitXY"
        android:src="@drawable/down"
        app:layout_constraintBottom_toBottomOf="@+id/btnRecord"
        app:layout_constraintEnd_toStartOf="@+id/btnRecord"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="@+id/btnRecord" />

</androidx.constraintlayout.widget.ConstraintLayout>
Copy the code

Record button animation effect inside the white button zoom out using selector implementation

<?xml version="1.0" encoding="utf-8"? >
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_pressed="true">
        <layer-list>
            <item android:drawable="@drawable/record_bg_recording" android:gravity="center" />

            <item android:drawable="@drawable/record_fg_recording" android:gravity="center" />
        </layer-list>
    </item>

    <item>
        <layer-list>
            <item android:drawable="@drawable/record_bg_default" android:gravity="center" />

            <item android:drawable="@drawable/record_fg_default" android:gravity="center" />
        </layer-list>
    </item>

</selector>
Copy the code

Custom circular progress bar: github.com/xiaojigugu/…

Preview the interface

<?xml version="1.0" encoding="utf-8"? >
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".PlayVideoActivity">
    
    <! -- Customize View to play video -->
    <com.junt.videorecorderlib.MediaPlayView
        android:id="@+id/playView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
    <! Cancelled - - - >
    <ImageButton
        android:id="@+id/btnGiveUp"
        android:layout_width="80dp"
        android:layout_height="80dp"
        android:background="@drawable/selector_video_play_button"
        android:src="@drawable/fanhui"
        app:layout_constraintBottom_toTopOf="@+id/guideline2"
        app:layout_constraintEnd_toStartOf="@+id/guideline"
        app:layout_constraintHorizontal_bias="0.44"
        app:layout_constraintStart_toStartOf="parent" />
    <! - sure - >
    <ImageButton
        android:id="@+id/btnConfirm"
        android:layout_width="80dp"
        android:layout_height="80dp"
        android:background="@drawable/selector_video_play_button"
        android:src="@drawable/duigou"
        app:layout_constraintBottom_toBottomOf="@+id/btnGiveUp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="@+id/guideline" />
    <! -- Auxiliary line -->
    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/guideline"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        app:layout_constraintGuide_percent="0.5" />
    <! -- Auxiliary line -->
    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/guideline2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintGuide_percent="0.9" />
</androidx.constraintlayout.widget.ConstraintLayout>  
Copy the code

Custom MediaPlayView inherits the SurfaceView and initializes MediaPlayer with the SurfaceHolder callback method

Create a camera Management class

Here is a direct copy of the code from Google’s official example

public class CameraHelper {

    public static final int MEDIA_TYPE_IMAGE = 1;
    public static final int MEDIA_TYPE_VIDEO = 2;

    /**
     * Iterate over supported camera video sizes to see which one best fits the
     * dimensions of the given view while maintaining the aspect ratio. If none can,
     * be lenient with the aspect ratio.
     *
     * @param supportedVideoSizes Supported camera video sizes.
     * @param previewSizes Supported camera preview sizes.
     * @param w     The width of the view.
     * @param h     The height of the view.
     * @return Best match camera video size to fit inthe view. */ public static Camera.Size getOptimalVideoSize(List<Camera.Size> supportedVideoSizes, List<Camera.Size> previewSizes, int w, Int h) {// Use a very small tolerance because we want an exact match. Final double ASPECT_TOLERANCE = 0.1; double targetRatio = (double) w / h; // Supported video sizes list might be null, it means that we are allowed to use the preview // sizes List<Camera.Size> videoSizes;if(supportedVideoSizes ! = null) { videoSizes = supportedVideoSizes; }else {
            videoSizes = previewSizes;
        }
        Camera.Size optimalSize = null;

        // Start with max value and refine as we iterate over available video sizes. This is the
        // minimum difference between view and camera height.
        double minDiff = Double.MAX_VALUE;

        // Target view height
        int targetHeight = h;

        // Try to find a video size that matches aspect ratio and the target view size.
        // Iterate over all available sizes and pick the largest size that can fit in the view and
        // still maintain the aspect ratio.
        for (Camera.Size size : videoSizes) {
            double ratio = (double) size.width / size.height;
            if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE)
                continue;
            if (Math.abs(size.height - targetHeight) < minDiff && previewSizes.contains(size)) {
                optimalSize = size;
                minDiff = Math.abs(size.height - targetHeight);
            }
        }

        // Cannot find video size that matches the aspect ratio, ignore the requirement
        if (optimalSize == null) {
            minDiff = Double.MAX_VALUE;
            for (Camera.Size size : videoSizes) {
                if(Math.abs(size.height - targetHeight) < minDiff && previewSizes.contains(size)) { optimalSize = size; minDiff = Math.abs(size.height - targetHeight); }}}returnoptimalSize; } / * * * @return the default camera on the device. Return null if there is no camera on the device.
     */
    public static Camera getDefaultCameraInstance() {
        returnCamera.open(); } / * * * @return the default rear/back facing camera on the device. Returns null if camera is not
     * available.
     */
    public static Camera getDefaultBackFacingCameraInstance() {
        Log.i(CameraHelper.class.getSimpleName(),"getDefaultBackFacingCameraInstance");
        returngetDefaultCamera(Camera.CameraInfo.CAMERA_FACING_BACK); } / * * * @return the default front facing camera on the device. Returns null if camera is not
     * available.
     */
    public static Camera getDefaultFrontFacingCameraInstance() {
        return getDefaultCamera(Camera.CameraInfo.CAMERA_FACING_FRONT);
    }


    /**
     *
     * @param position Physical position of the camera i.e Camera.CameraInfo.CAMERA_FACING_FRONT
     *                 or Camera.CameraInfo.CAMERA_FACING_BACK.
     * @return the default camera on the device. Returns null if camera is not available.
     */
    @TargetApi(Build.VERSION_CODES.GINGERBREAD)
    private static Camera getDefaultCamera(int position) {
        // Find the total number of cameras available
        int  mNumberOfCameras = Camera.getNumberOfCameras();

        // Find the ID of the back-facing ("default") camera
        Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
        for (int i = 0; i < mNumberOfCameras; i++) {
            Camera.getCameraInfo(i, cameraInfo);
            if (cameraInfo.facing == position) {
                returnCamera.open(i); }}return null;
    }

    /**
     * Creates a media file in the {@code Environment.DIRECTORY_PICTURES} directory. The directory
     * is persistent and available to other applications like gallery.
     *
     * @param type Media type. Can be video or image.
     * @return A file object pointing to the newly created file.
     */
    public  static File getOutputMediaFile(int type){
        // To be safe, you should check that the SDCard is mounted
        // using Environment.getExternalStorageState() before doing this.
        if(! Environment.getExternalStorageState().equalsIgnoreCase(Environment.MEDIA_MOUNTED)) {return  null;
        }

        File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(
                Environment.DIRECTORY_PICTURES), "CameraSample");
        // This location works best if you want the created images to be shared
        // between applications and persist after your app has been uninstalled.

        // Create the storage directory if it does not exist
        if (! mediaStorageDir.exists()){
            if (! mediaStorageDir.mkdirs()) {
                Log.d("CameraSample"."failed to create directory");
                return null;
            }
        }

        // Create a media file name
        String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US).format(new Date());
        File mediaFile;
        if (type == MEDIA_TYPE_IMAGE){
            mediaFile = new File(mediaStorageDir.getPath() + File.separator +
                    "IMG_"+ timeStamp + ".jpg");
        } else if(type == MEDIA_TYPE_VIDEO) {
            mediaFile = new File(mediaStorageDir.getPath() + File.separator +
                    "VID_"+ timeStamp + ".mp4");
        } else {
            return null;
        }
        returnmediaFile; }}Copy the code

Create the MediaRecorder configuration class

RecordConfig.java

public class RecordConfig {
    private static RecordConfig recordConfig;
    private Activity activity;

    public static final String RECORD_CONFIG_SP_NAME = "RECORD_CONFIG";
    private SharedPreferences sp;

    static final String CONFIG_QUALITY = "quality";
    static final String CONFIG_FOCUS_MODE = "focus_mode";
    static final String CONFIG_ENCODING_BIT_RATE = "bitRate";
    static final String CONFIG_FRAME_RATE = "frameRate";
    static final String CONFIG_OUTPUT_PATH = "outputPath";
    static final String CONFIG_MAX_DURATION = "duration";

    public static RecordConfig getInstance(a) {
        if (recordConfig == null) {
            recordConfig = new RecordConfig();
        }
        returnrecordConfig; } <! -- Receive context -->public RecordConfig with(Activity activity) {
        this.activity = activity;
        sp = activity.getSharedPreferences(RECORD_CONFIG_SP_NAME, Context.MODE_PRIVATE);
        return this; } <! -- Video quality -->public RecordConfig setQuality(int quality) {
        if (sp == null) {
            throw new NullPointerException("Please innovate method 'with()' first");
        }
        / / default: 480 p
        sp.edit().putInt(CONFIG_QUALITY, quality).commit();
        return this; } <! -- Focus mode -->public RecordConfig setFocusMode(String focusMode) {
        if (sp == null) {
            throw new NullPointerException("Please innovate method 'with()' first");
        }
        //default:FOCUS_MODE_CONTINUOUS_VIDEO
        sp.edit().putString(CONFIG_FOCUS_MODE, focusMode).commit();
        return this; } <! -- Audio sampling rate -->public RecordConfig setEncodingBitRate(int encodingBitRate) {
        if (sp == null) {
            throw new NullPointerException("Please innovate method 'with()' first");
        }
        //default:5 * 1280 * 720
        sp.edit().putInt(CONFIG_ENCODING_BIT_RATE, encodingBitRate).commit();
        return this; } <! -- Video frame rate -->public RecordConfig setFrameRate(int frameRate) {
        if (sp == null) {
            throw new NullPointerException("Please innovate method 'with()' first");
        }
        //default:20
        sp.edit().putInt(CONFIG_FRAME_RATE, frameRate).commit();
        return this;
    }

    /** * Set the video output path *@paramOutputPath the default video outputPath prefix is /storage/emulated/0 * eg: OutputPath = ". / smallvideo/files/VID_timestamp mp4 ", the final file path for/storage/emulated / 0 / smallvideo/files/VID_timestamp. Mp4 *@return RecordConfig
     */
    public RecordConfig setOutputPath(String outputPath) {
        if (sp == null) {
            throw new NullPointerException("Please innovate method 'with()' first");
        }
        / / default: / storage/emulated / 0 / junt/video/VID_timestamp. Mp4
        String path = Environment.getExternalStorageDirectory() + outputPath;
        sp.edit().putString(CONFIG_OUTPUT_PATH, path).commit();
        return this; } <! -- Set maximum duration -->public RecordConfig setMaxDuration(int duration) {
        if (sp == null) {
            throw new NullPointerException("Please innovate method 'with()' first");
        }
        //default:6*1000ms
        sp.edit().putInt(CONFIG_MAX_DURATION, duration).commit();
        return this;
    }

    /** * Get the video path */
    public static String obtainVideoPath(Intent data){
        if (data==null) {throw new NullPointerException("data is NULL");
        }
        return data.getStringExtra("path");
    }

    /** * get the video length */
    public static int obtainVideoDuration(Intent data){
        if (data==null) {throw new NullPointerException("data is NULL");
        }
        return data.getIntExtra("duration".0); } <! -- Jump to video recording interface -->public void obtainVideo(int requestCode) {
        if (activity == null) {
            throw new NullPointerException("Please innovate method 'with()' first");
        }
        activity.startActivityForResult(newIntent(activity, RecordVideoActivity.class), requestCode); } <! -- Video quality -->public static class Quality {
        public static int QUALITY_LOW = CamcorderProfile.QUALITY_LOW;
        public static int QUALITY_HIGH = CamcorderProfile.QUALITY_HIGH;
        public static int QUALITY_QCIF = CamcorderProfile.QUALITY_QCIF;
        public static int QUALITY_CIF = CamcorderProfile.QUALITY_CIF;
        public static int QUALITY_480P = CamcorderProfile.QUALITY_480P;
        public static int QUALITY_720P = CamcorderProfile.QUALITY_720P;
        public static int QUALITY_1080P = CamcorderProfile.QUALITY_1080P;
        public static int QUALITY_QVGA = CamcorderProfile.QUALITY_QVGA;
        @TargetApi(Build.VERSION_CODES.LOLLIPOP)
        public static intQUALITY_2160P = CamcorderProfile.QUALITY_2160P; } <! -- Focus mode -->public static class FocusMode {
        public static String FOCUS_MODE_AUTO = Camera.Parameters.FOCUS_MODE_AUTO;
        public static String FOCUS_MODE_CONTINUOUS_PICTURE = Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE;
        public static String FOCUS_MODE_CONTINUOUS_VIDEO = Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO;
        public static String FOCUS_MODE_EDOF = Camera.Parameters.FOCUS_MODE_EDOF;
        public static String FOCUS_MODE_INFINITY = Camera.Parameters.FOCUS_MODE_INFINITY;
        public static String FOCUS_MODE_FIXED = Camera.Parameters.FOCUS_MODE_FIXED;
        public staticString FOCUS_MODE_MACRO = Camera.Parameters.FOCUS_MODE_MACRO; }}Copy the code

Create the RecordVideoActivity screen

!!!!!!!!! All camera and video recording operations should be handled asynchronously

  1. Initialize the camera and preview it
        mCamera = Camera.open();
        Log.d(TAG, "Camera.open");
        // Get camera parameters
        Camera.Parameters parameters = mCamera.getParameters();
        // Get all preview sizes
        List<Camera.Size> mSupportedPreviewSizes = parameters.getSupportedPreviewSizes();
        // Get all video dimensions
        List<Camera.Size> mSupportedVideoSizes = parameters.getSupportedVideoSizes();
        // Get the appropriate size
        Camera.Size optimalSize = CameraHelper.getOptimalVideoSize(mSupportedVideoSizes,
                mSupportedPreviewSizes, surfaceView.getWidth(), surfaceView.getHeight());
        // Set the preview portrait orientation
        mCamera.setDisplayOrientation(90);
        // Obtain the default system configuration
        CamcorderProfile profile = CamcorderProfile.get(sp.getInt(RecordConfig.CONFIG_QUALITY, CamcorderProfile.QUALITY_480P));
        // Set the recording size
        profile.videoFrameWidth = optimalSize.width;
        profile.videoFrameHeight = optimalSize.height;
        // Set the video bit rate
        profile.videoBitRate = sp.getInt(RecordConfig.CONFIG_ENCODING_BIT_RATE, 5 * 1280 * 720);
        // Set the video frame rate
        profile.videoFrameRate = sp.getInt(RecordConfig.CONFIG_FRAME_RATE, 30);
        // Set the preview size
        parameters.setPreviewSize(profile.videoFrameWidth, profile.videoFrameHeight);
        // Set the focus mode
        parameters.setFocusMode(sp.getString(RecordConfig.CONFIG_FOCUS_MODE, Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO));
        // Set the flash mode
        parameters.setFlashMode(Camera.Parameters.FLASH_MODE_AUTO);
        mCamera.setParameters(parameters);
        try {
            / / bind SurfaceView
            mCamera.setPreviewDisplay(surfaceView.getHolder());
            // Start preview
            mCamera.startPreview();
            Log.d(TAG, "Camera initialization completed");
        } catch (IOException e) {
            Log.e(TAG, "Surface texture is unavailable or unsuitable" + e.getMessage());
        }  
Copy the code
  1. Initialize the MediaRecorder
        mMediaRecorder = new MediaRecorder();
        // Release the camera so that MediaRecorder can use it
        // It is clear from the source comments that this method must be called in advance
        //* <p>This must be done before calling
        //* {@link android.media.MediaRecorder#setCamera(Camera)}. This cannot be
        //* called after recording starts.
        //
        mCamera.unlock();
        // Bind the camera
        mMediaRecorder.setCamera(mCamera);
        // Set the audio source
        mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT);
        // Set the video source
        mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
        // Set the recorded video quality
        mMediaRecorder.setProfile(CamcorderProfile.get(sp.getInt(RecordConfig.CONFIG_QUALITY, CamcorderProfile.QUALITY_480P)));
        // Set the video bit rate
        mMediaRecorder.setVideoFrameRate(sp.getInt(RecordConfig.CONFIG_FRAME_RATE, 30));
        // Set the video frame rate
        mMediaRecorder.setVideoEncodingBitRate(sp.getInt(RecordConfig.CONFIG_ENCODING_BIT_RATE, 5 * 1280 * 720));
        // Set the video output file
        String defaultPath = Environment.getExternalStorageDirectory() + "/junt/video/";
        String outputPath = sp.getString(RecordConfig.CONFIG_OUTPUT_PATH, defaultPath) + "VID_" + System.currentTimeMillis() + ".mp4";
        mOutputFile = new File(outputPath);
        if(! mOutputFile.getParentFile().exists()) { mOutputFile.getParentFile().mkdirs(); } mMediaRecorder.setOutputFile(mOutputFile.getAbsolutePath());// Set recording video to portrait
        mMediaRecorder.setOrientationHint(90);
        // Set the preview Surface while recording
        mMediaRecorder.setPreviewDisplay(surfaceView.getHolder().getSurface());
        // Set the maximum duration
        / / items when setting time later can on the android media. The MediaRecorder. OnInfoListener listening receiving results in the callback methods
        // * After recording reaches the specified duration, a notification
        //* will be sent to the {@link android.media.MediaRecorder.OnInfoListener}
        //* with a "what" code of {@link #MEDIA_RECORDER_INFO_MAX_DURATION_REACHED}
        //* and recording will be stopped.
        MAX_DURATION = sp.getInt(RecordConfig.CONFIG_MAX_DURATION, 6 * 1000);
        mMediaRecorder.setMaxDuration(MAX_DURATION);
        try {
            // Set recording listener
            mMediaRecorder.setOnInfoListener(this);
            // Set error listening
            mMediaRecorder.setOnErrorListener(this);
            // Complete initialization and wait for recording
            mMediaRecorder.prepare();
        } catch (Exception e) {
            Log.d(TAG, "IllegalStateException preparing MediaRecorder: " + e.getMessage());
            return false;
        }   
        
        // Record finished listening
        @Override
        public void onInfo(MediaRecorder mr, int what, int extra) {
            Log.d(TAG, "onInfo what=" + what + " extra=" + extra);
            if(what == MediaRecorder.MEDIA_RECORDER_INFO_MAX_DURATION_REACHED && isRecording) { recordComplete(); }} ' 'note: steps1-2All operations in Java are asynchronousclass MediaPrepareTask extends AsyncTask<Void.Void.Boolean> {
        @Override
        protected Boolean doInBackground(Void... voids) {
            // Initialize Camera, MediaRecorder
            boolean initResult = prepareVideoRecorder();
            Log.d(TAG, "The doInBackground, init." + initResult);
            if (initResult) {
                Log.d(TAG, "Start preview");
                return true;
            } else {
                Log.d(TAG, "Initialization error");
                // Initialization error immediately release Camera, MediaRecorder
                releaseMediaRecorder();
                return false; }}}Copy the code
  1. Record button event
    /** * Record button touch event * Click Record (click again to stop recording), hold record (release to stop recording) */
    @Override
    public boolean onTouch(View view, MotionEvent motionEvent) {
        int action = motionEvent.getAction();
        if (action == MotionEvent.ACTION_DOWN) {
            / / press
            long downTime=System.currentTimeMillis();
            if (downTime-clickTime>1000) {if(! isRecording){ btnRecord.setPressed(true);
                    // Start recording
                    RecordTask recordTask = new RecordTask();
                    recordTask.execute();
                }else if(mMediaRecorder! =null){
                    recordComplete();
                }
            }
            clickTime=downTime;
        } else if (action == MotionEvent.ACTION_UP) {
            / / lift
            if(isRecording && mMediaRecorder ! =null) {
                // Stop recordingrecordComplete(); }}return true;
    }  
    
     /** * Start recording task */
    class RecordTask extends AsyncTask<Void.Void.Boolean> {
        @Override
        protected Boolean doInBackground(Void... voids) {
            mMediaRecorder.start();
            return true;
        }

        @Override
        protected void onPostExecute(Boolean aBoolean) {
            super.onPostExecute(aBoolean);
            isRecording = true;
            // Update the circular progress barsetProgressBar(); }}/**
     * 停止录制
     */
    private void recordComplete(a) {
        isRecording = false;
        btnRecord.setPressed(false);
        endTime = System.currentTimeMillis();
        hideRecordController();
        // Jump to the video player interface for a complete preview, and receive the result of using the video file in onActivityResult
        Intent intent = new Intent(RecordVideoActivity.this, PlayVideoActivity.class);
        intent.putExtra(VIDEO_PATH, mOutputFile.getAbsolutePath());
        startActivityForResult(intent, REQUEST_CODE_TO_PLAY);
    } 
    
       /** * Sets the progress bar display and resize it */
    private void setProgressBar(a) {
        progressBar.setProgress(0);
        ConstraintLayout.LayoutParams params = (ConstraintLayout.LayoutParams) progressBar.getLayoutParams();
        params.width = btnRecord.getMeasuredWidth() + 20;
        params.height = btnRecord.getMeasuredHeight() + 20;
        progressBar.setLayoutParams(params);
        progressBar.setVisibility(View.VISIBLE);
        startTime = System.currentTimeMillis
        // Update the progress bar with a timer
        progressHandler = new ProgressHandler();
        new Timer().schedule(new TimerTask() {
            @Override
            public void run(a) {
                if(progressHandler ! =null && isRecording) {
                    progressHandler.sendEmptyMessage(0); }}},0.50);
    }
    
     /** * Updates the progress bar */
    class ProgressHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if (msg.what == 0) {
                int progress = (int) ((System.currentTimeMillis() - startTime) / (MAX_DURATION / 100));
                if (progress <= 100) { progressBar.setProgress(progress); }}}}/** * Event when receiving user confirmation *@paramRequestCode indicates the playback requestCode *@paramResultCode indicates the resultCode *@paramData passes duration */
    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == REQUEST_CODE_TO_PLAY) {
            if (resultCode == RESULT_CANCELED) {
                // The user selects cancel to reset the video recording interface (progress bar, play button)
                resetProgress();
                showRecordController();
                // Delete the abandoned video
                deleteFile();
            } else if (resultCode == RESULT_OK) {
                // The user selects the video and returns the result to the caller
                Intent intent = new Intent();
                intent.putExtra("duration", data.getIntExtra("duration".0));
                intent.putExtra("path", mOutputFile.getAbsolutePath()); setResult(RESULT_OK, intent); finish(); }}}Copy the code

Play interface

    /** * Discard this video */
    private void giveUp(a) {
        setResult(RESULT_CANCELED);
        finish();
    }

    /** * select this video */
    private void chooseThisVideo(a) {
        Intent intent = new Intent();
        intent.putExtra("duration", mediaPlayView.getDuration());
        Log.i(TAG,"duration="+mediaPlayView.getDuration());
        setResult(RESULT_OK, intent);
        finish();
    }
Copy the code

The end

Source code: SmallVideoRecord