码迷,mamicode.com
首页 > 其他好文 > 详细

10.多媒体

时间:2015-10-26 22:17:07      阅读:357      评论:0      收藏:0      [点我收藏+]

标签:

通知

通知(Notification)是 Android系统中比较有特色的一个功能,当某个应用程序希望向用户发出一些提示信息,而该应用程序又不在前台运行时,就可以借助通知来实现。发出一条通知后,手机最上方的状态栏中会显示一个通知的图标,下拉状态栏后可以看到通知的详细内容。
 
通知的基本用法
通知的用法比较灵活,既可以在活动里创建,也可以在广播接收器里创建,当然还可以服务里创建。相比于广播接收器和服务,在活动里创建通知的场景还是比较少的,因为一般只有当程序进入到后台的时候才需要使用通知。
  1. publicclassMainActivityextendsActivityimplementsOnClickListener
  2. {
  3. /** Called when the activity is first created. */
  4. privateButton sendNotice;
  5. @Override
  6. publicvoid onCreate(Bundle savedInstanceState)
  7. {
  8. super.onCreate(savedInstanceState);
  9. setContentView(R.layout.main);
  10. sendNotice =(Button) findViewById(R.id.send_notice);
  11. sendNotice.setOnClickListener(this);
  12. }
  13. @Override
  14. publicvoid onClick(View v){
  15. switch(v.getId()){
  16. case R.id.send_notice:
  17. //1.需要一个NotificationManager来对通知进行管理,可以调用Context的getSystemService()方法获取到。 getSystemService方法接收一个字符串参数用于确定获取系统的哪个服务,这里传入 Context.NOTIFICATION_SERVICE 即可
  18. NotificationManager manager =(NotificationManager)getSystemService(NOTIFICATION_SERVICE);
  19. //2.创建一个Notification对象,这个对象用于存储通知所需的各种信息,可以使用它的有参构造函数来进行创建。Notification的有参构造函数接收三个参数,第一个参数用于指定通知的图标,比如项目的res/drawable目录下有一张icon.png图片,那么这里就可以传入R.drawable.icon。
  20. // 第二个参数用于指定通知的ticker内容,当通知刚被创建的时候,它会在系统的状态栏一闪而过,属于一种瞬时的提示信息。
  21. // 第三个参数用于指定通知被创建的时间,以毫秒为单位,当下拉系统状态栏时,这里指定的时间会显示在相应的通知上
  22. Notification notification =newNotification(R.drawable.ic_launcher,"This is ticker text",
  23. System.currentTimeMillis());
  24. //3.对通知的布局进行设定,这里需要调用Notification的setLatestEventInfo()方法就可以给通知设置一个标准的布局。
  25. // 这个方法接收四个参数,第一个参数是Context,第二个参数用于指定通知的标题内容,下拉系统状态栏就可以看到这部分内容。第三个参数用于指定通知的正文内容,
  26. // 同样下拉系统状态栏就可以看到这部分内容。第四个参数我们暂时还用不到,可以先传入null
  27. //notification.setLatestEventInfo(this, "This is content title","This is content text", null);
  28. //android手机通知是可以点击的。但是当你去点击它的时候,会发现没有任何效果。其实要想实现通知的点击效果,还需要在代码中进行相应的设置,这就涉及到了一个新的概念,PendingIntent。
  29. //PendingIntent从名字上看起来就和Intent有些类似,它们之间也确实存在着不少共同点。比如它们都可以去指明某一个“意图”,都可以用于启动活动、启动服务以及发送广播等。不同的是,
  30. //Intent更加倾向于去立即执行某个动作,而PendingIntent更加倾向于在某个合适的时机去执行某个动作。所以,也可以把PendingIntent简单地理解为延迟执行的Intent。PendingIntent的用法同样很简单,
  31. //它主要提供了几个静态方法用于获取PendingIntent的实例,可以根据需求来选择是使用getActivity()方法、getBroadcast()方法、还是getService()方法。这几个方法所接收的参数都是相同的,
  32. //第一个参数依旧是Context,第二个参数一般用不到,通常都是传入0即可。第三个参数是一个Intent对象,我们可以通过这个对象构建出PendingIntent的“意图”。第四个参数用于确定PendingIntent的行为,
  33. //有FLAG_ONE_SHOT、FLAG_NO_CREATE、FLAG_CANCEL_CURRENT和FLAG_UPDATE_CURRENT这四种值可选,具体查看文档。Notification 的setLatestEventInfo()方法的第四个正是一个PendingIntent对象。
  34. //因此,这里就可以通过PendingIntent构建出一个延迟执行的“意图”,当用户点击这条通知时就会执行相应的逻辑。现在优化NotificationTest项目,给刚才的通知加上点击功能,让用户点击它的时候可以启动另一个活动。
  35. Intent intent =newIntent(this,NotificationActivity.class);
  36. PendingIntent pi =PendingIntent.getActivity(this,0, intent,PendingIntent.FLAG_CANCEL_CURRENT);
  37. notification.setLatestEventInfo(this,"This is content title","This is content text", pi);
  38. //4.调用NotificationManager的notify()方法可以让通知显示出来了。notify()方法接收两个参数,第一个参数是id,
  39. // 要保证为每个通知所指定的id都是不同的。第二个参数则是Notification对象,这里直接将创建好的Notification对象传入即可
  40. manager.notify(1, notification);
  41. //Notification这个类的sound这个属性可以在通知发出的时候播放一段音频,这样就能够更好地告知用户有通知到来。sound这个属性是一个Uri对象,所以在指定音频文件的时候还需要先获取到音频文件对应的URI。
  42. //比如说,手机的/system/media/audio/ringtones目录下有一个Basic_tone.ogg音频文件,就可以写成:
  43. Uri soundUri =Uri.fromFile(newFile("/system/media/audio/ringtones/Basic_tone.ogg"));
  44. notification.sound = soundUri;
  45. //使用的是vibrate这个属性可以振动。它是一个长整型的数组,用于设置手机静止和振动的时长,以毫秒为单位。下标为0的值表示手机静止的时长,下标为1的值表示手机振动的时长,下标为2的值又表示手机静止的时长,
  46. //以此类推。所以,如果想要让手机在通知到来的时候立刻振动1秒,然后静止1秒,再振动1秒,还得需要权限,代码就可以写成:
  47. long[] vibrates ={0,1000,1000,1000};
  48. notification.vibrate = vibrates;
  49. //现在的手机基本上都会前置一个LED灯,当有未接电话或未读短信,而此时手机又处于锁屏状态时,LED灯就会不停地闪烁,提醒用户去查看。可以使用ledARGB、ledOnMS、ledOffMS以及flags这几个属性来实现这种效果。
  50. //ledARGB用于控制LED灯的颜色,一般有红绿蓝三种颜色可选。ledOnMS用于指定LED灯亮起的时长,以毫秒为单位。ledOffMS用于指定LED灯暗去的时长,也是以毫秒为单位。flags可用于指定通知的一些行为,
  51. //其中就包括显示LED灯这一选项。所以,当通知到来时,如果想要实现LED灯以绿色的灯光一闪一闪的效果,就可以写成:
  52. notification.ledARGB =Color.GREEN;
  53. notification.ledOnMS =1000;
  54. notification.ledOffMS =1000;
  55. notification.flags =Notification.FLAG_SHOW_LIGHTS;
  56. //如果不想进行那么多繁杂的设置,也可以直接使用通知的默认效果,它会根据当前手机的环境来决定播放什么铃声,以及如何振动,写法如下:
  57. notification.defaults =Notification.DEFAULT_ALL;
  58. break;
  59. default:
  60. break;
  61. }
  62. }
  63. }
  1. publicclassNotificationActivityextendsActivity
  2. {//通知activity
  3. protectedvoid onCreate(Bundle savedInstanceState){
  4. super.onCreate(savedInstanceState);
  5. setContentView(R.layout.notification_layout);
  6. //如果没有在代码中对该通知进行取消,它就会一直显示在系统的状态栏上显示。解决的方法也很简单,
  7. //调用NotificationManager的cancel()方法就可以取消通知了。这里在cancel()方法中传入了1,
  8. //这个1是什么意思呢?在创建通知的时候给每条通知指定的id,当时给这条通知设置的id就是1。
  9. //因此,如果想要取消哪一条通知,就在cancel()方法中传入该通知的id就行了。
  10. NotificationManager manager =(NotificationManager)getSystemService
  11. (NOTIFICATION_SERVICE);
  12. manager.cancel(1);
  13. }
  14. }
 

