多媒体之照相、录音、录像
1.照相和录像是调用Android系统,而录音是自定义的。其实调用他们并不难,难的是调用完后,怎么存储?怎么按照需求有序显示?
2.首先,来看看照相功
// 跳转至拍照界面
Intent intentPhote = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intentPhote.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 1);
imageName = DateFormat.format("yyyyMMdd_hhmmss", Calendar.getInstance(Locale.CHINA)) + ".jpg";
File photoOut = new File(getPhotopath());
Uri uri = Uri.fromFile(photoOut);
// 获取拍照后未压缩的原图片,并保存在uri路径中
intentPhote.putExtra(MediaStore.EXTRA_OUTPUT, uri);
startActivityForResult(intentPhote, 2000);
3.照完相后Android提供了一个回调方法startActivityForResult:如果你想在Activity中得到新打开Activity关闭后返回的数据,你需要使用系统提tartActivityForResult(Intent intent,int requestCode)方法打开新的Activity,新的Activity关闭后会向前面的Activity传回数据,为了得到传回的数据,你必须在前面的Activity中重写onActivityResult(int requestCode, int resultCode,Intent data)方法:
4. //第一个参数为请求码,即调用startActivityForResult()传递过去的值 //第二个参数为结果码,结果码用于标识返回数据来自哪个新Activity
//系统相机
if(requestCode == 2000 && resultCode == Activity.RESULT_OK) {
//返回时在uir中保持原图片,不压缩
String apath = getPhotopath();
Bitmap bitmap = getImageThumbnail(apath, screenWidth, screenHeight);
//创建缩略图
Bitmap bitmapThumbnail = getImageThumbnail(getPhotopath(), screenWidth/6,screenHeight/6);
saveScalePhoto(bitmap, getPhotopath());
saveScalePhoto(bitmapThumbnail, getPhotoThumbnailPath());
//删除视图
photoLinearLayout.removeAllViews();
//将保持的图片名保存在list缓存中
this.photoPathes.add(photoThumbnailPathUrl + imageName);
//重绘,更新视图
initPhotoData(this.photoPathes, ConstantInterface.POLL_MODULE, ConstantInterface.PHOTO_TYPE);//替代以上的方法
}
5.让我们来看看getImageThumbnail这 方法(可以解决OOM内存溢出的现象)
/**
* 根据指定的图像路径和大小来获取缩略图
* 此方法有两点好处:
* 1. 使用较小的内存空间,第一次获取的bitmap实际上为null,只是为了读取宽度和高度,
* 第二次读取的bitmap是根据比例压缩过的图像,第三次读取的bitmap是所要的缩略图。
* 2. 缩略图对于原图像来讲没有拉伸,这里使用了2.2版本的新工具ThumbnailUtils,使
* 用这个工具生成的图像不会被拉伸。
* @param imagePath 图像的路径
* @param width 指定输出图像的宽度
* @param height 指定输出图像的高度
* @return 生成的缩略图
*/
public Bitmap getImageThumbnail(String imagePath, int width, int height) {
Bitmap bitmap = null;
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
// 获取这个图片的宽和高,注意此处的bitmap为null
bitmap = BitmapFactory.decodeFile(imagePath, options);
options.inJustDecodeBounds = false; // 设为 false
// 计算缩放比
int h = options.outHeight;
int w = options.outWidth;
int beWidth = w / width;
int beHeight = h / height;
int be = 1;
if (beWidth < beHeight) {
be = beWidth;
} else {
be = beHeight;
}
if (be <= 0) {
be = 1;
}
options.inSampleSize = be;
// 重新读入图片,读取缩放后的bitmap,注意这次要把options.inJustDecodeBounds 设为 false
bitmap = BitmapFactory.decodeFile(imagePath, options);
// 利用ThumbnailUtils来创建缩略图,这里要指定要缩放哪个Bitmap对象
bitmap = ThumbnailUtils.extractThumbnail(bitmap, width, height,
ThumbnailUtils.OPTIONS_RECYCLE_INPUT);
return bitmap;
}
6.存储图片
/**
* 存储缩放的图片
* 图片数据
*/
public void saveScalePhoto(Bitmap bitmap,String url) {
FileOutputStream fos = null;
try {
fos = new FileOutputStream(url);
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos);
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
try {
fos.flush();
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
7.在页面显示图片
private List<String> photoPathes =new ArrayList<>();// 存放照片的路径
8.先用一个list保存中
/**
* 初始化 并且显示相册的 数据
*/
photoLinearLayout.removeAllViews();// 移除所有的数据
this.photoPathes=getPhotoPlateListData(ConstantInterface.POLL_MODULE, ConstantInterface.PHOTO_TYPE, pid+"");//通过访问数据库来获取所有与pid有关的缩略图数据
//显示的数据全部都在list中读取
super.initPhotoData(photoPathes, ConstantInterface.POLL_MODULE, ConstantInterface.PHOTO_TYPE);// 已经与数据库无关
其中getPhotoPlateListData()和initPhotoData()是两个重要的方法,第一个是从数据库中读取数据,第二个方法是将这些数据显示到页面上。
/**
* 遍历同一板块中不同item显示的图片
* @param media_plate_type
* @param type
* @return
*/
public List getPhotoPlateListData(String media_plate_type,String type,String media_plate_uid){
List dbList=mediaSelectPlateDb(media_plate_type, type, media_plate_uid);
List list = new ArrayList();
for(int i=0;i<dbList.size();i++){
//缩略图
list.add(photoThumbnailPathUrl + dbList.get(i));
}
return list;
}
/**
* 遍历list,显示数据
* 将资源路径 通过view显示 应该使用adapter模式进行转化
* @param dataPaths 资源的路径
* @param media_plate_type 资源的类型
* @param type 资源的类型
* @param ??????模块的id号码
*/
public void initPhotoData(List<String> dataPaths,String media_plate_type,String type){
//查询
for (int i = 0; i < dataPaths.size(); i++)
{
itemView=mLayoutInflater.inflate(R.layout.gallery_item, null);
imageView=(ImageView) itemView.findViewById(R.id.imageView);
imageView.setImageBitmap(BitmapFactory.decodeFile(dataPaths.get(i)));
badge = new BadgeView(this, imageView);
badge.setBackgroundResource(R.drawable.cancel2);
badge.setOnClickListener(new DeleteCatcheOnClickListener(dataPaths,i, imageView,media_plate_type,type));
imageView.setOnClickListener(new SelectCatcheOnClickListener(dataPaths,i, imageView, media_plate_type, type));
badge.show();
photoLinearLayout.addView(itemView);
}
}
这是其中的照相功能,这只是简单的存储和显示的问题,设计到具体的实际问题,还要根据问题来做相应的调整。其实摄像功能和照相差别不大,在我的程序中只是存图片的文件夹有两个,一个是缩略图,一个是原图(也算不上原图,都进行了压缩和处理,就是那getImageThumbnail()方法和saveScalePhoto()方法)。两个文件夹中的相片名字完全一样。而摄像功能的所有文件都放在一个文件下其中包括视频的缩略图和视频文件,他们的文件名是一样的,只是后缀与一样。这里我就不在缀诉摄像功能。
录音功能
录音功能是仿微信的,当手按下时录音,停下时停止。其实也没有什么好讲的,显示和存储都和照相的查不多。在这里我只说一下细节问题。
1.如录音后播放的声音太小,可以加上
AudioManager audioManager = (AudioManager) getApplicationContext().getSystemService(Context.AUDIO_SERVICE);
audioManager.setMode(AudioManager.MODE_IN_CALL);
audioManager.setMicrophoneMute(false);
audioManager.setSpeakerphoneOn(true);//使用扬声器外放,即使已经插入耳机
audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC), AudioManager.FLAG_PLAY_SOUND);//控制声音的大小
这样播放的声音就大很多了。
2.录音是还存在一个问题,若第一次点击时间太短,手松开后视图上是没有录音,但第二次若录音时间够长,这次会有两个录音的图片,但第一个是空的,就是没有声音。这是录音是
buttonSound.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent event) {
String voiceName = DateFormat.format("yyyyMMdd_hhmmss", Calendar.getInstance(Locale.CHINA)) + ".amr";//文件的格式 arm;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if (RECODE_STATE != RECORD_ING) {
mr = new AudioRecorder(soundPathUrl + voiceName);
RECODE_STATE = RECORD_ING;
showVoiceDialog();
try {
mr.start();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
mythread();
}
break;
case MotionEvent.ACTION_UP:
if (RECODE_STATE == RECORD_ING) {
RECODE_STATE = RECODE_ED;
if (dialog.isShowing()) {
dialog.dismiss();
}
try {
mr.stop();
voiceValue = 0.0;
} catch (IOException e) {
e.printStackTrace();
}
if (recodeTime < MIX_TIME) {
showWarnToast();
RECODE_STATE = RECORD_NO;
} else {
//删除视图
soundLinearLayout.removeAllViews();
soundPathes.add(voiceName);
Log.i("录音了:", "ccccc");
//重绘,更新视图
initSoundData(soundPathes, ConstantInterface.POLL_MODULE, ConstantInterface.SOUND_TYPE);
}
}
break;
}
return false;
}
});
这个问题是voiceName的作用域造成的,但若要把voiceName的作用域放到下面松开手时的地方,那上面的录制时有找不到录音的存放位置。目前这个还没有找到好的办法。
多媒体总结:实现单一功能还是比较简单的,但是要把这些功能联合起来需要费一番周折的。但要把这些功能复用起来那要好好想想办法了。这个程序中对这些功能是复用,我是将多媒体写成了一个父类,其他子类继承。这是最低级的复用方法了。应该将多媒体写成模块化的功能,用到时直接调用。但我的水平有限还没有达到这个地步,但这个思想一定要有。到能力达到时,一定要按照模块化的思想来写可复用的程序。这才是程序员的能力体现,要不永远都是码农!