Android使用MediaRecorder類實(shí)現(xiàn)視頻和音頻錄制功能
一、前期基礎(chǔ)知識(shí)儲(chǔ)備
Android提供了MediaRecorder這一個(gè)類來實(shí)現(xiàn)視頻和音頻的錄制。
由官方配圖可知,MediaRecorder用于錄制視頻時(shí)需要調(diào)用一系列的API來設(shè)置和錄制相關(guān)的配置,而且調(diào)用方法的順序是固定的,必須按照這個(gè)順序進(jìn)行API調(diào)用才能正確利用手機(jī)攝像頭實(shí)現(xiàn)錄像功能。
調(diào)用MediaRecorder的錄像API順序如下:
1)Open Camera - Use the Camera.open() to get an instance of the camera object.
2)Connect Preview - Prepare a live camera image preview by connecting a SurfaceView to the camera using Camera.setPreviewDisplay().
3)Start Preview - Call Camera.startPreview() to begin displaying the live camera images.
4)Start Recording Video - The following steps must be completed in order to successfully record video:
a.Unlock the Camera - Unlock the camera for use by MediaRecorder by calling Camera.unlock().
b.Configure MediaRecorder - Call in the following MediaRecorder methods in this order:
setCamera() - Set the camera to be used for video capture,綁定Camera進(jìn)行視頻錄制。
setAudioSource() - Set the audio source,設(shè)置音頻源。
setVideoSource() - Set the video source,設(shè)置視頻源。
setProfile() - Set the video output format and encoding,錄制效果的配置。
setOutputFile() - Set the output file, 設(shè)置錄制好的文件存儲(chǔ)位置。
setPreviewDisplay() - Connect Preview,設(shè)置預(yù)覽效果。
c.Prepare MediaRecorder- Prepare the MediaRecorder with provided configuration settings by calling MediaRecorder.prepare().
d.Start MediaRecorder - Start recording video by calling MediaRecorder.start().
停止錄像時(shí)調(diào)用的API順序如下:
1)Stop MediaRecorder - Stop recording video by calling MediaRecorder.stop().
2)Reset MediaRecorder - Optionally, remove the configuration settings from the recorder by calling MediaRecorder.reset().
3)Release MediaRecorder - Release the MediaRecorder by calling MediaRecorder.release().
4)Lock the Camera - Lock the camera so that future MediaRecorder sessions can use it by calling Camera.lock().
5)Stop the Preview - When your activity has finished using the camera, stop the preview using Camera.stopPreview().
6)Release Camera - Release the camera so that other applications can use it by calling Camera.release().
二、上代碼,具體實(shí)現(xiàn)錄制視頻和視頻播放功能
這里調(diào)用MediaRecorder的API實(shí)現(xiàn)視頻錄制功能并借用MediaPlayer多媒體播放類實(shí)現(xiàn)錄制好的視頻播放。
(1)布局文件如下,非常簡(jiǎn)單兩個(gè)按鈕下放置一個(gè)SurfaceView;
<LinearLayout 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/record_btn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="15dp" android:layout_weight="1" android:text="record" /> <Button android:id="@+id/play_btn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginRight="15dp" android:layout_weight="1" android:text="play" /> </LinearLayout> <SurfaceView android:id="@+id/surface_view" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginBottom="20dp" /> </LinearLayout>
(2)相機(jī)錄像前的準(zhǔn)備代碼;
/* * 相機(jī)預(yù)覽前的準(zhǔn)備工作代碼 單獨(dú)抽出來 * */ private boolean prepareVideoRecorder() throws IOException { if (mMediaRecorder == null) { mMediaRecorder = new MediaRecorder(); mMediaRecorder.reset(); } /*camera相關(guān)設(shè)置部分*/ mCamera = Camera.open(0);//Camera.CameraInfo.CAMERA_FACING_BACK if (mCamera != null) { //設(shè)置旋轉(zhuǎn)角度,順時(shí)針方向,因?yàn)槟J(rèn)是逆向90度的,這樣圖像就是正常顯示了 mCamera.setDisplayOrientation(90); mCamera.unlock(); mMediaRecorder.setCamera(mCamera); } /*recorder設(shè)置部分*/ mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER); mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA); mMediaRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH)); mMediaRecorder.setOutputFile(getOutputMediaFile()); mMediaRecorder.setPreviewDisplay(mSurfaceView.getHolder().getSurface()); mMediaRecorder.prepare(); return true; }
(3)創(chuàng)建錄像文件存儲(chǔ)位置代碼;
/* * 獲取手機(jī)外部存儲(chǔ)路徑 * */ private String getOutputFile() { File mediaFile = null; boolean OutputExist = Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED); if (OutputExist) { mediaFile = Environment.getExternalStorageDirectory(); return mediaFile.toString(); } return null; } /* * 獲取錄制視頻的日期 作為存儲(chǔ)文件路徑一部分 * */ private String getDate() { Log.d(TAG, "獲取錄制視頻的日期 "); Calendar ca = Calendar.getInstance(); int year = ca.get(Calendar.YEAR); // 獲取年份 int month = ca.get(Calendar.MONTH); // 獲取月份 int day = ca.get(Calendar.DATE); // 獲取日 String date = "" + year + "_" + (month + 1) + "_" + day; return date; } /* *創(chuàng)建視頻存儲(chǔ)文件夾 錄制好的視頻存儲(chǔ)在手機(jī)外部存儲(chǔ)中 以錄像時(shí)間+mp4格式命名 * */ private String getOutputMediaFile() { Log.d(TAG, "獲取視頻存儲(chǔ)的位置 "); String mediaPath = getOutputFile(); if (mediaPath != null) { File mediaFile = new File(mediaPath + "/recordVideo"); if (!mediaFile.exists()) { mediaFile.mkdir(); } return mMediaPath = mediaFile.getAbsolutePath() + File.separator + getDate() + ".mp4"; } return null; }
(4)錄制視頻結(jié)束時(shí)釋放相機(jī)資源;
/* * 錄制視頻結(jié)束時(shí)釋放相機(jī)資源 * */ private void releaseMediaRecorder() { Log.d(TAG, "錄制結(jié)束后釋放資源 "); if (mMediaRecorder != null) { mMediaRecorder.reset(); // clear recorder configuration mMediaRecorder.release(); // release the recorder object mMediaRecorder = null; mCamera.lock(); // lock camera for later use } }
(5)點(diǎn)擊錄制視頻按鈕mRecordBtn開始錄制和再次點(diǎn)擊停止錄制;
private void initBtnClick() { StartRecording(); mPlayBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { if (mMediaPlayer == null) { mMediaPlayer = new MediaPlayer(); mMediaPlayer.reset(); Uri uri = Uri.parse(mMediaPath); mMediaPlayer = MediaPlayer.create(MainActivity.this,uri); mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); mMediaPlayer.setDisplay(mSurfaceHolder); try{ mMediaPlayer.prepare(); }catch (Exception e){ e.printStackTrace(); } mMediaPlayer.start(); } } }); } private void StartRecording(){ mRecordBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { if (!mIsRecord) { try { Log.d(TAG, "首次點(diǎn)擊開始錄像 "); if (prepareVideoRecorder()) { mMediaRecorder.start(); mIsRecord = true; mRecordBtn.setText("stop"); } } catch (IOException e) { e.printStackTrace(); } } else { Log.d(TAG, "再次點(diǎn)擊停止錄像"); mMediaRecorder.stop(); releaseMediaRecorder(); mCamera.lock(); mRecordBtn.setText("record"); mIsRecord = false; if (mCamera != null) { mCamera.release(); mCamera = null; } } } }); }
(6)點(diǎn)擊播放視頻按鈕 mPlayBtn開始播放錄制剛剛錄制好的視頻;
mPlayBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { if (mMediaPlayer == null) { mMediaPlayer = new MediaPlayer(); mMediaPlayer.reset(); Uri uri = Uri.parse(mMediaPath); mMediaPlayer = MediaPlayer.create(MainActivity.this,uri); mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); mMediaPlayer.setDisplay(mSurfaceHolder); try{ mMediaPlayer.prepare(); }catch (Exception e){ e.printStackTrace(); } mMediaPlayer.start(); } } });
(7)針對(duì)6.0以上系統(tǒng)進(jìn)行運(yùn)行時(shí)權(quán)限申請(qǐng)
private void requestCameraAndStoragePermission() { //檢查用戶是否授權(quán) for (int i = 0; i < permissions.length; i++) { if (ContextCompat.checkSelfPermission(MainActivity.this, permissions[i]) != PackageManager.PERMISSION_GRANTED) { //沒有授權(quán)則請(qǐng)求相應(yīng)權(quán)限 ActivityCompat.requestPermissions(MainActivity.this, new String[]{permissions[i]}, 1); } } //利用權(quán)限申請(qǐng)工具類來實(shí)現(xiàn) mPermissionsUtils = PermissionsUtils.getInstance(); mPermissionsUtils.chekPermissions(MainActivity.this,permissions, permissionsResult); } //創(chuàng)建監(jiān)聽權(quán)限的接口對(duì)象 PermissionsUtils.IPermissionsResult permissionsResult = new PermissionsUtils.IPermissionsResult() { @Override public void passPermissons() { //StartRecording(); 注意這里的邏輯 并不是權(quán)限通過了就立即開始錄像了 而是權(quán)限通過了 就可以打開Camera進(jìn)行預(yù)覽 mCamera = Camera.open(0);//Camera.CameraInfo.CAMERA_FACING_BACK } @Override public void forbitPermissons() { Toast.makeText(MainActivity.this, "You denyied the permission", Toast.LENGTH_SHORT).show(); } };
錄制視頻及播放錄制視頻完整代碼如下
public class MainActivity extends AppCompatActivity implements SurfaceHolder.Callback{ private static final String TAG = "MainActivity"; private SurfaceView mSurfaceView; private Button mRecordBtn, mPlayBtn; private boolean mIsRecord = false; //是否正在錄像 private Camera mCamera; private MediaRecorder mMediaRecorder; private String mMediaPath; private MediaPlayer mMediaPlayer; private SurfaceHolder mSurfaceHolder; private PermissionsUtils mPermissionsUtils; private String[] permissions = {Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO}; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //6.0及以上系統(tǒng)請(qǐng)求運(yùn)行時(shí)權(quán)限 利用權(quán)限申請(qǐng)工具類(見下文) requestCameraAndStoragePermission(); mSurfaceView = (SurfaceView) findViewById(R.id.surface_view); mSurfaceView.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); // 必須-設(shè)置Surface不需要維護(hù)自己的緩沖區(qū) mRecordBtn = (Button) findViewById(R.id.record_btn); mPlayBtn = (Button) findViewById(R.id.play_btn); initBtnClick(); SurfaceHolder holder = mSurfaceView.getHolder(); holder.addCallback(this); } private void requestCameraAndStoragePermission() { //檢查用戶是否授權(quán) for (int i = 0; i < permissions.length; i++) { if (ContextCompat.checkSelfPermission(MainActivity.this, permissions[i]) != PackageManager.PERMISSION_GRANTED) { //沒有授權(quán)則請(qǐng)求相應(yīng)權(quán)限 ActivityCompat.requestPermissions(MainActivity.this, new String[]{permissions[i]}, 1); } } //利用權(quán)限申請(qǐng)工具類來實(shí)現(xiàn) mPermissionsUtils = PermissionsUtils.getInstance(); mPermissionsUtils.chekPermissions(MainActivity.this,permissions, permissionsResult); } //創(chuàng)建監(jiān)聽權(quán)限的接口對(duì)象 PermissionsUtils.IPermissionsResult permissionsResult = new PermissionsUtils.IPermissionsResult() { @Override public void passPermissons() { // StartRecording(); 注意這里的邏輯 并不是權(quán)限通過了就立即開始錄像了 而是權(quán)限通過了 就可以打開Camera進(jìn)行預(yù)覽 mCamera = Camera.open(0);//Camera.CameraInfo.CAMERA_FACING_BACK } @Override public void forbitPermissons() { Toast.makeText(MainActivity.this, "You denyied the permission", Toast.LENGTH_SHORT).show(); } }; private void StartRecording(){ mRecordBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { if (!mIsRecord) { try { Log.d(TAG, "首次點(diǎn)擊開始錄像 "); if (prepareVideoRecorder()) { mMediaRecorder.start(); mIsRecord = true; mRecordBtn.setText("stop"); } } catch (IOException e) { e.printStackTrace(); } } else { Log.d(TAG, "再次點(diǎn)擊停止錄像"); mMediaRecorder.stop(); releaseMediaRecorder(); mCamera.lock(); mRecordBtn.setText("record"); mIsRecord = false; if (mCamera != null) { mCamera.release(); mCamera = null; } } } }); } private void initBtnClick() { StartRecording(); mPlayBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { if (mMediaPlayer == null) { mMediaPlayer = new MediaPlayer(); mMediaPlayer.reset(); Uri uri = Uri.parse(mMediaPath); mMediaPlayer = MediaPlayer.create(MainActivity.this,uri); mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); mMediaPlayer.setDisplay(mSurfaceHolder); try{ mMediaPlayer.prepare(); }catch (Exception e){ e.printStackTrace(); } mMediaPlayer.start(); } } }); } /* * 相機(jī)預(yù)覽前的準(zhǔn)備工作代碼 單獨(dú)抽出來 * */ private boolean prepareVideoRecorder() throws IOException { if (mMediaRecorder == null) { mMediaRecorder = new MediaRecorder(); mMediaRecorder.reset(); } /*camera相關(guān)設(shè)置部分*/ mCamera = Camera.open(0);//Camera.CameraInfo.CAMERA_FACING_BACK if (mCamera != null) { //設(shè)置旋轉(zhuǎn)角度,順時(shí)針方向,因?yàn)槟J(rèn)是逆向90度的,這樣圖像就是正常顯示了 mCamera.setDisplayOrientation(90); mCamera.unlock(); mMediaRecorder.setCamera(mCamera); } /*recorder設(shè)置部分*/ mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER); mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA); mMediaRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH)); mMediaRecorder.setOutputFile(getOutputMediaFile()); mMediaRecorder.setPreviewDisplay(mSurfaceView.getHolder().getSurface()); mMediaRecorder.prepare(); return true; } /* * 獲取手機(jī)外部存儲(chǔ)路徑 * */ private String getOutputFile() { File mediaFile = null; boolean OutputExist = Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED); if (OutputExist) { mediaFile = Environment.getExternalStorageDirectory(); return mediaFile.toString(); } return null; } /* * 獲取錄制視頻的日期 作為存儲(chǔ)文件路徑一部分 * */ private String getDate() { Log.d(TAG, "獲取錄制視頻的日期 "); Calendar ca = Calendar.getInstance(); int year = ca.get(Calendar.YEAR); // 獲取年份 int month = ca.get(Calendar.MONTH); // 獲取月份 int day = ca.get(Calendar.DATE); // 獲取日 String date = "" + year + "_" + (month + 1) + "_" + day; return date; } /* *創(chuàng)建視頻存儲(chǔ)文件夾 錄制好的視頻存儲(chǔ)在手機(jī)外部存儲(chǔ)中 以錄像時(shí)間+mp4格式命名 * */ private String getOutputMediaFile() { Log.d(TAG, "獲取視頻存儲(chǔ)的位置 "); String mediaPath = getOutputFile(); if (mediaPath != null) { File mediaFile = new File(mediaPath + "/recordVideo"); if (!mediaFile.exists()) { mediaFile.mkdir(); } return mMediaPath = mediaFile.getAbsolutePath() + File.separator + getDate() + ".mp4"; } return null; } /* * 錄制視頻結(jié)束時(shí)釋放相機(jī)資源 * */ private void releaseMediaRecorder() { Log.d(TAG, "錄制結(jié)束后釋放資源 "); if (mMediaRecorder != null) { mMediaRecorder.reset(); // clear recorder configuration mMediaRecorder.release(); // release the recorder object mMediaRecorder = null; mCamera.lock(); // lock camera for later use } } @Override public void surfaceCreated(SurfaceHolder surfaceHolder) { mSurfaceHolder = surfaceHolder; } @Override public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) { mSurfaceHolder = surfaceHolder; } @Override public void surfaceDestroyed(SurfaceHolder surfaceHolder) { mSurfaceView = null; mSurfaceHolder = null; releaseMediaRecorder(); if (mCamera != null) { mCamera.release(); mCamera = null; } if (mMediaPlayer != null){ mMediaPlayer.release(); mMediaPlayer = null; } } }
三、延伸知識(shí),運(yùn)行時(shí)權(quán)限申請(qǐng)工具類
調(diào)用手機(jī)系統(tǒng)內(nèi)置的攝像頭進(jìn)行視頻錄制時(shí)及錄制視頻后將視頻保存在本地都需要申請(qǐng)系統(tǒng)權(quán)限,而且申請(qǐng)的權(quán)限(調(diào)用攝像頭權(quán)限、存儲(chǔ)權(quán)限)都屬于26個(gè)危險(xiǎn)權(quán)限,針對(duì)6.0以上的手機(jī),需要進(jìn)行運(yùn)行時(shí)權(quán)限的申請(qǐng),由于申請(qǐng)的權(quán)限過多,而且申請(qǐng)的時(shí)間不一致,所以這里提供一個(gè)權(quán)限申請(qǐng)工具類協(xié)助實(shí)現(xiàn)權(quán)限申請(qǐng)。(來自腳本之家文章:Android動(dòng)態(tài)請(qǐng)求權(quán)限的工具類(可請(qǐng)求多個(gè),并且功能完善))
完整代碼如下
/** * 運(yùn)行時(shí)權(quán)限申請(qǐng)工具類: * 檢查用戶是否授權(quán)——ContextCompat.checkSelfPermission * 如果沒有授權(quán),那么申請(qǐng)授權(quán)——ActivityCompat.requestPermissions * 申請(qǐng)授權(quán)之后的回調(diào)——onRequestPermissionsResult * 精髓:檢查權(quán)限 申請(qǐng)權(quán)限的代碼寫在工具類內(nèi) 同時(shí)寫入一個(gè)接口 兩個(gè)抽象方法-獲取權(quán)限成功 + 獲取權(quán)限失敗 然后在外部使用權(quán)限工具類時(shí)實(shí)現(xiàn)這兩個(gè)抽象方法 * Created by Administrator on 2018/7/3. */ public class PermissionsUtils { private final int mRequestCode = 100;//權(quán)限請(qǐng)求碼 public static boolean showSystemSetting = true; private PermissionsUtils() { } private static PermissionsUtils permissionsUtils; private IPermissionsResult mPermissionsResult; /* * 單例模式創(chuàng)建PermissionUtils實(shí)例 工具類中的靜態(tài)方法可以直接使用類名+方法名調(diào)用 非靜態(tài)方法還是需要獲取到工具類的實(shí)例 實(shí)例對(duì)方法進(jìn)行調(diào)用 * */ public static PermissionsUtils getInstance() { if (permissionsUtils == null) { synchronized (PermissionsUtils.class) { if (permissionsUtils == null) permissionsUtils = new PermissionsUtils(); } } return permissionsUtils; } /* * 檢查用戶是否授權(quán) + 如果沒有授權(quán) 則申請(qǐng)授權(quán) - 系統(tǒng)標(biāo)準(zhǔn)方法 * */ public void chekPermissions(Activity context, String[] permissions, @NonNull IPermissionsResult permissionsResult) { mPermissionsResult = permissionsResult; if (Build.VERSION.SDK_INT < 23) {//6.0系統(tǒng)及以上才會(huì)動(dòng)態(tài)申請(qǐng)權(quán)限 以下不用 所以直接return出去 permissionsResult.passPermissons(); return; } //創(chuàng)建一個(gè)mPermissionList,逐個(gè)判斷哪些權(quán)限未授予,未授予的權(quán)限存儲(chǔ)到mPerrrmissionList中 List<String> mPermissionList = new ArrayList<>(); //逐個(gè)判斷你要的權(quán)限是否已經(jīng)通過 for (int i = 0; i < permissions.length; i++) { if (ContextCompat.checkSelfPermission(context, permissions[i]) != PackageManager.PERMISSION_GRANTED) { mPermissionList.add(permissions[i]);//添加還未授予的權(quán)限 } } //申請(qǐng)權(quán)限 if (mPermissionList.size() > 0) {//有權(quán)限沒有通過,需要申請(qǐng) ActivityCompat.requestPermissions(context, permissions, mRequestCode); } else { //說明權(quán)限都已經(jīng)通過,利用接口變量調(diào)用實(shí)現(xiàn)的接口方法 即有權(quán)限之后需要調(diào)用的方法 permissionsResult.passPermissons(); return; } } //請(qǐng)求權(quán)限后回調(diào)的方法 //參數(shù): requestCode 是我們自己定義的權(quán)限請(qǐng)求碼 //參數(shù): permissions 是我們請(qǐng)求的權(quán)限名稱數(shù)組 //參數(shù): grantResults 是我們?cè)趶棾鲰撁婧笫欠裨试S權(quán)限的標(biāo)識(shí)數(shù)組,數(shù)組的長度對(duì)應(yīng)的是權(quán)限名稱數(shù)組的長度,數(shù)組的數(shù)據(jù)0表示允許權(quán)限,-1表示我們點(diǎn)擊了禁止權(quán)限 public void onRequestPermissionsResult(Activity context, int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { boolean hasPermissionDismiss = false;//有權(quán)限沒有通過 if (mRequestCode == requestCode) { for (int i = 0; i < grantResults.length; i++) { if (grantResults[i] == -1) { hasPermissionDismiss = true; } } //如果有權(quán)限沒有被允許 if (hasPermissionDismiss) { if (showSystemSetting) { showSystemPermissionsSettingDialog(context);//跳轉(zhuǎn)到系統(tǒng)設(shè)置權(quán)限頁面,或者直接關(guān)閉頁面,不讓他繼續(xù)訪問 } else { mPermissionsResult.forbitPermissons(); } } else { //全部權(quán)限通過,可以進(jìn)行下一步操作。。。 mPermissionsResult.passPermissons(); } } } /** * 不再提示權(quán)限時(shí)的展示對(duì)話框 */ AlertDialog mPermissionDialog; private void showSystemPermissionsSettingDialog(final Activity context) { final String mPackName = context.getPackageName(); if (mPermissionDialog == null) { mPermissionDialog = new AlertDialog.Builder(context) .setMessage("已禁用權(quán)限,請(qǐng)手動(dòng)授予") .setPositiveButton("設(shè)置", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { cancelPermissionDialog(); Uri packageURI = Uri.parse("package:" + mPackName); Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, packageURI); context.startActivity(intent); context.finish(); } }) .setNegativeButton("取消", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { //關(guān)閉頁面或者做其他操作 cancelPermissionDialog(); //mContext.finish(); mPermissionsResult.forbitPermissons(); } }) .create(); } mPermissionDialog.show(); } //關(guān)閉對(duì)話框 private void cancelPermissionDialog() { if (mPermissionDialog != null) { mPermissionDialog.cancel(); mPermissionDialog = null; } } public interface IPermissionsResult { void passPermissons(); void forbitPermissons(); } }
總結(jié)
以上所述是小編給大家介紹的Android使用MediaRecorder實(shí)現(xiàn)錄制視頻功能,希望對(duì)大家有所幫助,如果大家有任何疑問請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!
相關(guān)文章
Android開發(fā)Jetpack組件LiveData使用講解
LiveData是Jetpack組件的一部分,更多的時(shí)候是搭配ViewModel來使用,相對(duì)于Observable,LiveData的最大優(yōu)勢(shì)是其具有生命感知的,換句話說,LiveData可以保證只有在組件( Activity、Fragment、Service)處于活動(dòng)生命周期狀態(tài)的時(shí)候才會(huì)更新數(shù)據(jù)2022-08-08android中soap協(xié)議使用(ksoap調(diào)用webservice)
kSOAP是如何調(diào)用ebservice的呢,首先要使用SoapObject,這是一個(gè)高度抽象化的類,完成SOAP調(diào)用??梢哉{(diào)用它的addProperty方法填寫要調(diào)用的webservice方法的參數(shù)2014-02-02Android 中使用RecyclerView實(shí)現(xiàn)底部翻頁
這篇文章主要介紹了Android 中使用RecyclerView實(shí)現(xiàn)底部翻頁功能,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2017-11-11AndroidStudio修改Code Style來格式化自定義標(biāo)簽的xml文件方式
這篇文章主要介紹了AndroidStudio修改Code Style來格式化自定義標(biāo)簽的xml文件方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-03-03Android實(shí)現(xiàn)一個(gè)絲滑的自動(dòng)輪播控件實(shí)例代碼
輪播圖對(duì)大家來說應(yīng)該再熟悉不過了,下面這篇文章主要給大家介紹了關(guān)于Android實(shí)現(xiàn)一個(gè)絲滑的自動(dòng)輪播控件的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2018-08-08Android UI設(shè)計(jì)與開發(fā)之ViewPager介紹和簡(jiǎn)單實(shí)現(xiàn)引導(dǎo)界面
這篇文章主要為大家詳細(xì)介紹了Android UI設(shè)計(jì)與開發(fā)之ViewPager介紹和簡(jiǎn)單實(shí)現(xiàn)引導(dǎo)界面,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-08-08Android編程實(shí)現(xiàn)播放視頻的方法示例
這篇文章主要介紹了Android編程實(shí)現(xiàn)播放視頻的方法,結(jié)合具體實(shí)例形式分析了Android使用VideoView類播放視頻的相關(guān)操作技巧與注意事項(xiàng),需要的朋友可以參考下2017-08-08