在开头,先说一下在imageVIew中如何显示文件中存储的视频的第一帧的图片。很简单,几行代码就搞定。
MediaMetadataRetriever media = new MediaMetadataRetriever(); media.setDataSource(user.getPath()); Bitmap bitmap = media.getFrameAtTime();
下来,开始说拍照和存储视频,先在配置文件中声明一下provider,因为在安卓7.0以上想要存储必须要这个东西,没办法,写吧。
<provider android:name="android.support.v4.content.FileProvider" android:authorities="zj.it.bhne.gridengineeringsurvey.fileprovider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" /> </provider>
meta-data中的file_paths就是自己建的一个xml文件。"."的意思时所有格式。
<?xml version="1.0" encoding="utf-8"?> <paths > <external-path name="external_files" path="."/> <files-path name="my_images" path="images/"/> <root-path name="root_path" path="." /> </paths>
好了,准备工作做完之后就开始编写拍照和录视频吧。首先时录视频。
/** * 启动相机,创建文件,并要求返回uri */ private void startVideo() { Intent intent = new Intent(); //指定动作,启动相机 intent.setAction(MediaStore.ACTION_VIDEO_CAPTURE); intent.addCategory(Intent.CATEGORY_DEFAULT); Log.d(TAG, "指定启动相机动作,完成。"); //创建文件 createVideoFile(); Log.d(TAG, "创建视频文件结束。"); //添加权限 intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); Log.d(TAG, "添加权限。"); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { //做一些处理 //获取uri mVideoUri = FileProvider.getUriForFile(this, "zj.it.bhne.gridengineeringsurvey.fileprovider", mVideoFile); } else { //在版本低于此的时候,做一些处理 mVideoUri = Uri.fromFile(mVideoFile); } Log.d(TAG, "根据视频文件路径获取uri。"); //将uri加入到额外数据 intent.putExtra(MediaStore.EXTRA_OUTPUT, mVideoUri); Log.d(TAG, "将uri加入启动相机的额外数据。"); Log.d(TAG, "启动相机..."); //启动相机并要求返回结果 startActivityForResult(intent, VIDEO_RESULT_CODE); Log.d(TAG, "拍摄中..."); } /** * 创建视频文件 */ private void createVideoFile() { Log.d(TAG, "开始创建图片文件..."); //设置图片文件名(含后缀),以当前时间的毫秒值为名称 mVideoName = Calendar.getInstance().getTimeInMillis() + ".mp4"; Log.d(TAG, "设置图片文件的名称为:"+mVideoName); //创建图片文件 mVideoFile = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/bhne/" + getPath() + "/", mVideoName); //将图片的绝对路径设置给mImagePath,后面会用到 mVideoPath = mVideoFile.getAbsolutePath(); //按设置好的目录层级创建 mVideoFile.getParentFile().mkdirs(); Log.d(TAG, "按设置的目录层级创建图片文件,路径:"+mVideoPath); //不加这句会报Read-only警告。且无法写入SD mVideoFile.setWritable(true); Log.d(TAG, "将图片文件设置可写。"); }
需要标注的我都写好了注释,直接看代码就可以了。下面是拍照并存储。
/** * 启动相机,创建文件,并要求返回uri */ @TargetApi(Build.VERSION_CODES.JELLY_BEAN) private void startCamera() { Intent intent = new Intent(); //指定动作,启动相机 intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE); intent.addCategory(Intent.CATEGORY_DEFAULT); Log.d(TAG, "指定启动相机动作,完成。"); //创建文件 createImageFile(); Log.d(TAG, "创建图片文件结束。"); //添加权限 intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); Log.d(TAG, "添加权限。"); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { //做一些处理 //获取uri mImageUri = FileProvider.getUriForFile(this, "zj.it.bhne.gridengineeringsurvey.fileprovider", mImageFile); } else { //在版本低于此的时候,做一些处理 mImageUri = Uri.fromFile(mImageFile); } Log.d(TAG, "根据图片文件路径获取uri。"); //将uri加入到额外数据 intent.putExtra(MediaStore.EXTRA_OUTPUT, mImageUri); Log.d(TAG, "将uri加入启动相机的额外数据。"); Log.d(TAG, "启动相机..."); //启动相机并要求返回结果 startActivityForResult(intent, PHOTO_RESULT_CODE); Log.d(TAG, "拍摄中..."); } /** * 创建图片文件 */ private void createImageFile(){ Log.d(TAG, "开始创建图片文件..."); //设置图片文件名(含后缀),以当前时间的毫秒值为名称 mImageName = Calendar.getInstance().getTimeInMillis() + ".jpg"; Log.d(TAG, "设置图片文件的名称为:"+mImageName); //创建图片文件 mImageFile = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/bhne/" + getPath() + "/", mImageName); //将图片的绝对路径设置给mImagePath,后面会用到 mImagePath = mImageFile.getAbsolutePath(); //按设置好的目录层级创建 mImageFile.getParentFile().mkdirs(); Log.d(TAG, "按设置的目录层级创建图片文件,路径:"+mImagePath); //不加这句会报Read-only警告。且无法写入SD mImageFile.setWritable(true); Log.d(TAG, "将图片文件设置可写。"); }
好了。就这样,注意任何时候不要硬编码文件路径,请使用 Android 文件系统 API 访问。千万不要像下面这样写:
File file = new File("/mnt/sdcard/Download/Album", alName);
下面时录音的存储:
/** * 启动录音机,创建文件 */ private void startRecord() { Intent intent = new Intent(); intent.setAction(MediaStore.Audio.Media.RECORD_SOUND_ACTION); createVoiceFile(); Log.d(TAG, "创建录音文件"); //添加权限 intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); Log.d(TAG, "启动系统录音机,开始录音..."); startActivityForResult(intent, VOICE_RESULT_CODE); } /** * 创建音频目录 */ private void createVoiceFile() { mVoiceName = getMyTime() + ".amr"; Log.d(TAG, "录音文件名称:" + mVoiceName); mVoiceFile = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/" + SD_APP_DIR_NAME + "/" + VOICE_DIR_NAME + "/", mVoiceName); mVoicePath = mVoiceFile.getAbsolutePath(); mVoiceFile.getParentFile().mkdirs(); Log.d(TAG, "按设置的目录层级创建音频文件,路径:" + mVoicePath); mVoiceFile.setWritable(true); } /** * 获取日期并格式化 * 如:2017_10_20 周三 上午 11:20:35 * * @return 格式化好的日期字符串 */ private String getMyTime() { //存储格式化后的时间 String time; //存储上午下午 String ampTime = ""; //判断上午下午,am上午,值为 0 ; pm下午,值为 1 int apm = Calendar.getInstance().get(Calendar.AM_PM); if (apm == 0) { ampTime = "上午"; } else { ampTime = "下午"; } //设置格式化格式 SimpleDateFormat format = new SimpleDateFormat("yyyy_MM_dd E " + ampTime + " kk:mm:ss"); time = format.format(new Date()); return time; } /** * 保存音频到SD卡的指定位置 * * @param path 录音文件的路径 */ private void saveVoiceToSD(String path) { //创建输入输出 InputStream isFrom = null; OutputStream osTo = null; try { //设置输入输出流 isFrom = new FileInputStream(path); osTo = new FileOutputStream(mVoicePath); byte bt[] = new byte[1024]; int len; while ((len = isFrom.read(bt)) != -1) { Log.d(TAG, "len = " + len); osTo.write(bt, 0, len); } Log.d(TAG, "保存录音完成。"); } catch (IOException e) { e.printStackTrace(); } finally { if (osTo != null) { try { //不管是否出现异常,都要关闭流 osTo.close(); Log.d(TAG, "关闭输出流"); } catch (IOException e) { e.printStackTrace(); } } if (isFrom != null) { try { isFrom.close(); Log.d(TAG, "关闭输入流"); } catch (IOException e) { e.printStackTrace(); } } } } /** * 通过Uri,获取录音文件的路径(绝对路径) * * @param uri 录音文件的uri * @return 录音文件的路径(String) */ private String getAudioFilePathFromUri(Uri uri) { Cursor cursor = getContentResolver() .query(uri, null, null, null, null); cursor.moveToFirst(); int index = cursor.getColumnIndex(MediaStore.Audio.AudioColumns.DATA); String temp = cursor.getString(index); cursor.close(); return temp; }
下面处理照片,视频以及录音存储
/** * 处理返回结果。 * 1、图片 * 2、音频 * 3、视频 * * @param requestCode 请求码 * @param resultCode 结果码 成功 -1 失败 0 * @param data 返回的数据 */ @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); // Log.d(TAG, "拍摄结束。"); Log.d(TAG, "录音结束。"); if (resultCode == Activity.RESULT_OK) { Log.d(TAG, "返回成功。"); Log.d(TAG, "请求码:" + requestCode + " 结果码:" + resultCode + " data:" + data); switch (requestCode) { case PHOTO_RESULT_CODE: { Bitmap bitmap = null; try { //根据uri设置bitmap bitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), mImageUri); Log.d(TAG, "根据uri设置bitmap。"); } catch (IOException e) { e.printStackTrace(); } //将图片保存到SD的指定位置 savePhotoToSD(bitmap); //更新系统图库 updateSystemGallery(); Log.d(TAG, "结束。"); break; } case VOICE_RESULT_CODE: { try { Uri uri = data.getData(); String filePath = getAudioFilePathFromUri(uri); Log.d(TAG, "根据uri获取文件路径:" + filePath); Log.d(TAG, "开始保存录音文件"); saveVoiceToSD(filePath); } catch (Exception e) { throw new RuntimeException(e); } break; } case VIDEO_RESULT_CODE: { // saveVideoTOSD(); break; } } } }