Demo1 录音机
下面来分析:
各种引用的创建(比较特殊的ImageButton 和 MediaRecorder)
初始化我就不多说
Handler的使用 上篇博文谈过不多说
关于这种写法的监听可以借鉴(毕竟所有控件只不过是View的子类而已)
然后读取有没有内存卡
Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED)
初始化临时文件File.createTempFile( 参数一基本文件名 参数二后缀 参数三目录路径)
不多说下面看settime的代码
发送Bundle 不多谈
结束录音的时候
源代码如下
public class MyAudioRecorderActivity extends Activity implements OnClickListener{
public static final int UPDATE_TIME=0;//更新录音时间的消息编号
ImageButton ibRecord;//录制按钮
ImageButton ibStop;//停止按钮
TextView tvTime;//时间长度显示
Handler hd;//消息处理器
File myFile ;//用于存放音轨的文件
MediaRecorder myMediaRecorder;//媒体录音机
int countSecond=0;//录制的秒数
boolean recordFlag=false;//录制中标志
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
//初始化按钮引用
ibRecord=(ImageButton)findViewById(R.id.ImageButton01);
ibStop=(ImageButton)findViewById(R.id.ImageButton02);
//初始化显示录音时长的文本框
tvTime=(TextView)findViewById(R.id.TextView02);
//为录制按钮添加监听器
ibRecord.setOnClickListener(this);
//为停止按钮添加监听器
ibStop.setOnClickListener(this);
//初始化消息处理器对象
//本案例中希望通过附加线程修改TextView中的内容,因此要在主
//线程中创建一个Handler
hd=new Handler()
{
@Override
public void handleMessage(Message msg)
{
//调用父类处理
super.handleMessage(msg);
//根据消息what编号的不同,执行不同的业务逻辑
switch(msg.what)
{
//将消息中的内容提取出来显示在Toast中
case UPDATE_TIME:
//获取消息中的数据
Bundle b=msg.getData();
//获取内容字符串
String msgStr=b.getString("msg");
//设置字符串到显示录音时长的文本框中
tvTime.setText(msgStr);
break;
}
}
};
}
@Override
public void onClick(View v) {
if(v == ibRecord){//按下录音按钮
if(!Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED))
{//若没有插闪存卡则报错
Toast.makeText(this, "请检测内存卡", Toast.LENGTH_SHORT).show();
return;
}
try
{
if(recordFlag==true)
{//若已经在录音中则提示并返回
Toast.makeText(this, "录音中,请结束本次录音再开始新录音!", Toast.LENGTH_SHORT).show();
return;
}
//初始化临时文件对象
myFile = File.createTempFile
(
"myAudio", //基本文件名
".amr", //后缀
Environment.getExternalStorageDirectory() //目录路径
);
//创建录音机对象
myMediaRecorder = new MediaRecorder();
//设置输入设备为麦克风
myMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
//设置输出格式为默认的amr格式
myMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT);
//设置音频编码器为默认的编码器
myMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);
//设置输出文件的路径
myMediaRecorder.setOutputFile(myFile.getAbsolutePath());
//准备录音
myMediaRecorder.prepare();
//开始录音
myMediaRecorder.start();
//设置录音中标记为true
recordFlag=true;
//启动一个线程进行计时
new Thread()
{
public void run()
{
while(recordFlag)
{
//计时器加一
countSecond++;
//调用方法设置新时长
setTime();
//休息1000ms
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
} catch (IOException e) {
e.printStackTrace();
}
}
else if(v == ibStop){//按下停止按钮
if(myFile != null&&myMediaRecorder!=null)
{
//停止录音
myMediaRecorder.stop();
//释放录音机对象
myMediaRecorder.release();
//将录音机对象引用设置为null
myMediaRecorder = null;
}
//设置录音中标记为false
recordFlag=false;
//计时器清0
countSecond=0;
//调用方法设置新时长
setTime();
}
}
//设置显示时间的方法
public void setTime()
{
//计算分钟和秒
int second=countSecond%60;
int minute=countSecond/60;
//创建内容字符串
String msgStr=minute+"m:"+second+"s";
//创建消息数据Bundle
Bundle b=new Bundle();
//将内容字符串放进数据Bundle中
b.putString("msg", msgStr);
//创建消息对象
Message msg=new Message();
//设置数据Bundle到消息中
msg.setData(b);
//设置消息的what值
msg.what=UPDATE_TIME;
//发送消息
hd.sendMessage(msg);
}
最后注意要
声明权限!!!!
=================================================================================
Demo2 照相机
重点分析:
Uri 的临时存储
由于使用了startActivityForResult来发送intent的所有必须要实现该方法
自定义的图片保存进手机闪存的方法
flush 方法
照片路径处理的工具类代码
功能是获取下一个可以储存的文件 以及创建一个临时文件
public class StoreFileUtil
{
//当前存储照片对应的文件
static File currFile;
//获取下一个可以存储的文件
public static File nextFile()
{
//获取闪存卡路径
String path= Environment.getExternalStorageDirectory().getAbsolutePath();
//寻找可以使用的文件名
int c=0;
File fTest=new File(path+"/mc"+c+".jpg");
while(fTest.exists())
{
c++;
fTest=new File(path+"/mc"+c+".jpg");
}
currFile=fTest;
return fTest;
}
//临时文件
public static File tempFile()
{
//获取闪存卡路径
String path= Environment.getExternalStorageDirectory().getAbsolutePath();
File fTest=new File(path+"/BNtemp.jpg");
return fTest;
}
Activity代码
@SuppressLint("HandlerLeak")
public class MyCameraActivity extends Activity
{
private static int REQUEST_ORIGINAL=0;// 请求图片信号标识
private Button photo;//拍照按钮
private Button save;//保存按钮
private ImageView picture;//用于显示图片的ImageView
public static Bitmap bitmap;//当前拍的照片
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//获取显示图片的ImageView
picture = (ImageView) findViewById(R.id.picture_imageView);
//获取拍照按钮
photo = (Button) findViewById(R.id.main_Button_paizhao);
//给拍照按钮添加监听器
photo.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
//调用系统相机的Intent
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
//临时照片文件的位置
Uri uri = Uri.fromFile(StoreFileUtil.tempFile());
//将照片文件的位置传入intent
intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
//发出intent启动系统相机
startActivityForResult(intent, REQUEST_ORIGINAL);
}
});
//保存按钮
save = (Button) findViewById(R.id.main_Button_baocun);
//为保存按钮添加监听器
save.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
if(bitmap==null)
{
Toast.makeText(MyCameraActivity.this, "请先拍照再保存!", Toast.LENGTH_SHORT) .show();
}
//保存照片
setInSDBitmap(bitmap);
Toast.makeText(MyCameraActivity.this, "保存成功:"+StoreFileUtil.currFile.getName()+"!", Toast.LENGTH_SHORT) .show();
}
});
}
//系统相机返回时的回调方法
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK)
{
if(requestCode == REQUEST_ORIGINAL)
{
//从临时照片文件的位置加载照片
bitmap=BitmapFactory.decodeFile(StoreFileUtil.tempFile().getAbsolutePath());
//将图片设置给ImageView显示
picture.setImageBitmap(bitmap);
}
}
}
//将图片保存进手机闪存的方法
public static void setInSDBitmap(Bitmap bitmapToStore)
{
File file=StoreFileUtil.nextFile();
try
{
FileOutputStream fos=new FileOutputStream(file);
bitmapToStore.compress(Bitmap.CompressFormat.JPEG, 70, fos);
fos.flush();
fos.close();
}
catch(Exception e)
{
e.printStackTrace();
}
}
//重写返回按钮按下的处理方法
@Override
public boolean onKeyDown(int keyCode, KeyEvent event)
{
if(keyCode == KeyEvent.KEYCODE_BACK)
{
finish();
}
return false;
}
Demo3 摄像机
注意代码只是讲述了如何去调用摄像的功能,然后以及在此基础上去实现一些自己定义的功能
下面还是接着来说代码吧
值得注意的是 SurfaceView 是摄像预览用的
还有SurfaceHolder的作用
这段代码有个重点 即是摄像机的代码要用横屏实现
this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
如果用竖屏会出现90°bug 即拍摄的时候会选择90° 而且至今也未处理
this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
Handler我就不多说了
分析一下录音机和摄影机
关于媒体的记录用的都是 MediaRecorder这个类
直接上Activity吧
public class MyVideoRecorderActivity extends Activity implements OnClickListener{
public static final int UPDATE_TIME=0;//更新录音时间的消息编号
SurfaceView mSurfaceView; //摄像预览用的SurfaceView
private SurfaceHolder mSurfaceHolder;
private ImageButton buttonStart;//开始录制按钮
private ImageButton buttonStop;//停止录制按钮
private MediaRecorder recorder;//多媒体录制器
Handler hd;//消息处理器
int countSecond=0;//录制的秒数
private TextView tvTime;//显示录制时间的文本View
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//设置为横屏
this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
//设置全屏显示
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags
(
WindowManager.LayoutParams.FLAG_FULLSCREEN ,
WindowManager.LayoutParams.FLAG_FULLSCREEN
);
//加载主界面
setContentView(R.layout.main);
//对摄像预览用的SurfaceView及其SurfaceHolder进行初始化
mSurfaceView=(SurfaceView)this.findViewById(R.id.mySurfaceView);
mSurfaceHolder=mSurfaceView.getHolder();
//给录制、停止按钮添加监听器
buttonStart=(ImageButton)this.findViewById(R.id.ImageButton01);
buttonStop=(ImageButton)this.findViewById(R.id.ImageButton02);
buttonStart.setOnClickListener(this);
buttonStop.setOnClickListener(this);
//拿到显示录制时长的文本框的引用
tvTime=(TextView)this.findViewById(R.id.tvTime);
//线程中创建一个Handler
hd=new Handler()
{
@Override
public void handleMessage(Message msg)
{
//调用父类处理
super.handleMessage(msg);
//根据消息what编号的不同,执行不同的业务逻辑
switch(msg.what)
{
//将消息中的内容提取出来显示在Toast中
case UPDATE_TIME:
//获取消息中的数据
Bundle b=msg.getData();
//获取内容字符串
String msgStr=b.getString("msg");
//设置字符串到显示录音时长的文本框中
tvTime.setText(msgStr);
break;
}
}
};
}
@Override
public void onClick(View v)
{
if(v== buttonStart)
{//按下了开始录制按钮
if(recorder!=null)
{//若录制器不为空则报错并返回
Toast.makeText
(
this,
"请停止本次拍摄再开始新的拍摄!",
Toast.LENGTH_SHORT
).show();
return;
}
try
{
//创建录制器对象
recorder = new MediaRecorder();
//创建输出文件对象
File outf=File.createTempFile
(
"aas",
".mp4",
Environment.getExternalStorageDirectory()
);
//设置预览的View
recorder.setPreviewDisplay(mSurfaceHolder.getSurface());
//设置视频输入设备
recorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
//设置录音源为麦克风
recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
//设置输出格式为mp4
recorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
//设置视频大小(640,480) 不能自己随便乱写 必须和硬件匹配
recorder.setVideoSize(640,480);
//设置视频帧速率
recorder.setVideoFrameRate(15);//高端机可以到20,山寨只能15
//设置视频编码
recorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
//设置音频编码
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);
//设置最大时长
recorder.setMaxDuration(10000);
//设置输出文件
recorder.setOutputFile(outf.getAbsolutePath());
//准备录制器
recorder.prepare();
//开始录制
recorder.start();
//启动一个线程进行计时
new Thread()
{
public void run()
{
while(recorder!=null)
{
//计时器加一
countSecond++;
//调用方法设置新时长
setTime();
//休息1000ms
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
}
catch(Exception e)
{
e.printStackTrace();
Toast.makeText
(
this,
"录制故障:"+e.toString(),
Toast.LENGTH_SHORT
).show();
}
}
else if(v==buttonStop)
{
//停止录制
recorder.stop();
//释放录制器
recorder.release();
//清空录制器引用
recorder=null;
//计时器清0
countSecond=0;
//调用方法设置新时长
setTime();
}
}
@Override
protected void onPause() {
super.onPause();
if(recorder!= null)
{//若相机不为空则释放相机
recorder.stop();
recorder.release();
recorder = null;
}
}
//设置显示时间的方法
public void setTime()
{
//计算分钟和秒
int second=countSecond%60;
int minute=countSecond/60;
//创建内容字符串
String msgStr=minute+"m:"+second+"s";
//创建消息数据Bundle
Bundle b=new Bundle();
//将内容字符串放进数据Bundle中
b.putString("msg", msgStr);
//创建消息对象
Message msg=new Message();
//设置数据Bundle到消息中
msg.setData(b);
//设置消息的what值
msg.what=UPDATE_TIME;
//发送消息
hd.sendMessage(msg);
}
}
====================================================================================
Demo4 媒体播放器(当然和爱奇艺和腾讯视频之类的差远了0.0)
需要注意的主要是 媒体播放器MediaPlayer(不管播放音乐还是播放视频 机制其实差不多,当然目前播放视频都需要GPU来解压!!)
然后还有 进度条拖拉 SeekBar
依旧还是这么使用我们的Handler
MediaPlayer
MediaPlayer的使用 (我这里没有讲逻辑 逻辑的话下面的源码中有 需要的话可以去看 不难理解的)
先 创建对象
然后 设置成音频流格式 mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
然后 设置播放显示用的SurfaceView的SurfaceHolder!!这个很重要
给 该对象结束的时候设置监听
然后 设置播放路径 mp.setDataSource(currentPlay);
准备播放 mp.prepare();
开始播放 mp.start();
播放的总间隔(即时长) mp.getDuration()
获取当前播放的位置 mp.getCurrentPosition()
播放暂停 mp.pause();
播放停止 mp.stop();
释放播放器 mp.release();
清空引用 mp=null;
/进度拖拉到指定位置
mp.seekTo(progress*mp.getDuration()/sb.getMax());
SeekBar
SeekBar的使用方法
1.禁用拖拉条 sb.setEnabled(false); true则是启用
2.根据片长设置拖拉条的最大值 sb.setMax(mp.getDuration()/1000);
3.设置拖拉条当前的进度(这样写的代码要自动拖拉 所以根据播放的位置来实现拖拉)sb.setProgress(mp.getCurrentPosition()/1000);
实现进度拖拉的监听方法
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromUser)
Activity 代码
public class MyVideoPlayerActivity extends Activity
implements OnClickListener,OnSeekBarChangeListener
{
public static final int UPDATE_TIME=0;//更新播放时间的消息编号
public static final String currentPlay= Environment.getExternalStorageDirectory().getAbsolutePath()+"/aa.3gp";//播放路径
ImageButton play; //播放按钮
ImageButton pause; //暂停按钮
ImageButton stop; //停止按钮
SurfaceView sv; //播放显示用的SurfaceView
SurfaceHolder sh; //播放用的SurfaceHolder
MediaPlayer mp; //媒体播放器
SeekBar sb; //进度显示拖拉条
TextView tvTime; //时间长度显示
Handler hd; //消息处理器
int state=0; //播放状态指示 0-未准备 1-播放中 2-暂停中
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
//为播放、暂停、停止三个按钮添加监听器
play=(ImageButton)this.findViewById(R.id.ImageButton01);
pause=(ImageButton)this.findViewById(R.id.ImageButton02);
stop=(ImageButton)this.findViewById(R.id.ImageButton03);
play.setOnClickListener(this);
pause.setOnClickListener(this);
stop.setOnClickListener(this);
//初始化播放用的SurfaceView及SurfaceHolder
sv=(SurfaceView)this.findViewById(R.id.VideoView01);
sh=sv.getHolder();
//初始化进度拖拉条引用
sb=(SeekBar)this.findViewById(R.id.SeekBar01);
//不在暂停状态禁用拖拉条
sb.setEnabled(false);
//给进度拖拉条添加监听器
sb.setOnSeekBarChangeListener(this);
//初始化显示播放时长的文本框
tvTime=(TextView)findViewById(R.id.TextView01);
//线程中创建一个Handler
hd=new Handler()
{
@Override
public void handleMessage(Message msg)
{
//调用父类处理
super.handleMessage(msg);
//根据消息what编号的不同,执行不同的业务逻辑
switch(msg.what)
{
//将消息中的内容提取出来显示在Toast中
case UPDATE_TIME:
//获取消息中的数据
Bundle b=msg.getData();
//获取内容字符串
String msgStr=b.getString("msg");
//设置字符串到显示录音时长的文本框中
tvTime.setText(msgStr);
break;
}
}
};
}
@Override
public void onClick(View v) {
if(v==play)
{//按下播放按钮
if(state==1)
{//若当前已经是播放状态则报错返回
Toast.makeText
(
this,
"播放中,请结束本次播放再开始新的播放!",
Toast.LENGTH_SHORT
).show();
return;
}
else if(state==0)
{//若当前是未准备状态则进行播放前的准备工作
//创建媒体播放器对象
mp=new MediaPlayer();
//设置音频流格式
mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
//设置播放显示用SurfaceView的SurfaceHolder
mp.setDisplay(sh);
//给视频播放结束事件添加的监听器
mp.setOnCompletionListener(
new OnCompletionListener()
{
@Override
public void onCompletion(MediaPlayer arg0)
{//歌曲播放结束停止播放并更新界面状态
state=2;
}
}
);
try
{
//设置播放路径
mp.setDataSource(currentPlay);
//准备播放
mp.prepare();
}
catch(Exception e)
{
e.printStackTrace();
}
//根据片长设置拖拉条最大值
sb.setMax(mp.getDuration()/1000);
}
mp.start(); //开始播放
state=1; //状态设置为1-播放中
sb.setEnabled(false); //禁用拖拉条
new Thread() //启动一个线程定时更新进度条及时间
{
public void run()
{
while(state==1)
{
sb.setProgress(mp.getCurrentPosition()/1000);
setTime(mp.getCurrentPosition()/1000);
try
{
Thread.sleep(1000);
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
}.start();
}
else if(v==pause)
{//按下暂停按钮
if(state!=1)
{//若当前不是播放状态
Toast.makeText
(
this,
"请在播放状态再暂停!",
Toast.LENGTH_SHORT
).show();
return;
}
mp.pause(); //暂停
state=2; //设置状态为2-暂停
sb.setEnabled(true);//启用拖拉条
}
else if(v==stop)
{//按下停止按钮
if(state==0)
{
return;
}
state=0;
mp.stop(); //停止播放
mp.release(); //释放播放器
mp=null; //清空引用
sb.setEnabled(false);//禁用拖拉条
sb.setProgress(0);//设置进度为0
setTime(0);//设置时间为0
}
}
//设置显示时间的方法
public void setTime(int countSecond)
{
//计算分钟和秒
int second=countSecond%60;
int minute=countSecond/60;
//创建内容字符串
String msgStr=minute+"m:"+second+"s";
//创建消息数据Bundle
Bundle b=new Bundle();
//将内容字符串放进数据Bundle中
b.putString("msg", msgStr);
//创建消息对象
Message msg=new Message();
//设置数据Bundle到消息中
msg.setData(b);
//设置消息的what值
msg.what=UPDATE_TIME;
//发送消息
hd.sendMessage(msg);
}
//实现进度拖拉的监听方法
@Override
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromUser) {
if(mp!=null&&state==2)//=======Emulator work no "&&state==2"
{
//进度拖拉到指定位置
mp.seekTo(progress*mp.getDuration()/sb.getMax());
}
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) { }
@Override
public void onStopTrackingTouch(SeekBar seekBar) { }
}