Android Studio App开发中使用录音机、MediaRecorder录制音频和MediaPlayer播放音频讲解及实战(附源码)

简介: Android Studio App开发中使用录音机、MediaRecorder录制音频和MediaPlayer播放音频讲解及实战(附源码)

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

一、使用录音机录制音频

手机有自带的系统相机,也有自带的系统录音机,只要在调用startActivityForResult之前指定该动作,就会自动跳转到系统的录音机界面 效果如下

当然这里最好连接真机测试 模拟机好像没有录音机 总之调试有点麻烦

代码如下

Java类

package com.example.chapter13;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
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.TextView;
import com.example.chapter13.util.DateUtil;
import com.example.chapter13.util.FileUtil;
public class AudioRecordActivity extends AppCompatActivity implements View.OnClickListener {
    private final static String TAG = "AudioRecordActivity";
    private int RECORDER_CODE = 1; // 录制操作的请求码
    private TextView tv_audio;
    private ImageView iv_audio; // 该图标充当播放按钮
    private Uri mAudioUri; // 音频文件的uri路径
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_audio_record);
        tv_audio = findViewById(R.id.tv_audio);
        iv_audio = findViewById(R.id.iv_audio);
        findViewById(R.id.btn_recorder).setOnClickListener(this);
        findViewById(R.id.iv_audio).setOnClickListener(this);
    }
    @Override
    public void onClick(View v) {
        if (v.getId() == R.id.btn_recorder) {
            // 下面打开系统自带的录音机
            Intent intent = new Intent(MediaStore.Audio.Media.RECORD_SOUND_ACTION);
            registerForActivityResult(intent, RECORDER_CODE); // 跳到录音机页面
        } else if (v.getId() == R.id.iv_audio) {
            // 下面打开系统自带的收音机
            Intent intent = new Intent(Intent.ACTION_VIEW);
            intent.setDataAndType(mAudioUri, "audio/*"); // 类型为音频
            startActivity(intent); // 跳到收音机页面
        }
    }
    private void registerForActivityResult(Intent intent, int recorder_code) {
    }
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
        super.onActivityResult(requestCode, resultCode, intent);
        if (resultCode==RESULT_OK && requestCode==RECORDER_CODE){
            mAudioUri = intent.getData(); // 获得录制好的音频uri
            String filePath = String.format("%s/%s.mp3",
                    getExternalFilesDir(Environment.DIRECTORY_MUSIC), "audio_"+ DateUtil.getNowDateTime());
            FileUtil.saveFileFromUri(this, mAudioUri, filePath); // 保存为临时文件
            tv_audio.setText("录制完成的音频地址为:"+mAudioUri.toString());
            iv_audio.setVisibility(View.VISIBLE);
        }
    }
}

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_audio"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingLeft="5dp"
        android:textColor="@color/black"
        android:textSize="17sp" />
    <ImageView
        android:id="@+id/iv_audio"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:scaleType="fitCenter"
        android:src="@drawable/play_audio"
        android:visibility="gone" />
</LinearLayout>

二、利用MediaPlayer播放音频

尽管让App跳到收音机界面就能播放音频,但是通常App都不希望用户离开自身页面,何况播音本来就是一个小功能,完全可以一边播放音频一边操作界面,若想在App内部自己播音,便用到了媒体播放器MediaPlayer,并且要找到音频文件的路径。

MediaPlayer常用方法如下

1:reset 重置播放器

2:prepare 准备播放

3:start 开始播放

4:pause 暂停播放

5:stop 停止播放

6:create 创建uri指定的播放器

7:setDataSource  设置播放器数据来源的文件路径

应用场景主要有以下两个1:播放指定音频文件

2:退出页面时准备释放媒体资源

代码如下

Java类

