Android Studio App开发之使用摄像机录制视频和从视频库中选取视频的讲解及实战(附源码)

简介: Android Studio App开发之使用摄像机录制视频和从视频库中选取视频的讲解及实战(附源码)

运行有问题或需要源码请点赞关注收藏后评论区留言~~~

一、使用摄像机录制视频

与音频类似,通过系统摄像机可以很方便的录制视频,只要指定摄像动作为MediaStore.ACTION_VIDEO_CAPTURE即可

视频录制完成之后,最好能够预览视频的摄制画面,所以代码中调用了getOneFrame方法获取视频文件的某帧图片,查看该图片可以大致了解视频内容

效果如下

因为没连真机所以摄像头拍不出什么 读者可自行连接真机测试效果更佳

代码如下

Java类

package com.example.chapter13;
import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.view.View;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import com.example.chapter13.util.DateUtil;
import com.example.chapter13.util.FileUtil;
import com.example.chapter13.util.MediaUtil;
public class VideoRecordActivity extends AppCompatActivity implements View.OnClickListener {
    private final static String TAG = "VideoRecordActivity";
    private int RECORDER_CODE = 1; // 录制操作的请求码
    private TextView tv_video;
    private RelativeLayout rl_video;
    private ImageView iv_video;
    private Uri mVideoUri; // 视频文件的路径对象
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_video_record);
        tv_video = findViewById(R.id.tv_video);
        rl_video = findViewById(R.id.rl_video);
        iv_video = findViewById(R.id.iv_video);
        findViewById(R.id.btn_recorder).setOnClickListener(this);
        findViewById(R.id.rl_video).setOnClickListener(this);
    }
    @Override
    public void onClick(View v) {
        if (v.getId() == R.id.btn_recorder) {
            // 下面准备跳到系统的摄像机,并获得录制完的视频文件
            Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
            intent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 1); // 视频质量。0 低质量;1 高质量
            intent.putExtra(MediaStore.EXTRA_SIZE_LIMIT, 10485760L); // 大小限制,单位字节
            intent.putExtra(MediaStore.EXTRA_DURATION_LIMIT, 10); // 时长限制,单位秒
            startActivityForResult(intent, RECORDER_CODE); // 打开系统摄像机
        } else if (v.getId() == R.id.rl_video) {
            // 创建一个内容获取动作的意图(准备跳到系统播放器)
            Intent intent = new Intent(Intent.ACTION_VIEW);
            intent.setDataAndType(mVideoUri, "video/*"); // 类型为视频
            startActivity(intent); // 打开系统的视频播放器
        }
    }
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
        super.onActivityResult(requestCode, resultCode, intent);
        if (resultCode==RESULT_OK && requestCode==RECORDER_CODE){ // 从摄像机返回
            mVideoUri = intent.getData(); // 获得已录制视频的路径对象
//            // 生成临时视频的保存路径
//            String filePath = String.format("%s/%s.mp4",
//                    getExternalFilesDir(Environment.DIRECTORY_MOVIES), "video_"+ DateUtil.getNowDateTime());
//            // 把录制完的视频保存到临时路径
//            FileUtil.saveFileFromUri(this, mVideoUri, filePath);
            tv_video.setText("录制完成的视频地址为:"+mVideoUri.toString());
            rl_video.setVisibility(View.VISIBLE);
            // 获取视频文件的某帧图片
            Bitmap bitmap = MediaUtil.getOneFrame(this, mVideoUri);
            iv_video.setImageBitmap(bitmap); // 设置图像视图的位图对象
        }
    }
}

XML文件

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    <Button
        android:id="@+id/btn_recorder"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="打开摄像机"
        android:textColor="@color/black"
        android:textSize="17sp" />
    <TextView
        android:id="@+id/tv_video"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingLeft="5dp"
        android:textColor="@color/black"
        android:textSize="17sp" />
    <RelativeLayout
        android:id="@+id/rl_video"
        android:layout_width="match_parent"
        android:layout_height="300dp"
        android:visibility="gone">
        <ImageView
            android:id="@+id/iv_video"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:scaleType="fitCenter" />
        <ImageView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:scaleType="fitCenter"
            android:src="@drawable/play_video" />
    </RelativeLayout>
</LinearLayout>

二、从视频库中选取视频

系统自带的相册通常既保存图片又保存视频,这意味着用户能够从中选择已有的视频,此时相册相当于系统视频库,正如App可以跳到系统 相册选择图片那样,App也能跳到系统视频库选择视频,不同之处在于,打开视频库之前需要指定数据类型为视频,这样系统才知道该去浏览视频库了