多媒体编程

  • 文本、图片、音频、视频

    图片

  • 图片的总大小 = 图片的总像素 * 每个像素占用的大小
  • 单色位图:只能表示2种颜色

    • 使用两个数字:0和1

    • 使用一个长度为1的二进制数字就可以表示了

    • 每个像素占用1/8个字节
  • 16色位图:能表示16种颜色
    • 需要16个数字:0-15,0000 - 1111
    • 使用一个长度为4的二进制数组就可以表示了
    • 每个像素占用1/2个字节
  • 256色位图:能表示256种颜色
    • 需要256个数字:0 - 255,0000 0000 - 1111 1111
    • 使用一个长度为8的二进制数字
    • 每个像素占用1个字节
  • 24位位图:
    • 每个像素占用24位,也就是3个字节,所在叫24位位图
    • R:0-255,需要一个长度为8的二进制数字,占用1个字节
    • G:0-255,需要一个长度为8的二进制数字,占用1个字节
    • B:0-255,需要一个长度为8的二进制数字,占用1个字节

加载大图片

  • 计算机把图片所有像素信息全部解析出来,保存至内存
  • Android保存图片像素信息,是用ARGB保存,所以每个像素占用4个字节,很容易内存溢出
  • 手机屏幕320*480,总像素:153600
  • 图片宽高2400*3200,总像素7680000
  • 2400 / 320 = 7
  • 3200 / 480 = 6

