导语
多媒体编程介绍
一、多媒体概念
- 文字、图片、音频、视频
1.1、计算机图片大小的计算
图片大小 = 图片的总像素 * 每个像素占用的大小
- 单色图:每个像素占用1/8个字节
- 16色图:每个像素占用1/2个字节
- 256色图:每个像素占用1个字节
- 24位图:每个像素占用3个字节
 ##1.2、计算机图片详解
- 图片的大小 = 图片的像素 * 每个像素占用的大小
- 单色:只能表示两种颜色- 使用0和1表示黑与白
- 使用一个长度为1的二进制数字即可表示一个像素的颜色信息
- 每个像素占用1/8个字节
 
- 16色:只能表示16种颜色- 使用16个数字对应这16种颜色
- 0~15,0000 ~ 1111,也就是说需要长度为4的二进制数字
- 每个像素占用1/2个字节
 
- 256色:只能表示256种颜色- 使用256个数字对应256种颜色
- 0~255,0000 0000 ~ 1111 1111,也就是说需要长度为8的二进制数字
- 每个像素占用1个字节
 
- 24位色:需要长度为24的二进制数字- 每个像素占用3个字节
- R:0~255,占用1个字节
- G:同上
- B:同上
 
#二、加载大图片到内存
###2.1、出现内存溢出问题
- Android系统以ARGB表示每个像素,所以每个像素占用4个字节,很容易内存溢出
- android系统里面每个应用程序默认的vm虚拟机最大的heap空间 16M,如果应用程序占用的内存空间超过了16M 则OOM(out of memory)内存溢出。
###2.2 如何解决加载大图片内存溢出问题?
- 手机屏幕的分辨率要比图片的分辨率小很多。 只需要根据手机的分辨率把图片给压缩采样,加载到手机上就行了。步骤:- 计算手机屏幕的宽高
- 计算图片的宽高
- 计算图片的缩放比例
- opts.inSampleSize = scale; 设置图片的缩放比例
- 加载图片到内存
 