与图片类似,选择单个视频与多个视频的回调方法是不一样的,此处不再赘述

效果如下

此处可以选择拍摄或选择已有文件

拍摄画面如下 连接真机食用效果更佳~~~

代码如下

Java类

package com.example.chapter13;
import android.content.ClipData;
import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import com.example.chapter13.util.MediaUtil;
public class VideoChooseActivity extends AppCompatActivity implements View.OnClickListener {
    private final static String TAG = "VideoChooseActivity";
    private int CHOOSE_CODE = 3; // 只在视频库挑选图片的请求码
    private int COMBINE_CODE = 4; // 既可录像获得现场视频、也可在视频库挑选已有视频的请求码
    private TextView tv_video;
    private RelativeLayout rl_video;
    private ImageView iv_video;
    private Uri mVideoUri; // 视频文件的路径对象
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_video_choose);
        tv_video = findViewById(R.id.tv_video);
        rl_video = findViewById(R.id.rl_video);
        iv_video = findViewById(R.id.iv_video);
        findViewById(R.id.btn_choose).setOnClickListener(this);
        findViewById(R.id.btn_combine).setOnClickListener(this);
        findViewById(R.id.rl_video).setOnClickListener(this);
    }
    @Override
    public void onClick(View v) {
        if (v.getId() == R.id.btn_choose) {
            // 创建一个内容获取动作的意图(准备跳到系统视频库)
            Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
            intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true); // 是否允许多选
            intent.setType("video/*"); // 类型为视频
            startActivityForResult(intent, CHOOSE_CODE); // 打开系统视频库
        } else if (v.getId() == R.id.btn_combine) {
            openSelectDialog(); // 打开选择对话框(要录像还是去视频库)
        } else if (v.getId() == R.id.rl_video) {
            // 创建一个内容获取动作的意图(准备跳到系统播放器)
            Intent intent = new Intent(Intent.ACTION_VIEW);
            intent.setDataAndType(mVideoUri, "video/*"); // 类型为视频
            startActivity(intent); // 打开系统的视频播放器
        }
    }
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
        super.onActivityResult(requestCode, resultCode, intent);
        if (resultCode==RESULT_OK && requestCode==CHOOSE_CODE) { // 从视频库回来
            if (intent.getData() != null) { // 选择一个视频
                Uri uri = intent.getData(); // 获得已选择视频的路径对象
                showVideoFrame(uri); // 显示视频的某帧图片
            } else if (intent.getClipData() != null) { // 选择多个视频
                ClipData videos = intent.getClipData(); // 获取剪切板数据
                if (videos.getItemCount() > 0) { // 至少选择了一个文件
                    Uri uri = videos.getItemAt(0).getUri(); // 取第一个视频
                    showVideoFrame(uri); // 显示视频的某帧图片
                }
            }
        }
        if (resultCode==RESULT_OK && requestCode==COMBINE_CODE) { // 从混合选择对话框回来
            if (intent.getData() != null) { // 录像或者从视频库选择一个视频
                Uri uri = intent.getData(); // 获得已选择视频的路径对象
                showVideoFrame(uri); // 显示视频的某帧图片
            }
        }
    }
    // 打开选择对话框(要录像还是去视频库)
    private void openSelectDialog() {
        // 声明摄像机的录像行为
        Intent recordIntent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
        Intent[] intentArray = new Intent[] { recordIntent };
        // 声明视频库的打开行为
        Intent videoIntent = new Intent(Intent.ACTION_GET_CONTENT);
        videoIntent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, false); // 是否允许多选
        videoIntent.setType("video/*"); // 类型为视频
        // 弹出含摄像机和视频库在内的列表对话框
        Intent chooserIntent = new Intent(Intent.ACTION_CHOOSER);
        chooserIntent.putExtra(Intent.EXTRA_TITLE, "请录像或选择视频");
        chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentArray);
        chooserIntent.putExtra(Intent.EXTRA_INTENT, videoIntent);
        // 在页面底部弹出多种选择方式的列表对话框
        startActivityForResult(Intent.createChooser(chooserIntent, "选择视频"), COMBINE_CODE);
    }
    // 显示视频的某帧图片
    private void showVideoFrame(Uri uri) {
        mVideoUri = uri;
        tv_video.setText("你选中的视频地址为:"+uri.toString());
        rl_video.setVisibility(View.VISIBLE);
        // 获取视频文件的某帧图片
        Bitmap bitmap = MediaUtil.getOneFrame(this, uri);
        iv_video.setImageBitmap(bitmap); // 设置图像视图的位图对象
    }
}