1.首先对图片进行缩放

  • 获取屏幕宽高

    Display dp = getWindowManager().getDefaultDisplay();
    int screenWidth = dp.getWidth();
    int screenHeight = dp.getHeight();
  • 获取图片宽高

    Options opts =newOptions();
    //请求图片属性但不申请内存
    opts.inJustDecodeBounds =true;
    BitmapFactory.decodeFile("sdcard/dog.jpg", opts);
    int imageWidth = opts.outWidth;
    int imageHeight = opts.outHeight;
  • 图片的宽高除以屏幕宽高,算出宽和高的缩放比例,取较大值作为图片的缩放比例,且大于1才缩放

    int scale =1;
    int scaleX = imageWidth / screenWidth;
    int scaleY = imageHeight / screenHeight;
    if(scaleX >= scaleY && scaleX >1){
    scale = scaleX;
    }
    elseif(scaleY > scaleX && scaleY >1){
    scale = scaleY;
    }

2.然后按缩放比例加载图片

//设置缩放比例
opts.inSampleSize = scale;
//为图片申请内存
opts.inJustDecodeBounds =false;
Bitmap bm =BitmapFactory.decodeFile("sdcard/dog.jpg", opts);
iv.setImageBitmap(bm);
 
 

创建图片副本

  • 在内存中创建一个图片的拷贝
  • 保存图片
  • SD每次准备的时候,系统其实是遍历sd卡所有文件,系统会把所有的多媒体文件,都在MediaStore数据库中生成一个索引,数据库中保存了文件的文件名、路径、大小、长度和艺术家
  • 图库、音乐、视频程序每次启动时,其实不会去遍历sd卡寻找多媒体文件,而是直接从MediaStore数据库中读取多媒体文件,通过库中的索引找到对应的多媒体文件后,把文件显示在界面

直接加载的bitmap对象是只读的,无法修改,要修改图片只能在内存中创建出一个一模一样的bitmap副本,然后修改副本

//加载原图
Bitmap srcBm =BitmapFactory.decodeFile("sdcard/photo3.jpg");
 
//创建与原图大小一致的空白bitmap
Bitmap copyBm =Bitmap.createBitmap(srcBm.getWidth(), srcBm.getHeight(), srcBm.getConfig());
//定义画笔
Paint paint =newPaint();
//把纸铺在画版上
Canvas canvas =newCanvas(copyBm);
//把srcBm的内容绘制在copyBm上
canvas.drawBitmap(srcBm,newMatrix(), paint);
 
iv_copy.setImageBitmap(copyBm);
 

对图片进行特效处理

  • 首先定义一个矩阵对象

    Matrix mt =newMatrix();
  • 缩放效果

    //x轴缩放1倍,y轴缩放0.5倍
    mt.setScale(1,0.5f);
  • 旋转效果

    //以copyBm.getWidth() / 2, copyBm.getHeight() / 2点为轴点,顺时旋转30度
    mt.setRotate(30, copyBm.getWidth()/2, copyBm.getHeight()/2);
  • 平移

    //x轴坐标+10,y轴坐标+20
    mt.setTranslate(10,20);
  • 镜面

    //把X坐标都变成负数
    mt.setScale(-1,1);
    //图片整体向右移
    mt.postTranslate(copyBm.getWidth(),0);
  • 倒影

    //把Y坐标都变成负数
    mt.setScale(1,-1);
    //图片整体向下移
    mt.postTranslate(0, copyBm.getHeight());

 