package com.example.chapter13;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.database.Cursor;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.provider.MediaStore;
import android.util.Log;
import android.view.View;
import com.example.chapter13.adapter.AudioRecyclerAdapter;
import com.example.chapter13.bean.MediaInfo;
import com.example.chapter13.util.FileUtil;
import com.example.chapter13.widget.RecyclerExtras;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
public class AudioPlayActivity extends AppCompatActivity implements RecyclerExtras.OnItemClickListener {
    private final static String TAG = "AudioPlayActivity";
    private RecyclerView rv_audio; // 音频列表的循环视图
    private List<MediaInfo> mAudioList = new ArrayList<MediaInfo>(); // 音频列表
    private Uri mAudioUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; // 音频库的Uri
    private String[] mAudioColumn = new String[]{ // 媒体库的字段名称数组
            MediaStore.Audio.Media._ID, // 编号
            MediaStore.Audio.Media.TITLE, // 标题
            MediaStore.Audio.Media.DURATION, // 播放时长
            MediaStore.Audio.Media.SIZE, // 文件大小
            MediaStore.Audio.Media.DATA}; // 文件路径
    private AudioRecyclerAdapter mAdapter; // 音频列表的适配器
    private MediaPlayer mMediaPlayer = new MediaPlayer(); // 媒体播放器
    private Timer mTimer = new Timer(); // 计时器
    private int mLastPosition = -1; // 上次播放的音频序号
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_audio_play);
        rv_audio = findViewById(R.id.rv_audio);
        loadAudioList(); // 加载音频列表
        showAudioList(); // 显示音频列表
    }
    // 加载音频列表
    private void loadAudioList() {
        mAudioList.clear(); // 清空音频列表
        // 通过内容解析器查询音频库,并返回结果集的游标。记录结果按照修改时间降序返回
        Cursor cursor = getContentResolver().query(mAudioUri, mAudioColumn,
                null, null, "date_modified desc");
        if (cursor != null) {
            // 下面遍历结果集,并逐个添加到音频列表。简单起见只挑选前十个音频
            for (int i=0; i<10 && cursor.moveToNext(); i++) {
                MediaInfo audio = new MediaInfo(); // 创建一个音频信息对象
                audio.setId(cursor.getLong(0)); // 设置音频编号
                audio.setTitle(cursor.getString(1)); // 设置音频标题
                audio.setDuration(cursor.getInt(2)); // 设置音频时长
                audio.setSize(cursor.getLong(3)); // 设置音频大小
                audio.setPath(cursor.getString(4)); // 设置音频路径
                Log.d(TAG, audio.getTitle() + " " + audio.getDuration() + " " + audio.getSize() + " " + audio.getPath());
                if (!FileUtil.checkFileUri(this, audio.getPath())) {
                    i--;
                    continue;
                }
                mAudioList.add(audio); // 添加至音频列表
            }
            cursor.close(); // 关闭数据库游标
        }
    }
    // 显示音频列表
    private void showAudioList() {
        // 创建一个水平方向的线性布局管理器
        LinearLayoutManager manager = new LinearLayoutManager(this, RecyclerView.VERTICAL, false);
        rv_audio.setLayoutManager(manager); // 设置循环视图的布局管理器
        mAdapter = new AudioRecyclerAdapter(this, mAudioList); // 创建音频列表的线性适配器
        mAdapter.setOnItemClickListener(this); // 设置线性列表的点击监听器
        rv_audio.setAdapter(mAdapter); // 设置循环视图的列表适配器
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        mTimer.cancel(); // 取消计时器
        if (mMediaPlayer.isPlaying()) { // 是否正在播放
            mMediaPlayer.stop(); // 结束播放
        }
        mMediaPlayer.release(); // 释放媒体播放器
    }
    @Override
    public void onItemClick(View view, final int position) {
        if (mLastPosition!=-1 && mLastPosition!=position) {
            MediaInfo last_audio = mAudioList.get(mLastPosition);
            last_audio.setProgress(-1); // 当前进度设为-1表示没在播放
            mAudioList.set(mLastPosition, last_audio);
            mAdapter.notifyItemChanged(mLastPosition); // 刷新此处的列表项
        }
        mLastPosition = position;
        final MediaInfo audio = mAudioList.get(position);
        Log.d(TAG, "onItemClick position="+position+",audio.getPath()="+audio.getPath());
        mTimer.cancel(); // 取消计时器
        mMediaPlayer.reset(); // 重置媒体播放器
        // mMediaPlayer.setVolume(0.5f, 0.5f); // 设置音量,可选
        mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); // 设置音频流的类型为音乐
        try {
            mMediaPlayer.setDataSource(audio.getPath()); // 设置媒体数据的文件路径
            mMediaPlayer.prepare(); // 媒体播放器准备就绪
            mMediaPlayer.start(); // 媒体播放器开始播放
        } catch (Exception e) {
            e.printStackTrace();
        }
        mTimer = new Timer(); // 创建一个计时器
        mTimer.schedule(new TimerTask() {
                @Override
                public void run() {
                    audio.setProgress(mMediaPlayer.getCurrentPosition()); // 设置进度条的当前进度
                    mAudioList.set(position, audio);
                    // 界面刷新操作需要在主线程执行,故而向处理器发送消息,由处理器在主线程更新界面
                    mHandler.sendEmptyMessage(position);
                    Log.d(TAG, "CurrentPosition="+mMediaPlayer.getCurrentPosition()+",position="+position);
                }
                }, 0, 1000); // 计时器每隔一秒就更新进度条上的播放进度
    }
    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            mAdapter.notifyItemChanged(msg.what); // 刷新此处的列表项
        }
    };
}