- 具体解析如下:- Android内存中使用ARGB保存像素信息,每个像素占用4个字节
- SD卡中图片像素大小:2400 * 3200 = 7680000
- 手机的屏幕像素大小: 320 * 480 = 153600
- 把SD卡图片先缩小,再加载,先计算图片的缩小比例- 2400 / 320 = 7.5
- 3200 / 480 = 6.67
 
 
##2.1对图片进行缩放
- 获取屏幕宽高 - //Display类提供关于屏幕尺寸和分辨率的信息。 Display dp = getWindowManager().getDefaultDisplay(); //获取屏幕的宽度尺寸,以像素为单位。(不建议使用,请使用getSize(Point) 代替) int screenWidth = dp.getWidth(); //获取屏幕的高度尺寸,以像素为单位。(不建议使用,请使用getSize(Point) 代替) int screenHeight = dp.getHeight();
- 获取图片宽高 ???????????? - //创建请求权对象 Options opts = new Options(); //请求图片属性但不申请内存,//不解析像素信息,只解析图片宽高,那么就不会申请内存去保存像素信息 opts.inJustDecodeBounds = true; //api会解析图片的所有像素信息,把像素信息保存在内存中 BitmapFactory.decodeFile("sdcard/dog.jpg", opts); //获取图片宽高 int imageWidth = opts.outWidth; int imageHeight = opts.outHeight;
- 图片的宽高除以屏幕宽高,算出宽和高的缩放比例,取较大值作为图片的缩放比例 - //计算缩小比例 int scale = 1; int scaleX = imageWidth / screenWidth; int scaleY = imageHeight / screenHeight; //取较大值 if(scaleX >= scaleY && scaleX > 1){ scale = scaleX; } else if(scaleY > scaleX && scaleY > 1){ scale = scaleY; }
- 按缩放比例加载图片 - //设置缩放比例 opts.inSampleSize = scale; //为图片申请内存 opts.inJustDecodeBounds = false; //按照缩小后的比例来解析像素 Bitmap bm = BitmapFactory.decodeFile("sdcard/dog.jpg", opts); //查找并初始化图片控件 ImageView iv = (ImageView) findViewById(R.id.iv); //为图片重新添加内容 iv.setImageBitmap(bm);
–加载图片到内存,并显示在手机桌面上的示例–
    public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
    public void click(View v){
        Options opts = new Options();
        //不解析像素信息,只解析图片宽高,那么就不会申请内存去保存像素信息
        opts.inJustDecodeBounds = true;
        //api会解析图片的所有像素信息,把像素信息保存在内存中
        BitmapFactory.decodeFile("sdcard/dog.jpg", opts);
        //获取图片宽高
        int imageWidth = opts.outWidth;
        int imageHeight = opts.outHeight;
        //获取屏幕宽高
        Display dp = getWindowManager().getDefaultDisplay();
        @SuppressWarnings("deprecation")
        int screenWidth = dp.getWidth();
        int screenHeight = dp.getHeight();
        //计算缩小比例
        int scale = 1;
        int scaleWidth = imageWidth / screenWidth;
        int scaleHeight = imageHeight / screenHeight;
        //取较大值
        if(scaleWidth >= scaleHeight && scaleWidth > 1){
            scale = scaleWidth;
        }
        else if(scaleWidth < scaleHeight && scaleHeight > 1){
            scale = scaleHeight;
        }
        //设置缩放比例
        opts.inSampleSize = scale;
        //为图片申请内存
        opts.inJustDecodeBounds = false;
        //按照缩小后的比例来解析像素
        Bitmap bm = BitmapFactory.decodeFile("sdcard/dog.jpg", opts);
        ImageView iv = (ImageView) findViewById(R.id.iv);
        iv.setImageBitmap(bm);
    }
}
#三、在内存中创建图片的副本
直接加载的bitmap对象是只读的,无法修改,要修改图片只能在内存中创建出一个一模一样的bitmap副本,然后修改副本
//加载SD卡中的原图到内存,注意选个小的,不要内存溢出这个对象是只读的
Bitmap srcBm = BitmapFactory.decodeFile("sdcard/photo3.jpg");
//通过图片控件将原图显示到桌面,这个对象是只读的。
iv_src.setImageBitmap(srcBm);
//创建与原图大小一致的空白bitmap
Bitmap copyBm = Bitmap.createBitmap(srcBm.getWidth(), srcBm.getHeight(), srcBm.getConfig());
//定义画笔
Paint paint = new Paint();
//把纸铺在画版上
Canvas canvas = new Canvas(copyBm);
//把srcBm的内容绘制在copyBm上
canvas.drawBitmap(srcBm, new Matrix(), paint);
//将副本图片通过图片控件显示到桌面,这个对象是可读写的。
iv_copy.setImageBitmap(copyBm);
–创建图片副本示例–
public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //这个对象是只读的
        Bitmap bmSrc = BitmapFactory.decodeResource(getResources(), R.drawable.photo3);
        //1.创建与原图大小一致的bitmap对象,相当于创建了与原图大小一致的白纸
        Bitmap bmCopy = Bitmap.createBitmap(bmSrc.getWidth(), bmSrc.getHeight(), bmSrc.getConfig());
        //2.创建画笔对象
        Paint paint = new Paint();
        //3.创建画板对象,把白纸铺在画板上
        Canvas canvas = new Canvas(bmCopy);
        //4.开始作画,把原图内容画到白纸上
        canvas.drawBitmap(bmSrc, new Matrix(), paint);
        ImageView iv_src = (ImageView) findViewById(R.id.iv_src);
        iv_src.setImageBitmap(bmSrc);
        ImageView iv_copy = (ImageView) findViewById(R.id.iv_copy);
        iv_copy.setImageBitmap(bmCopy);
    }
}
##3.1对图片进行特效处理
- 首先定义一个矩阵对象 - Matrix mt = new Matrix();
- 缩放效果 - //x轴缩放1倍,y轴缩放0.5倍 mt.setScale(1, 0.5f); mt.setScale(2.0f, 2.0f);
- 旋转效果 - //以副本图片为轴点,顺时旋转30度 mt.setRotate(30,copyBm.getWidth()/2, copyBm.getHeight()/2);
- 平移 - //x轴坐标+10,y轴坐标+20 mt.setTranslate(10, 20);
- 镜面 - //把X坐标都变成负数 mt.setScale(-1, 1); //matrix.setScale(-1.0f, 1); //图片整体向右移 mt.postTranslate(copyBm.getWidth(), 0);
- 倒影 - //把Y坐标都变成负数 mt.setScale(1, -1); //图片整体向下移 mt.postTranslate(0, copyBm.getHeight());
–图片特效示例–
public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //这个对象是只读的
        Bitmap bmSrc = BitmapFactory.decodeResource(getResources(), R.drawable.photo3);
        //1.创建与原图大小一致的bitmap对象,相当于创建了与原图大小一致的白纸
        Bitmap bmCopy = Bitmap.createBitmap(bmSrc.getWidth(), bmSrc.getHeight(), bmSrc.getConfig());
        //2.创建画笔对象
        Paint paint = new Paint();
        //3.创建画板对象,把白纸铺在画板上
        Canvas canvas = new Canvas(bmCopy);
        //4.开始作画,把原图内容画到白纸上
        //4.1对图片进行特效处理
        //首先定义一个矩阵对象
        Matrix mt = new Matrix();
        //平移
        //mt.setTranslate(10, 20);
        //缩放
        //mt.setScale(0.5f,2);
        //mt.setScale(0.5f,2, bmCopy.getWidth()/2, bmCopy.getHeight()/2);
        //旋转
        //mt.setRotate(45,bmCopy.getWidth()/2, bmCopy.getHeight()/2);
        //镜面
        //mt.setScale(-1, 1); 
        //mt.postTranslate(bmCopy.getWidth(), 0);
        //倒影
        mt.setScale(1, -1);
        mt.postTranslate(0, bmCopy.getHeight());
        //4.2 把原图内容画到白纸上
        canvas.drawBitmap(bmSrc, mt, paint);
        ImageView iv_src = (ImageView) findViewById(R.id.iv_src);
        iv_src.setImageBitmap(bmSrc);
        ImageView iv_copy = (ImageView) findViewById(R.id.iv_copy);
        iv_copy.setImageBitmap(bmCopy);
    }
}
#四、画画板,在图片副本上进行读写操作
###4.1、实现功能:记录用户触摸事件的XY坐标,绘制直线
- 1、在手机屏幕上加载图片副本 - //1将原画加载到内存 Bitmap bmSrc = BitmapFactory.decodeResource(getResources(), R.drawable.bg); //2在内存中创建图片的副本 bmCopy = Bitmap.createBitmap(bmSrc.getWidth(), bmSrc.getHeight(), bmSrc.getConfig()); paint = new Paint(); canvas = new Canvas(bmCopy); //作画 canvas.drawBitmap(bmSrc, new Matrix(), paint); //3将副本图片显示在桌面上 iv = (ImageView) findViewById(R.id.iv); iv.setImageBitmap(bmCopy);
- 2、给ImageView图片副本设置触摸侦听,得到用户的触摸事件,并获知用户触摸ImageView的坐标,实现在副本图片上划线 - iv.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { // TODO Auto-generated method stub switch (event.getAction()) {//判断手指触屏事件 //触摸屏幕 case MotionEvent.ACTION_DOWN: //得到触摸屏幕时手指的坐标 startX = (int) event.getX(); startY = (int) event.getY(); break; //在屏幕上滑动 case MotionEvent.ACTION_MOVE: //用户滑动手指,坐标不断的改变,获取最新坐标 int newX = (int) event.getX(); int newY = (int) event.getY(); //用上次onTouch方法得到的坐标和本次得到的坐标绘制直线 canvas.drawLine(startX, startY, newX, newY, paint); //刷新桌面图片 iv.setImageBitmap(copyBm); //改变每次画线的开始坐标 startX = newX; startY = newY; break; } //该触摸事件是否由此侦听处理 return true; } });
- 刷子效果,加粗画笔 - public void flush(View v){ paint.setStrokeWidth(8); }
- 调色板,改变画笔颜色 - public void green(View v){ paint.setColor(Color.GREEN); }
- 保存图片至SD卡 - public void save(View v){ File file = new File("sdcard/dazuo2.png"); FileOutputStream fos; try { fos = new FileOutputStream(file); //保存 bmCopy.compress(CompressFormat.PNG, 100, fos); } catch (FileNotFoundException e) { e.printStackTrace(); } //发送sd卡就绪广播,触发sd卡的遍历 Intent intent = new Intent(); intent.setAction(Intent.ACTION_MEDIA_MOUNTED); intent.setData(Uri.fromFile(Environment.getExternalStorageDirectory())); sendBroadcast(intent); }
- 系统每次收到SD卡就绪广播时,都会去遍历sd卡的所有文件和文件夹,把遍历到的所有多媒体文件都在MediaStore数据库保存一个索引,这个索引包含多媒体文件的文件名、路径、大小
- 图库每次打开时,并不会去遍历sd卡获取图片,而是通过内容提供者从MediaStore数据库中获取图片的信息,然后读取该图片 
- 系统开机或者点击加载sd卡按钮时,系统会发送sd卡就绪广播,我们也可以手动发送就绪广播 - Intent intent = new Intent(); intent.setAction(Intent.ACTION_MEDIA_MOUNTED); intent.setData(Uri.fromFile(Environment.getExternalStorageDirectory())); sendBroadcast(intent);
#五、撕衣服
- 原理:把穿内衣和穿外衣的照片重叠显示,内衣照在下面,用户滑动屏幕时,触摸的是外衣照,把手指经过的像素都置为透明,内衣照就显示出来了
- 步骤:
- 1.相对布局 外衣图片与内衣图片重叠,内衣上外衣下
- 2.在手机屏幕上加载外衣原画,并显示外衣图片副本
- 3.设置触摸侦听,移动的位置颜色变透明, - //初始化外衣图片控件 iv = (ImageView) findViewById(R.id.iv); //为图片控件设置触摸侦听 iv.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { // TODO Auto-generated method stub switch (event.getAction()) {//判断手指触屏事件 //手指在屏幕上滑动时 case MotionEvent.ACTION_MOVE: //得到相对图片的坐标 int x = (int) event.getX(); int y = (int) event.getY(); //设置坐标方圆5像素都变为透明 for (int i = -5; i < 5; i++) { for (int j = -5; j < 5; j++) { if(Math.sqrt(i*i + j*j) <= 5){ //把指定坐标的像素置为指定颜色 if(x + i < bmCopy.getWidth() && x + i >= 0 && y + j < bmCopy.getHeight() && y + j >= 0){ bmCopy.setPixel(x + i, y + j, Color.TRANSPARENT); iv.setImageBitmap(bmCopy); } } } } break; } return true; } });
- 每次只设置一个像素点太慢,以触摸的像素为圆心,半径为5画圆,圆内的像素全部置为透明 - for (int i = -5; i < 6; i++) { for (int j = -5; j < 6; j++) { if(Math.sqrt(i * i + j * j) <= 5) copyBm.setPixel(newX + i, newY + j, Color.TRANSPARENT); } }
#六、音乐播放器
##6.1播放服务
- 1.创建音乐服务(播放音频的代码应该运行在服务中,定义一个播放服务MusicService)
- 1.1 MusicService服务里定义play、stop、pause、continuePlay等方法 - //定义媒体播放器(MediaPlayer)控件的视图,此视图包含了一些典型的按钮,像"播放(Play)/暂停(Pause)", "倒带(Rewind)", "快进(Fast Forward)"与进度滑动器(progress slider)。 //媒体播放器在服务启动时,就要被创建,要提全局 MediaPlayer player; @Override public void onCreate() { super.onCreate(); player = new MediaPlayer(); } //服务内部定义开始音乐功能, private void play() { //重置,进入空闲状态 player.reset(); try { //设置音频文件来源,进入初始化状态 player.setDataSource("sdcard/bzj.mp3"); //player.setDataSource("http://192.168.15.77:8080/bzj.mp3"); //进入准备状态 //player.prepare(); //异步准备 player.prepareAsync(); //异步准备需要设置准备侦听,准备好就开始播放 player.setOnPreparedListener(new OnPreparedListener() { @Override public void onPrepared(MediaPlayer mp) { //开始播放 player.start(); addTimer(); } }); } catch (Exception e) { e.printStackTrace(); } } //服务内部定义暂停音乐功能 private void pause() { player.pause(); } private void stop() { player.stop(); } //服务内部定义继续音乐功能 private void continuePlay() { player.start(); }
- 1.2 把play、stop、pause、continuePlay等方法这几个方法抽取成一个接口MusicInterface - public interface MusicControllerInterface { void play(); void pause(); void continuePlay(); void seekTo(int progress); }
- 1.3 服务中定义一个中间人类,继承Binder,实现MusicInterface - class MusicController extends Binder implements MusicControllerInterface{ @Override public void play() { MusicService.this.play(); } @Override public void pause() { MusicService.this.pause(); } @Override public void continuePlay() { MusicService.this.continuePlay(); } @Override public void seekTo(int progress) { MusicService.this.seekTo(progress); } }
- 1.4 为音乐服务进行清单配置 - <service android:name="com.itheima.musicplayer.MusicService"></service>
- Activity界面中混合启动startService服务(启动MusicService服务,再bind服务) - //UI界面已启动,执行此方法 protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //启动音乐服务意图(很显然是显示启动) Intent intent = new Intent(this, MusicService.class); //创建通讯频道对象,将用与获取中间人 MusicConnection conn = new MusicConnection(); sb = (SeekBar) findViewById(R.id.sb); sb.setOnSeekBarChangeListener(new OnSeekBarChangeListener() { //停止划动 @Override public void onStopTrackingTouch(SeekBar seekBar) { //获取用户停止划动后的进度 int progress = seekBar.getProgress(); //改变音乐播放进度 mci.seekTo(progress); } //开始划动 @Override public void onStartTrackingTouch(SeekBar seekBar) { } //正在划动 @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { } }); //------------------------------------------ //混合启动 //start是为了让进程变成服务进程 startService(intent); //bind是为了拿到中间人对象 bindService(intent, conn, BIND_AUTO_CREATE); }
 
##6.2根据播放进度设置进度条
- 获取当前的播放时间和当前音频的最长时间 - int currentPosition = player.getCurrentPosition(); int duration = player.getDuration();
- 播放进度需要不停的获取,不停的刷新进度条,使用计时器每500毫秒获取一次播放进度
- 发消息至Handler,把播放进度放进Message对象中,在Handler中更新SeekBar的进度 - Timer timer = new Timer(); timer.schedule(new TimerTask() { @Override public void run() { //获取歌曲当前播放的时间点 int currentPosition = player.getCurrentPosition(); //获取歌曲总时长 int duration = player.getDuration(); //获取消息对象 //Message msg = Message.obtain(); Message msg =MainActivity.handler.obtainMessage(); //把播放进度存入Message中 Bundle data = new Bundle(); data.putInt("currentPosition", currentPosition); data.putInt("duration", duration); msg.setData(data); // 将消息发送到消息队列 MainActivity.handler.sendMessage(msg); } }, 5, 500);
- 在Activity中定义Handler - static Handler handler = new Handler(){ public void handleMessage(android.os.Message msg) { //取出消息携带的数据 Bundle data = msg.getData(); int currentPosition = data.getInt("currentPosition"); int duration = data.getInt("duration"); //设置播放进度 sb.setMax(duration); sb.setProgress(currentPosition); }; };
##6.3拖动进度条改变播放进度
- 1.在UI界面获取进度条控件,并对其设置拖动侦听,调用服务里的改变音乐播放进度方法(把进度参数传过去) - //初始化SeekBar音乐滚动条 sb = (SeekBar) findViewById(R.id.sb); //给sb设置一个拖动侦听 sb.setOnSeekBarChangeListener(new OnSeekBarChangeListener() { //停止拖动时调用 @Override public void onStopTrackingTouch(SeekBar seekBar) { //获取用户停止划动后的进度 int progress = seekBar.getProgress(); //改变音乐播放进度,调用音乐服务抽取出来的接口方法,去改变音乐播放进度 mci.seekTo(progress); } //开始拖动时调用 @Override public void onStartTrackingTouch(SeekBar seekBar) { } //拖动的时候不断调用 @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { } });
- 2.定义方法:在音乐服务中定义根据进度参数改变音乐播放进度的方法 - public void seekTo(int progress){ player.seekTo(progress); }
#七、视频播放器
##7.1SurfaceView
- 对画面的实时更新要求较高
- 双缓冲技术:内存中有两个画布,A画布显示至屏幕,B画布在内存中绘制下一帧画面,绘制完毕后B显示至屏幕,A在内存中继续绘制下一帧画面
- 播放视频也是用MediaPlayer,不过跟音频不同,要设置显示在哪个SurfaceView - SurfaceView sv = (SurfaceView) findViewById(R.id.sv); SurfaceHolder sh = sv.getHolder(); MediaPlayer player = new MediaPlayer(); player.reset(); try { player.setDataSource("sdcard/2.3gp"); player.setDisplay(sh); player.prepare(); } catch (Exception e) { e.printStackTrace(); } player.start();
- SurfaceView是重量级组件,可见时才会创建
- 给SurfaceHolder设置CallBack,类似于侦听,可以知道SurfaceView的状态 - //看做侦听,可以知道SurfaceView的状态,并执行相对应方法 sh.addCallback(new Callback() { //SurfaceView一旦不可见,就会被销毁,SurfaceView销毁时调用 @Override public void surfaceDestroyed(SurfaceHolder holder) { // TODO Auto-generated method stub } //SurfaceView一旦可见,就会被创建SurfaceView创建时调用 @Override public void surfaceCreated(SurfaceHolder holder) { // TODO Auto-generated method stub } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { // TODO Auto-generated method stub } });
- SurfaceView一旦不可见,就会被销毁,一旦可见,就会被创建,销毁时停止播放,再次创建时再开始播放
–视频播放器示例
public class MainActivity extends Activity {
    private MediaPlayer player;
    int progress = 0;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //获取显示视频的控件
        SurfaceView sv = (SurfaceView) findViewById(R.id.sv);
        //获取控制器
        final SurfaceHolder holder = sv.getHolder();
//        Thread t = new Thread(){
//            @Override
//            public void run() {
//                try {
//                    sleep(200);
//                } catch (InterruptedException e) {
//                    // TODO Auto-generated catch block
//                    e.printStackTrace();
//                }
//                
//                runOnUiThread(new Runnable() {
//                    
//                    @Override
//                    public void run() {
//                        MediaPlayer player = new MediaPlayer();
//                        player.reset();
//                        try {
//                            player.setDataSource("sdcard/2.3gp");
//                            //指定视频画面播放在哪个组件
//                            player.setDisplay(holder);
//                            player.prepare();
//                            player.start();
//                        } catch (Exception e) {
//                            // TODO Auto-generated catch block
//                            e.printStackTrace();
//                        } 
//                        
//                    }
//                });
//            }
//        };
//        t.start();
        //给SurfaceHolder设置CallBack,类似于侦听,可以知道SurfaceView的状态
        holder.addCallback(new Callback() {
            //SurfaceView摧毁时调用
            @Override
            public void surfaceDestroyed(SurfaceHolder holder) {
                if(player != null){
                    //每次SurfaceView摧毁时记录播放进度
                    progress = player.getCurrentPosition();
                    player.stop();
                    player.release();
                    player = null;
                }
            }
            //SurfaceView创建时调用
            @Override
            public void surfaceCreated(SurfaceHolder holder) {
                if(player == null){
                    player = new MediaPlayer();
                    player.reset();
                    try {
                        //指定要播放的资源
                        player.setDataSource("sdcard/2.3gp");
                        //指定视频画面播放在哪个组件
                        player.setDisplay(holder);
                        //进入准备状态
                        player.prepare();
                        //跳到先前的进度
                        player.seekTo(progress);
                        //开始播放
                        player.start();
                    } catch (Exception e) {
                        e.printStackTrace();
                    } 
                }
            }
            //SurfaceView结构改变时调用
            @Override
            public void surfaceChanged(SurfaceHolder holder, int format, int width,
                    int height) {
                // TODO Auto-generated method stub
            }
        });
    }
}
##7.2 VideoView 使用方法简单播放视频
public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        VideoView vv = (VideoView) findViewById(R.id.vv);
        vv.setVideoPath("sdcard/2.3gp");
        vv.start();
    }
}
#八、摄像头
- 8.1启动系统提供的拍照程序 - public void click1(View v){ //隐式启动系统提供的拍照Activity Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); //设置照片的保存路径 File file = new File(Environment.getExternalStorageDirectory(), "haha.jpg"); intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(file)); //arg1:请求码,用于匹配 startActivityForResult(intent, 0); }
- 8.2启动系统提供的摄像程序 - public void click2(View v){ Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE); //设置摄像的保存路径 File file = new File(Environment.getExternalStorageDirectory(), "haha.3gp"); intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(file)); //设置保存视频文件的质量 intent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 1); startActivityForResult(intent, 0); }
- 8.3 启动系统提供的拍照Activity和启动系统提供的摄像Activity示例: - public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } public void click1(View v){ //启动系统提供的拍照Activity Intent intent = new Intent(); intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE); intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File("sdcard/haha.jpg"))); startActivityForResult(intent, 10);//开启拍照并设置请求码 } public void click2(View v){ //启动系统提供的摄像Activity Intent intent = new Intent(); intent.setAction(MediaStore.ACTION_VIDEO_CAPTURE); intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File("sdcard/haha2.3gp"))); intent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 1); startActivityForResult(intent, 20);//开启摄像并设置请求码 } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); //判断请求码,进行操作 if(requestCode == 10){ Toast.makeText(this, "拍照完成", 0).show(); } else if(requestCode == 20){ Toast.makeText(this, "摄像完成", 0).show(); } } }
#9 Camera照相机
- 1.设置UI界面的Activity
public class MainActivity extends Activity {
            private static final String TAG = “Camera”;
             private Camera mCamera;
             private CameraPreview mPreview;
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            if(checkCameraHardware(this)){
                // 创建摄像头实例
                mCamera = getCameraInstance();
                // 创建预览界面
                mPreview = new CameraPreview(this, mCamera);
                FrameLayout preview = (FrameLayout) findViewById(R.id.camera_preview);
                //把SurfaceView设置为帧布局的子节点
                preview.addView(mPreview);
             // 给拍照按钮设置点击侦听
                Button captureButton = (Button) findViewById(R.id.button_capture);
                captureButton.setOnClickListener(
                    new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            //自动对焦
                            mCamera.autoFocus(new AutoFocusCallback() {
                                //自动对焦完成时,此方法调用
                                @Override
                                public void onAutoFocus(boolean success, Camera camera) {
                                    // 拍照
                                    mCamera.takePicture(null, null, mPicture);
                                }
                            });
                        }
                    }
                );
            }
            else{
                Toast.makeText(this, "连摄像头都没有,穷逼", 0).show();
            }
        }
    /** 检测是否有摄像头 */
    private boolean checkCameraHardware(Context context) {
        //检测是否具有指定的系统功能
        if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)){
            // 有摄像头
            return true;
        } else {
            // 没有摄像头
            return false;
        }
    }
    /** 这是一个获取摄像头实例的安全的途径 */
    public static Camera getCameraInstance(){
        Camera c = null;
        try {
            //返回后置摄像头的实例,如果没有后置摄像头,返回空
            c = Camera.open(); // attempt to get a Camera instance
        }
        catch (Exception e){
            // Camera is not available (in use or does not exist)
        }
        return c; // returns null if camera is unavailable
    }
    private PictureCallback mPicture = new PictureCallback() {
        //摄像头照相方法执行时,会调用此方法
        @Override
        public void onPictureTaken(byte[] data, Camera camera) {
            //指定相片保存的路径和文件名
            File pictureFile = new File("sdcard/ohohoho2.jpg");
            try {
                FileOutputStream fos = new FileOutputStream(pictureFile);
                fos.write(data);
                fos.close();
            } catch (FileNotFoundException e) {
                Log.d(TAG, "File not found: " + e.getMessage());
            } catch (IOException e) {
                Log.d(TAG, "Error accessing file: " + e.getMessage());
            }
        }
    };
}
- 2.设置摄像头预览类CameraPreview - /* 摄像头预览类 / - public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback { private static final String TAG = "Camera"; private SurfaceHolder mHolder; private Camera mCamera; @SuppressWarnings("deprecation") public CameraPreview(Context context, Camera camera) { super(context); mCamera = camera; // Install a SurfaceHolder.Callback so we get notified when the // underlying surface is created and destroyed. // 获取SurfaceView的holder mHolder = getHolder(); // 设置侦听,侦听SurfaceView的摧毁和创建 mHolder.addCallback(this); // 3.0之后,会自动设置 mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); } public void surfaceCreated(SurfaceHolder holder) { // The Surface has been created, now tell the camera where to draw the preview. try { //指定摄像头的预览界面显示在哪个SurfaceView中 mCamera.setPreviewDisplay(holder); //开始预览 mCamera.startPreview(); } catch (IOException e) { Log.d(TAG, "Error setting camera preview: " + e.getMessage()); } } public void surfaceDestroyed(SurfaceHolder holder) { // empty. Take care of releasing the Camera preview in your activity. } public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { // If your preview can change or rotate, take care of those events here. // Make sure to stop the preview before resizing or reformatting it. if (mHolder.getSurface() == null){ // preview surface does not exist return; } // stop preview before making changes try { mCamera.stopPreview(); } catch (Exception e){ // ignore: tried to stop a non-existent preview } // set preview size and make any resize, rotate or // reformatting changes here // start preview with new settings try { mCamera.setPreviewDisplay(mHolder); mCamera.startPreview(); } catch (Exception e){ Log.d(TAG, "Error starting camera preview: " + e.getMessage()); } } }
- 3.设置清单文件 
<uses-permission android:name="android.permission.CAMERA" />
  <uses-feature android:name="android.hardware.camera" />
  <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
- 4.布局文件
  <?xml version=”1.0” encoding=”utf-8”?>
        
          
  <Button
    android:id="@+id/button_capture"
    android:text="拍照"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    />
</LinearLayout>