画画板

记录用户触摸事件的XY坐标,绘制直线

  • 给ImageView设置触摸侦听,得到用户的触摸事件,并获知用户触摸ImageView的坐标
iv.setOnTouchListener(newOnTouchListener(){
 
@Override
publicboolean onTouch(View v,MotionEventevent){
// TODO Auto-generated method stub
switch(event.getAction()){
//触摸屏幕
caseMotionEvent.ACTION_DOWN:
//得到触摸屏幕时手指的坐标
startX =(int)event.getX();
startY =(int)event.getY();
break;
//在屏幕上滑动
caseMotionEvent.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;
 
}
returntrue;
}
});
  • 刷子效果,加粗画笔

    paint.setStrokeWidth(8);
  • 调色板,改变画笔颜色

    paint.setColor(Color.GREEN);
  • 保存图片至SD卡

    FileOutputStream fos =null;
    try{
    fos =newFileOutputStream(newFile("sdcard/dazuo.png"));
    }catch(FileNotFoundException e){
    // TODO Auto-generated catch block
    e.printStackTrace();
    }
    //保存图片
    copyBm.compress(CompressFormat.PNG,100, fos);
  • 系统每次收到SD卡就绪广播时,都会去遍历sd卡的所有文件和文件夹,把遍历到的所有多媒体文件都在MediaStore数据库保存一个索引,这个索引包含多媒体文件的文件名、路径、大小
  • 图库每次打开时,并不会去遍历sd卡获取图片,而是通过内容提供者从MediaStore数据库中获取图片的信息,然后读取该图片
  • 系统开机或者点击加载sd卡按钮时,系统会发送sd卡就绪广播,我们也可以手动发送就绪广播

    Intent intent =newIntent();
    intent.setAction(Intent.ACTION_MEDIA_MOUNTED);
    intent.setData(Uri.fromFile(Environment.getExternalStorageDirectory()));
    sendBroadcast(intent);

 

撕衣服

  • 原理:把穿内衣和穿外衣的照片重叠显示,内衣照在下面,用户滑动屏幕时,触摸的是外衣照,把手指经过的像素都置为透明,内衣照就显示出来了

    iv.setOnTouchListener(newOnTouchListener(){
     
    @Override
    publicboolean onTouch(View v,MotionEventevent){
    switch(event.getAction()){
    caseMotionEvent.ACTION_MOVE:
    int newX =(int)event.getX();
    int newY =(int)event.getY();
    //把指定的像素变成透明
    copyBm.setPixel(newX, newY,Color.TRANSPARENT);
    iv.setImageBitmap(copyBm);
    break;
     
    }
    returntrue;
    }
    });
  • 每次只设置一个像素点太慢,以触摸的像素为圆心,半径为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);
    }
    }

 

音乐播放器