XML文件

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="5dp">
    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:text="点击音频列表开始播放"
        android:textColor="@color/black"
        android:textSize="17sp" />
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:layout_margin="2dp"
        android:orientation="horizontal">
        <TextView
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="3"
            android:gravity="left|center"
            android:text="音频名称"
            android:textColor="@color/black"
            android:textSize="15sp" />
        <TextView
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="right|center"
            android:text="总时长"
            android:textColor="@color/black"
            android:textSize="15sp" />
    </LinearLayout>
    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/rv_audio"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />
</LinearLayout>

三、利用MediaRecoder录制音频

Android提供了媒体录制器MediaRecoder,它既能录制音频也能录制视频,它可以在当前页面直接录音,而不必跳到系统自带的录音机界面。常用方法与MediaPlayer基本一致

它的基本应用场景也只有两个1:开始录制媒体文件

2:停止录制媒体文件

其中录制音频的场景需要经历以下步骤

重置录制器-设置媒体文件的路径-准备录制-开始录制

效果如下 可以自行选择编码格式和录制时长

代码如下

Java类

package com.example.chapter13;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.MediaRecorder;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import com.example.chapter13.util.MediaUtil;
import java.util.Timer;
import java.util.TimerTask;
public class MediaRecorderActivity extends AppCompatActivity implements View.OnClickListener, MediaRecorder.OnInfoListener {
    private static final String TAG = "MediaRecorderActivity";
    private Button btn_record;
    private LinearLayout ll_progress;
    private ProgressBar pb_record; // 声明一个进度条对象
    private TextView tv_progress;
    private ImageView iv_audio; // 该图标充当播放按钮
    private MediaRecorder mMediaRecorder = new MediaRecorder(); // 媒体录制器
    private boolean isRecording = false; // 是否正在录制
    private int mAudioEncoder; // 音频编码
    private int mOutputFormat; // 输出格式
    private int mDuration; // 录制时长
    private String mRecordFilePath; // 录制文件的保存路径
    private Timer mTimer = new Timer(); // 计时器
    private int mTimeCount; // 时间计数
    private MediaPlayer mMediaPlayer = new MediaPlayer(); // 媒体播放器
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_media_recorder);
        btn_record = findViewById(R.id.btn_record);
        ll_progress = findViewById(R.id.ll_progress);
        pb_record = findViewById(R.id.pb_record);
        tv_progress = findViewById(R.id.tv_progress);
        iv_audio = findViewById(R.id.iv_audio);
        btn_record.setOnClickListener(this);
        iv_audio.setOnClickListener(this);
        initEncoderSpinner(); // 初始化音频编码的下拉框
        initFormatSpinner(); // 初始化输出格式的下拉框
        initDurationSpinner(); // 初始化录制时长的下拉框
    }
    // 初始化音频编码的下拉框
    private void initEncoderSpinner() {
        ArrayAdapter<String> encoderAdapter = new ArrayAdapter<String>(this,
                R.layout.item_select, encoderDescArray);
        Spinner sp_encoder = findViewById(R.id.sp_encoder);
        sp_encoder.setPrompt("请选择音频编码"); // 设置下拉框的标题
        sp_encoder.setAdapter(encoderAdapter); // 设置下拉框的数组适配器
        // 给下拉框设置选择监听器,一旦用户选中某一项,就触发监听器的onItemSelected方法
        sp_encoder.setOnItemSelectedListener(new EncoderSelectedListener());
        sp_encoder.setSelection(0); // 设置下拉框默认显示第一项
    }
    private String[] encoderDescArray = {
            "默认编码",
            "窄带编码",
            "宽带编码",
            "低复杂度的高级编码",
            "高效率的高级编码",
            "增强型低延时的高级编码"
    };
    private int[] encoderArray = {
            MediaRecorder.AudioEncoder.DEFAULT,
            MediaRecorder.AudioEncoder.AMR_NB,
            MediaRecorder.AudioEncoder.AMR_WB,
            MediaRecorder.AudioEncoder.AAC,
            MediaRecorder.AudioEncoder.HE_AAC,
            MediaRecorder.AudioEncoder.AAC_ELD
    };
    class EncoderSelectedListener implements AdapterView.OnItemSelectedListener {
        public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
            mAudioEncoder = encoderArray[arg2];
        }
        public void onNothingSelected(AdapterView<?> arg0) {}
    }
    // 初始化输出格式的下拉框
    private void initFormatSpinner() {
        ArrayAdapter<String> formatAdapter = new ArrayAdapter<String>(this,
                R.layout.item_select, formatDescArray);
        Spinner sp_format = findViewById(R.id.sp_format);
        sp_format.setPrompt("请选择输出格式"); // 设置下拉框的标题
        sp_format.setAdapter(formatAdapter); // 设置下拉框的数组适配器
        sp_format.setSelection(0); // 设置下拉框默认显示第一项
        // 给下拉框设置选择监听器,一旦用户选中某一项,就触发监听器的onItemSelected方法
        sp_format.setOnItemSelectedListener(new FormatSelectedListener());
    }
    private String[] formatDescArray = {
            "默认格式",
            "窄带格式",
            "宽带格式",
            "高级的音频传输流格式"
    };
    private int[] formatArray = {
            MediaRecorder.OutputFormat.DEFAULT,
            MediaRecorder.OutputFormat.AMR_NB,
            MediaRecorder.OutputFormat.AMR_WB,
            MediaRecorder.OutputFormat.AAC_ADTS
    };
    class FormatSelectedListener implements AdapterView.OnItemSelectedListener {
        public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
            mOutputFormat = formatArray[arg2];
        }
        public void onNothingSelected(AdapterView<?> arg0) {}
    }
    // 初始化录制时长的下拉框
    private void initDurationSpinner() {
        ArrayAdapter<String> durationAdapter = new ArrayAdapter<String>(this,
                R.layout.item_select, durationDescArray);
        Spinner sp_duration = findViewById(R.id.sp_duration);
        sp_duration.setPrompt("请选择录制时长"); // 设置下拉框的标题
        sp_duration.setAdapter(durationAdapter); // 设置下拉框的数组适配器
        sp_duration.setSelection(0); // 设置下拉框默认显示第一项
        // 给下拉框设置选择监听器,一旦用户选中某一项,就触发监听器的onItemSelected方法
        sp_duration.setOnItemSelectedListener(new DurationSelectedListener());
    }
    private String[] durationDescArray = {"5秒", "10秒", "20秒", "30秒", "60秒"};
    private int[] durationArray = {5, 10, 20, 30, 60};
    class DurationSelectedListener implements AdapterView.OnItemSelectedListener {
        public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
            mDuration = durationArray[arg2];
        }
        public void onNothingSelected(AdapterView<?> arg0) {}
    }
    @Override
    public void onClick(View v) {
        if (v.getId() == R.id.btn_record) {
            if (!isRecording) { // 未在录音
                startRecord(); // 开始录音
            } else { // 正在录音
                stopRecord(); // 停止录音
            }
        } else if (v.getId() == R.id.iv_audio) {
            mMediaPlayer.reset(); // 重置媒体播放器
            mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); // 设置音频流的类型为音乐
            try {
                mMediaPlayer.setDataSource(mRecordFilePath); // 设置媒体数据的文件路径
                mMediaPlayer.prepare(); // 媒体播放器准备就绪
                mMediaPlayer.start(); // 媒体播放器开始播放
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    // 开始录音
    private void startRecord() {
        Log.d(TAG, "startRecord mAudioEncoder="+mAudioEncoder+", mOutputFormat="+mOutputFormat+", mDuration="+mDuration);
        ll_progress.setVisibility(View.VISIBLE);
        isRecording = !isRecording;
        btn_record.setText("停止录制");
        pb_record.setMax(mDuration); // 设置进度条的最大值
        mTimeCount = 0; // 时间计数清零
        mTimer = new Timer(); // 创建一个计时器
        mTimer.schedule(new TimerTask() {
            @Override
            public void run() {
                pb_record.setProgress(mTimeCount); // 设置进度条的当前进度
                tv_progress.setText(MediaUtil.formatDuration(mTimeCount*1000));
                mTimeCount++;
            }
        }, 0, 1000); // 计时器每隔一秒就更新进度条上的录制进度
        // 获取本次录制的媒体文件路径
        mRecordFilePath = MediaUtil.getRecordFilePath(this, "RecordAudio", ".amr");
        // 下面是媒体录制器的处理代码
        mMediaRecorder.reset(); // 重置媒体录制器
        mMediaRecorder.setOnInfoListener(this); // 设置媒体录制器的信息监听器
        mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); // 设置音频源为麦克风
        mMediaRecorder.setOutputFormat(mOutputFormat); // 设置媒体的输出格式。该方法要先于setAudioEncoder调用
        mMediaRecorder.setAudioEncoder(mAudioEncoder); // 设置媒体的音频编码器
        // mMediaRecorder.setAudioSamplingRate(8); // 设置媒体的音频采样率。可选
        // mMediaRecorder.setAudioChannels(2); // 设置媒体的音频声道数。可选
        // mMediaRecorder.setAudioEncodingBitRate(1024); // 设置音频每秒录制的字节数。可选
        mMediaRecorder.setMaxDuration(mDuration * 1000); // 设置媒体的最大录制时长
        // mMediaRecorder.setMaxFileSize(1024*1024*10); // 设置媒体的最大文件大小
        // setMaxFileSize与setMaxDuration设置其一即可
        mMediaRecorder.setOutputFile(mRecordFilePath); // 设置媒体文件的保存路径
        try {
            mMediaRecorder.prepare(); // 媒体录制器准备就绪
            mMediaRecorder.start(); // 媒体录制器开始录制
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    // 停止录音
    private void stopRecord() {
        isRecording = !isRecording;
        btn_record.setText("开始录制");
        mTimer.cancel(); // 取消定时器
        mMediaRecorder.stop(); // 媒体录制器停止录制
    }
    @Override
    public void onInfo(MediaRecorder mr, int what, int extra) {
        // 录制达到最大时长,或者达到文件大小限制,都停止录制
        if (what == MediaRecorder.MEDIA_RECORDER_INFO_MAX_DURATION_REACHED
                || what == MediaRecorder.MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED) {
            stopRecord(); // 停止录音
            iv_audio.setVisibility(View.VISIBLE);
            Toast.makeText(this, "已结束录制,音频文件路径为"+mRecordFilePath, Toast.LENGTH_LONG).show();
        }
    }
    @Override
    protected void onStop() {
        super.onStop();
        if (!TextUtils.isEmpty(mRecordFilePath) && isRecording) {
            stopRecord(); // 停止录音
        }
        if (mMediaPlayer.isPlaying()) { // 如果正在播放
            mMediaPlayer.stop(); // 停止播放
        }
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        mMediaRecorder.release(); // 释放媒体录制器
        mMediaPlayer.release(); // 释放媒体播放器
    }
}

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="40dp"
        android:paddingLeft="5dp">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:gravity="center"
            android:text="音频编码:"
            android:textColor="@color/black"
            android:textSize="17sp" />
        <Spinner
            android:id="@+id/sp_encoder"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="left|center"
            android:spinnerMode="dialog" />
    </LinearLayout>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:paddingLeft="5dp">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:gravity="center"
            android:text="输出格式:"
            android:textColor="@color/black"
            android:textSize="17sp" />
        <Spinner
            android:id="@+id/sp_format"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="left|center"
            android:spinnerMode="dialog" />
    </LinearLayout>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:paddingLeft="5dp">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:gravity="center"
            android:text="录制时长:"
            android:textColor="@color/black"
            android:textSize="17sp" />
        <Spinner
            android:id="@+id/sp_duration"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="left|center"
            android:spinnerMode="dialog" />
    </LinearLayout>
    <Button
        android:id="@+id/btn_record"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="开始录音"
        android:textColor="@color/black"
        android:textSize="17sp" />
    <LinearLayout
        android:id="@+id/ll_progress"
        android:layout_width="match_parent"
        android:layout_height="30dp"
        android:paddingLeft="5dp"
        android:paddingRight="5dp"
        android:orientation="horizontal"
        android:visibility="gone">
        <ProgressBar
            android:id="@+id/pb_record"
            style="?android:attr/progressBarStyleHorizontal"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="4" />
        <TextView
            android:id="@+id/tv_progress"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="right|center"
            android:textColor="@color/black"
            android:textSize="15sp" />
    </LinearLayout>
    <ImageView
        android:id="@+id/iv_audio"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:scaleType="fitCenter"
        android:src="@drawable/play_audio"
        android:visibility="gone" />
</LinearLayout>

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

相关文章
|
3月前
|
开发工具
uniapp, 短剧视频类App实现参考,支持滑动播放,仿抖音 仿陌陌 短视频 无限滑动播放 视频流
阿里云点播服务web播放器sdk,短剧视频类App实现参考。仿抖音 仿陌陌 短视频 无限滑动播放 视频流。无uniapp video 原生组件的层级、遮挡、覆盖问题,适合与不同功能视图组合使用,实现丰富的应用功能。
uniapp, 短剧视频类App实现参考,支持滑动播放,仿抖音 仿陌陌 短视频 无限滑动播放 视频流
|
4月前
|
开发工具 uml git
AOSP源码下载方法,解决repo sync错误:android-13.0.0_r82
本文分享了下载AOSP源码的方法,包括如何使用repo工具和处理常见的repo sync错误,以及配置Python环境以确保顺利同步特定版本的AOSP代码。
580 0
AOSP源码下载方法,解决repo sync错误:android-13.0.0_r82
|
4月前
|
Java Android开发 芯片
使用Android Studio导入Android源码:基于全志H713 AOSP,方便解决编译、编码问题
本文介绍了如何将基于全志H713芯片的AOSP Android源码导入Android Studio以解决编译和编码问题,通过操作步骤的详细说明,展示了在Android Studio中利用代码提示和补全功能快速定位并修复编译错误的方法。
188 0
使用Android Studio导入Android源码:基于全志H713 AOSP,方便解决编译、编码问题
|
4月前
|
开发工具 Android开发 git
全志H713 Android 11 :给AOSP源码,新增一个Product
本文介绍了在全志H713 Android 11平台上新增名为myboard的产品的步骤,包括创建新的device目录、编辑配置文件、新增内核配置、记录差异列表以及编译kernel和Android系统的详细过程。
201 0
|
Android开发
我的Android进阶之旅------&gt;介绍一款集录制与剪辑为一体的屏幕GIF 动画制作工具 GifCam
由于上一篇文章:我的Android进阶之旅------>Android之动画之Frame Animation实例 中展示的是Frame动画效果,但是之前我是将图片截取下来,不好说明确切的动画过程,因此今天百度了一下gif动画的制作工具,找到了这款不错的软件GifCam。
1295 0
|
29天前
|
开发框架 前端开发 Android开发
安卓与iOS开发中的跨平台策略
在移动应用开发的战场上,安卓和iOS两大阵营各据一方。随着技术的演进,跨平台开发框架成为开发者的新宠,旨在实现一次编码、多平台部署的梦想。本文将探讨跨平台开发的优势与挑战,并分享实用的开发技巧,帮助开发者在安卓和iOS的世界中游刃有余。
|
16天前
|
搜索推荐 前端开发 API
探索安卓开发中的自定义视图:打造个性化用户界面
在安卓应用开发的广阔天地中,自定义视图是一块神奇的画布,让开发者能够突破标准控件的限制,绘制出独一无二的用户界面。本文将带你走进自定义视图的世界,从基础概念到实战技巧,逐步揭示如何在安卓平台上创建和运用自定义视图来提升用户体验。无论你是初学者还是有一定经验的开发者,这篇文章都将为你打开新的视野,让你的应用在众多同质化产品中脱颖而出。
40 19
|
29天前
|
IDE Java 开发工具
移动应用与系统:探索Android开发之旅
在这篇文章中,我们将深入探讨Android开发的各个方面,从基础知识到高级技术。我们将通过代码示例和案例分析,帮助读者更好地理解和掌握Android开发。无论你是初学者还是有经验的开发者,这篇文章都将为你提供有价值的信息和技巧。让我们一起开启Android开发的旅程吧!
|
16天前
|
JSON Java API
探索安卓开发:打造你的首个天气应用
在这篇技术指南中,我们将一起潜入安卓开发的海洋,学习如何从零开始构建一个简单的天气应用。通过这个实践项目,你将掌握安卓开发的核心概念、界面设计、网络编程以及数据解析等技能。无论你是初学者还是有一定基础的开发者,这篇文章都将为你提供一个清晰的路线图和实用的代码示例,帮助你在安卓开发的道路上迈出坚实的一步。让我们一起开始这段旅程,打造属于你自己的第一个安卓应用吧!
41 14
|
19天前
|
Java Linux 数据库
探索安卓开发:打造你的第一款应用
在数字时代的浪潮中,每个人都有机会成为创意的实现者。本文将带你走进安卓开发的奇妙世界,通过浅显易懂的语言和实际代码示例,引导你从零开始构建自己的第一款安卓应用。无论你是编程新手还是希望拓展技术的开发者,这篇文章都将为你打开一扇门,让你的创意和技术一起飞扬。
下一篇
DataWorks