XML文件

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
        <Button
            android:id="@+id/btn_choose"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="打开视频库选取视频"
            android:textColor="@color/black"
            android:textSize="16sp" />
        <Button
            android:id="@+id/btn_combine"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="录像或从视频库选取"
            android:textColor="@color/black"
            android:textSize="16sp" />
    </LinearLayout>
    <TextView
        android:id="@+id/tv_video"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingLeft="5dp"
        android:textColor="@color/black"
        android:textSize="17sp" />
    <RelativeLayout
        android:id="@+id/rl_video"
        android:layout_width="match_parent"
        android:layout_height="300dp"
        android:visibility="gone">
        <ImageView
            android:id="@+id/iv_video"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:scaleType="fitCenter" />
        <ImageView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:scaleType="fitCenter"
            android:src="@drawable/play_video" />
    </RelativeLayout>
</LinearLayout>

创作不易 觉得有帮助请点赞关注收藏~~~

相关文章
|
8月前
|
容器
HarmonyOS NEXT仓颉开发语言实战案例:外卖App
仓颉语言实战分享,教你如何用仓颉开发外卖App界面。内容包括页面布局、导航栏自定义、搜索框实现、列表模块构建等,附完整代码示例。轻松掌握Scroll、List等组件使用技巧,提升HarmonyOS应用开发能力。
|
4月前
|
移动开发 前端开发 Android开发
【02】建立各项目录和页面标准化产品-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
【02】建立各项目录和页面标准化产品-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
470 12
【02】建立各项目录和页面标准化产品-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
|
4月前
|
移动开发 JavaScript 应用服务中间件
【06】优化完善落地页样式内容-精度优化-vue加vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
【06】优化完善落地页样式内容-精度优化-vue加vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
444 5
【06】优化完善落地页样式内容-精度优化-vue加vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
|
4月前
|
移动开发 Rust JavaScript
【01】首页建立-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
【01】首页建立-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
882 4
【01】首页建立-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
HarmonyOS NEXT仓颉开发语言实战案例:电影App
周末好!本文分享使用仓颉语言重构ArkTS实现的电影App案例,对比两者在UI布局、组件写法及语法差异。内容包括页面结构、列表分组、分类切换与电影展示等。通过代码演示仓颉在HarmonyOS开发中的应用。##仓颉##ArkTS##HarmonyOS开发
|
8月前
|
容器
HarmonyOS NEXT仓颉开发语言实战案例:健身App
本期分享一个健身App首页的布局实现,顶部采用Stack容器实现重叠背景与偏移效果,列表部分使用List结合Scroll实现可滚动内容。代码结构清晰,适合学习HarmonyOS布局技巧。
HarmonyOS NEXT仓颉开发语言实战案例:小而美的旅行App
本文分享了一个旅行App首页的设计与实现,使用List容器搭配Row、Column布局完成个人信息、功能列表及推荐模块的排版,详细展示了HarmonyOS下的界面构建技巧。
|
8月前
|
容器
HarmonyOS NEXT仓颉开发语言实战案例:银行App
仓颉语言银行App项目分享,页面布局采用List容器,实现沉浸式体验与模块化设计。顶部资产模块结合Stack与Row布局,背景图与内容分离,代码清晰易懂;功能按钮部分通过负边距实现上移效果,圆角仅保留顶部;热门推荐使用header组件,结构更规范。整体代码风格与ArkTS相似,但细节更灵活,适合金融类应用开发。
|
4月前
|
移动开发 Android开发
【03】建立隐私关于等相关页面和内容-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
【03】建立隐私关于等相关页面和内容-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
237 0
|
9月前
|
Android开发 Windows
Android studio 报错Connect to 127.0.0.1:8888 [/127.0.0.1] failed: Connection refused: connect(已解决)
这是一篇关于解决Android Studio报错“Connect to 127.0.0.1:8888 failed: Connection refused”的文章。问题通常因系统代理设置被Android Studio自动保存导致。解决方法是找到系统中Android Studio使用的gradle.properties文件(位于Windows的C:\Users\你的电脑用户名\.gradle或Mac的/Users/.{你的用户目录}/.gradle),删除或注释掉多余的代理配置后保存并重新Sync项目。希望此经验能帮助快速解决同类问题!
1499 36