播放服务

  • 播放音频的代码应该运行在服务中,定义一个播放服务MusicService
  • 服务里定义play、stop、pause、continuePlay等方法

    privatevoid play(){
    // TODO Auto-generated method stub
    player.reset();
    try{
    player.setDataSource("sdcard/bzj.mp3");
    player.prepare();
    }catch(Exception e){
    // TODO Auto-generated catch block
    e.printStackTrace();
    }
    player.start();
     
    }
    privatevoid pause(){
    player.pause();
    }
    privatevoid stop(){
    player.stop();
    }
    privatevoid continuePlay(){
    player.start();
    }
  • 把这几个方法抽取成一个接口MusicInterface
  • 定义一个中间人类,继承Binder,实现MusicInterface
  • 先start启动MusicService,再bind

    Intent intent =newIntent(this,MusicService.class);
    startService(intent);
    bindService(intent, conn, BIND_AUTO_CREATE);

    根据播放进度设置进度条

  • 获取当前的播放时间和当前音频的最长时间

    int currentPosition = player.getCurrentPosition();
    int duration = player.getDuration();
  • 播放进度需要不停的获取,不停的刷新进度条,使用计时器每500毫秒获取一次播放进度
  • 发消息至Handler,把播放进度放进Message对象中,在Handler中更新SeekBar的进度

    Timer timer =newTimer();
    timer.schedule(newTimerTask(){
     
    @Override
    publicvoid run(){
    int currentPosition = player.getCurrentPosition();
    int duration = player.getDuration();
    Message msg =Message.obtain();
    //把播放进度存入Message中
    Bundle data =newBundle();
    data.putInt("currentPosition", currentPosition);
    data.putInt("duration", duration);
    msg.setData(data);
    MainActivity.handler.sendMessage(msg);
    }
    },5,500);
  • 在Activity中定义Handler

    staticHandler handler =newHandler(){
    publicvoid 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);
    };
    };

    拖动进度条改变播放进度

    //给sb设置一个拖动侦听
    sb.setOnSeekBarChangeListener(newOnSeekBarChangeListener(){
    //停止拖动时调用
    @Override
    publicvoid onStopTrackingTouch(SeekBar seekBar){
    // TODO Auto-generated method stub
    int progress = seekBar.getProgress();
    mi.seekTo(progress);
    }
    //开始拖动时调用
    @Override
    publicvoid onStartTrackingTouch(SeekBar seekBar){
    // TODO Auto-generated method stub
     
    }
    //拖动的时候不断调用
    @Override
    publicvoid onProgressChanged(SeekBar seekBar,int progress,
    boolean fromUser){
    // TODO Auto-generated method stub
     
    }
    });

视频播放器

SurfaceView

  • 对画面的实时更新要求较高
  • 重量级组件
  • 只要不可见,就不会创建,可见时,才会创建
  • 只要不可见,就会销毁
  • 双缓冲技术:内存中有两个画布,A画布显示至屏幕,B画布在内存中绘制下一帧画面,绘制完毕后B显示至屏幕,A在内存中继续绘制下一帧画面
  • 播放视频也是用MediaPlayer,不过跟音频不同,要设置显示在哪个SurfaceView

    SurfaceView sv =(SurfaceView) findViewById(R.id.sv);
    SurfaceHolder sh = sv.getHolder();
     
    MediaPlayer player =newMediaPlayer();
    player.reset();
    try{
    player.setDataSource("sdcard/2.3gp");
    player.setDisplay(sh);
    player.prepare();
    }catch(Exception e){
    e.printStackTrace();
    }
    player.start();
  • SurfaceView是重量级组件,可见时才会创建
  • 给SurfaceHolder设置CallBack,类似于侦听,可以知道SurfaceView的状态

    sh.addCallback(newCallback(){
    //SurfaceView销毁时调用
    @Override
    publicvoid surfaceDestroyed(SurfaceHolder holder){
    // TODO Auto-generated method stub
     
    }
    //SurfaceView创建时调用
    @Override
    publicvoid surfaceCreated(SurfaceHolder holder){
    // TODO Auto-generated method stub
     
    }
     
    @Override
    publicvoid surfaceChanged(SurfaceHolder holder,int format,int width,
    int height){
    // TODO Auto-generated method stub
     
    }
    });
  • SurfaceView一旦不可见,就会被销毁,一旦可见,就会被创建,销毁时停止播放,再次创建时再开始播放

 

 

FFMPEG

  • 开源免费的音视频编解码器

    Vitamio

  • 封装了FFMPEG的视频播放框架
  • 对外提供的api全部都是javaAp

 

摄像头

  • 启动系统提供的拍照程序

    //隐式启动系统提供的拍照Activity
    Intent intent =newIntent(MediaStore.ACTION_IMAGE_CAPTURE);
    //设置照片的保存路径
    File file =newFile(Environment.getExternalStorageDirectory(),"haha.jpg");
    intent.putExtra(MediaStore.EXTRA_OUTPUT,Uri.fromFile(file));
    startActivityForResult(intent,0);
  • 启动系统提供的摄像程序

    Intent intent =newIntent(MediaStore.ACTION_VIDEO_CAPTURE);
     
    File file =newFile(Environment.getExternalStorageDirectory(),"haha.3gp");
    intent.putExtra(MediaStore.EXTRA_OUTPUT,Uri.fromFile(file));
    //设置保存视频文件的质量
    intent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY,1);
    startActivityForResult(intent,0);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 





10.多媒体

标签:

原文地址:http://www.cnblogs.com/liuyu0529/p/4912473.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!