【0050】Android基础-38-多媒体(图片、音频、视频)

【关键点】

【1】在Android中的图片显示的格式是ARGB(透明度);

【2】png 格式 Android采用的是png格式

【1】计算机表示图形的几种方式

 图形的大小 = 图片的总像素 * 每个像素的大小

  单色  每个像素最多可以表示2种颜色  只需要使用长度为1的二进制位来表示  那么每个像素占1/8byte 

  16色  每个像素最多可以表示16种颜色 0000 - 1111 那么只需要使用长度为4的二进制表示 那么每个像素占1/2个byte

  256色 每个像素最多可以表示256种颜色  0000 0000 - 1111 1111  那么只需要使用长度8的二进制位表示 那么每个像素占1byte 

  24位 rgb 

       r 1byte   0-255

       g 1byte  0-255

       b 1byte  0-255     那么一个像素占3byte 

 jpg 格式

 png 格式 Android采用的是png格式

【2】【实例】缩放加载大图片实例

【最终的效果】点击加载按钮,将已经准备的大图片显示出来;

此图片的分辨率是2400*3200;手机屏幕的分辨率是:320*480;

保证横竖屏都可以切换显示;

【2.1】加载图片出现问题

【出现的问题】

【问题的原因】图片加载时占用的内存过大,实际的分配的内存空间只有16MB;

 

【结论】手机加载图片的大小与实际的图片的大小没有关系;就比如上面的图片的大小是1.7MB;而实际手机加载申请的字节是:30720012B = 29.296875MB;

【需要计算缩放比】

实际的图片的像素大小是2400*3200,而手机的屏幕的分辨率是320*480,因此需要根据实际的手机的屏幕的大小来进行图片的缩放;

计算缩放比的时候需要动态的计算,不能只定义单独的屏幕的宽或者高,两者要兼顾

比如:

    

 
图片分辨率 2400 3200
屏幕分辨率 320 480
比率 7(7.5取整) 6(6.66取整)

如果只兼顾宽:则高不合适:3200/7=457<480,图片显示的不够完整;只兼顾高是一样的道理;

 如果换一个图片的加载,则比率又会不同,因此需要动态的加载

【解决办法】使用WindowManager进行动态的计算比率;

【2.2】获得手机屏幕的宽和高

 

【获取手机的宽和高】

【方法1】

【方法2】

 

【注意】需要在api13之后的版本才可以使用;

【核心代码】

【2.3】获得图片的宽和高

【核心代码】

【2.4】计算缩放比

【2.5】按照缩放比显示图片

【2.6】遗留问题1

【说明】不同的分辨率手的分配的单个应用的大小是不同的;

在真机上是无法修改的;

【2.7】遗留问题2

  当加载的图片的分辨小于手机的分辨率时是不需要进行缩放的;

【3】【实例】创建原图的拷贝

【3.1】创建原图拷贝的意义

【结论】如果选择推荐缩放到最佳尺寸,则进行柔光灯处理之后的速度会快;

【原因说明】原图是不可以被修改的;

 

【3.2】【实例】将原图加载,并将原图拷贝一个副本出来;

副本是可以修改的, 出现了一道红杠;

【源码】

【MainActivity.java】

 1 package com.itheima.copybitmap;
 2 
 3 import android.os.Bundle;
 4 import android.app.Activity;
 5 import android.graphics.Bitmap;
 6 import android.graphics.BitmapFactory;
 7 import android.graphics.Canvas;
 8 import android.graphics.Color;
 9 import android.graphics.Matrix;
10 import android.graphics.Paint;
11 import android.view.Menu;
12 import android.view.View;
13 import android.widget.ImageView;
14 
15 public class MainActivity extends Activity {
16 
17     @Override
18     protected void onCreate(Bundle savedInstanceState) {
19         super.onCreate(savedInstanceState);
20         setContentView(R.layout.activity_main);
21 
22         // [1]找到我们关心的控件
23         ImageView iv_src = (ImageView) findViewById(R.id.iv_src);
24         ImageView iv_copy = (ImageView) findViewById(R.id.iv_copy);
25 
26         // [2] 把tomcat.png 转换成bitmap 然后显示到iv_src
27         Bitmap srcBitmap = BitmapFactory.decodeResource(getResources(),
28                 R.drawable.tomcat);
29 
30         // [2.1 test] 修改原图 原图不可以被修改
31         // srcBitmap.setPixel(20, 30, Color.RED);
32 
33         iv_src.setImageBitmap(srcBitmap);
34 
35         // [3]拷贝原图
36 
37         // [3.1]创建模板
38         Bitmap copybitmap = Bitmap.createBitmap(srcBitmap.getWidth(),
39                 srcBitmap.getHeight(), srcBitmap.getConfig());
40         // [3.2]想作画 需要一个画布 以copybitmap为模板
41         Canvas canvas = new Canvas(copybitmap);
42         // [3.3]创建一个画笔
43         Paint paint = new Paint();
44         // [3.4]开始作画 srcBitmap参考原图去画
45         canvas.drawBitmap(srcBitmap, new Matrix(), paint);
46 
47         for (int i = 0; i < 10; i++) {
48             // [一次修改一个像素]
49             copybitmap.setPixel(20 + i, 30, Color.RED);
50         }
51 
52         // [4]把copybitmap显示到iv_copy上
53         iv_copy.setImageBitmap(copybitmap);
54 
55     }
56 
57 }

【activity_main.xml】源码

 1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 2     xmlns:tools="http://schemas.android.com/tools"
 3     android:layout_width="match_parent"
 4     android:layout_height="match_parent"
 5     android:orientation="vertical"
 6     android:gravity="center_horizontal"
 7     tools:context=".MainActivity" >
 8 
 9     <ImageView
10         android:id="@+id/iv_src"
11         android:layout_width="wrap_content"
12         android:layout_height="wrap_content" />
13 
14     <ImageView
15         android:id="@+id/iv_copy"
16         android:layout_width="wrap_content"
17         android:layout_height="wrap_content" />
18 
19 </LinearLayout>

【4】图形处理的api :比如图片的反转等等;

【4.1】图片的旋转

【参数】第一个:倾斜多少度;第二个/第三个:旋转的中心点

【修正】老虎的腿没了,因为创建的画布的大小与原图的大小一致,因此需要进行进行修改副本的画布的大小;

【4.2】图片随着时间不停的在旋转

【注意】不能在主线程中进行ui的更新,需要开辟子线程

 1 package com.itheima.copybitmap;
 2 
 3 import android.os.Bundle;
 4 import android.os.SystemClock;
 5 import android.app.Activity;
 6 import android.graphics.Bitmap;
 7 import android.graphics.BitmapFactory;
 8 import android.graphics.Canvas;
 9 import android.graphics.Color;
10 import android.graphics.Matrix;
11 import android.graphics.Paint;
12 import android.view.Menu;
13 import android.view.View;
14 import android.widget.ImageView;
15 
16 public class MainActivity extends Activity {
17 
18     private float degrees;// 图片旋转的角度
19 
20     @Override
21     protected void onCreate(Bundle savedInstanceState) {
22         super.onCreate(savedInstanceState);
23         setContentView(R.layout.activity_main);
24 
25         // [1]找到我们关心的控件
26         ImageView iv_src = (ImageView) findViewById(R.id.iv_src);
27         final ImageView iv_copy = (ImageView) findViewById(R.id.iv_copy);
28 
29         // [2] 把tomcat.png 转换成bitmap 然后显示到iv_src
30         final Bitmap srcBitmap = BitmapFactory.decodeResource(getResources(),
31                 R.drawable.tomcat);
32 
33         // [2.1 test] 修改原图 原图不可以被修改
34         // srcBitmap.setPixel(20, 30, Color.RED);
35 
36         iv_src.setImageBitmap(srcBitmap);
37 
38         new Thread() {
39             public void run() {
40 
41                 for (int i = 0; i < 100; i++) {
42 
43                     degrees += 5;
44 
45                     // [3]拷贝原图
46 
47                     // [3.1]创建模板
48                     final Bitmap copybitmap = Bitmap.createBitmap(
49                             srcBitmap.getWidth(), srcBitmap.getHeight(),
50                             srcBitmap.getConfig());
51                     // [3.2]想作画 需要一个画布 以copybitmap为模板
52                     Canvas canvas = new Canvas(copybitmap);
53                     // [3.3]创建一个画笔
54                     Paint paint = new Paint();
55                     // [3.4]开始作画 srcBitmap参考原图去画
56                     Matrix matrix = new Matrix();
57 
58                     // [3.5]对图片进行旋转
59                     matrix.setRotate(degrees, srcBitmap.getWidth() / 2,
60                             srcBitmap.getHeight() / 2);
61                     canvas.drawBitmap(srcBitmap, matrix, paint);
62 
63                     // ☆ 注意不能在子线程更新ui
64                     runOnUiThread(new Runnable() {
65                         public void run() {
66                             // 这个方法里面的逻辑一定是在主线程执行
67                             // [4]把copybitmap显示到iv_copy上
68                             iv_copy.setImageBitmap(copybitmap);
69                         }
70                     });
71 
72                     SystemClock.sleep(1000);
73 
74                 }
75 
76             };
77         }.start();
78 
79     }
80 
81 }

【4.3】图片的缩放

【注意】如果是放大的话,对应的画板也要相应的放大;

【源码】

    matrix.setScale(0.5f, 0.5f);
【源码】
 1 package com.itheima.copybitmap;
 2 
 3 import android.os.Bundle;
 4 import android.os.SystemClock;
 5 import android.app.Activity;
 6 import android.graphics.Bitmap;
 7 import android.graphics.BitmapFactory;
 8 import android.graphics.Canvas;
 9 import android.graphics.Color;
10 import android.graphics.Matrix;
11 import android.graphics.Paint;
12 import android.view.Menu;
13 import android.view.View;
14 import android.widget.ImageView;
15 
16 public class MainActivity extends Activity {
17 
18     @Override
19     protected void onCreate(Bundle savedInstanceState) {
20         super.onCreate(savedInstanceState);
21         setContentView(R.layout.activity_main);
22 
23         // [1]找到我们关心的控件
24         ImageView iv_src = (ImageView) findViewById(R.id.iv_src);
25         final ImageView iv_copy = (ImageView) findViewById(R.id.iv_copy);
26 
27         // [2] 把tomcat.png 转换成bitmap 然后显示到iv_src
28         final Bitmap srcBitmap = BitmapFactory.decodeResource(getResources(),
29                 R.drawable.tomcat);
30 
31         // [2.1 test] 修改原图 原图不可以被修改
32         // srcBitmap.setPixel(20, 30, Color.RED);
33 
34         iv_src.setImageBitmap(srcBitmap);
35 
36         // [3]拷贝原图
37         // [3.1]创建模板
38         final Bitmap copybitmap = Bitmap.createBitmap(srcBitmap.getWidth(),
39                 srcBitmap.getHeight(), srcBitmap.getConfig());
40         // [3.2]想作画 需要一个画布 以copybitmap为模板
41         Canvas canvas = new Canvas(copybitmap);
42         // [3.3]创建一个画笔
43         Paint paint = new Paint();
44         // [3.4]开始作画 srcBitmap参考原图去画
45         Matrix matrix = new Matrix();
46     
47        // [3.5]对图片缩放处理 
48         matrix.setScale(1.0f, -1.0f);
49 
50         canvas.drawBitmap(srcBitmap, matrix, paint);
51 
52         // [4]把copybitmap显示到iv_copy上
53         iv_copy.setImageBitmap(copybitmap);
54 
55     }
56 
57 }

【4.4】位移 

 

   matrix.setTranslate(20, 0);  

【4.5】倒影的效果

【Android中的坐标系】横轴右为正,纵轴下为正;

 【4.6】镜面效果

【5】【实例】画画板实例 

例如实现此功能:

【5.1】注意细节

【注意1】

 【注意 2】

在图像画完之后需要更新UI

【注意3】出现下面的情况是因为起点的坐标没有更新;

也就是说:画笔的起点一直在开始定义的点,没有动态的跟随鼠标的抬起和落下更新;

【5.2】实现画画的功能

【功能演示】

【源码】

  1 package com.itheima.paint;
  2 
  3 import java.io.File;
  4 import java.io.FileNotFoundException;
  5 import java.io.FileOutputStream;
  6 
  7 import android.net.Uri;
  8 import android.os.Bundle;
  9 import android.os.Environment;
 10 import android.app.Activity;
 11 import android.content.Intent;
 12 import android.graphics.Bitmap;
 13 import android.graphics.Bitmap.CompressFormat;
 14 import android.graphics.BitmapFactory;
 15 import android.graphics.Canvas;
 16 import android.graphics.Color;
 17 import android.graphics.Matrix;
 18 import android.graphics.Paint;
 19 import android.view.Menu;
 20 import android.view.MotionEvent;
 21 import android.view.View;
 22 import android.view.View.OnTouchListener;
 23 import android.widget.ImageView;
 24 
 25 public class MainActivity extends Activity {
 26 
 27     private Bitmap srcBitmap;
 28     private ImageView iv;
 29     private Bitmap copyBitmap;
 30     private Canvas canvas;
 31     private Paint paint;
 32 
 33     @Override
 34     protected void onCreate(Bundle savedInstanceState) {
 35         super.onCreate(savedInstanceState);
 36         setContentView(R.layout.activity_main);
 37         //[1]找到imageview  显示我们画的内容 
 38         iv = (ImageView) findViewById(R.id.iv);
 39         
 40         //[2]把bg转换成bitmap 
 41         srcBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.bg);
 42         //[2.1]创建模板 
 43         copyBitmap = Bitmap.createBitmap(srcBitmap.getWidth(), srcBitmap.getHeight(), srcBitmap.getConfig());
 44         //[2.2]以copybitmap为模板  创建一个画布 
 45         canvas = new Canvas(copyBitmap);
 46         //[2.3]创建一个画笔 
 47         paint = new Paint();
 48         //[2.4]开始作画 
 49         canvas.drawBitmap(srcBitmap, new Matrix(), paint);
 50         
 51 //        canvas.drawLine(20, 20, 30, 50, paint);
 52         
 53         //[3]把copybitmap显示到iv上 
 54         iv.setImageBitmap(copyBitmap);
 55         
 56         //[4]给iv设置一个触摸事件 
 57         iv.setOnTouchListener(new OnTouchListener() {
 58             
 59             int startX = 0;
 60             int startY = 0;
 61             @Override
 62             public boolean onTouch(View v, MotionEvent event) {
 63                 //[5]获取手指触摸的事件类型 
 64                 int action = event.getAction();
 65                 //[6]具体判断一下是什么事件类型 
 66                 switch (action) {
 67                 case MotionEvent.ACTION_DOWN: //按下 
 68                     //[7]获取手指按下坐标 
 69                     startX = (int) event.getX();
 70                     startY= (int) event.getY();
 71                     System.out.println("按下:"+startX+"---"+startY);
 72                     break;
 73                     
 74                 case MotionEvent.ACTION_MOVE://移动 
 75                     //[8]获取停止的坐标  
 76                     int stopX = (int) event.getX();
 77                     int stopY = (int) event.getY();
 78                     
 79                     System.out.println("移动:"+stopX+"---"+stopY);
 80                     
 81                     //[9]画线 
 82                     canvas.drawLine(startX, startY, stopX, stopY, paint);
 83                     
 84                     //[9.1]更新一下起点坐标 
 85                     startX = stopX;
 86                     startY = stopY;
 87                     
 88                     //[10]记得更新ui 
 89                     iv.setImageBitmap(copyBitmap);
 90                     
 91                     
 92                     break;
 93                     
 94                 case MotionEvent.ACTION_UP: //抬起;
 95 
 96                     break;
 97                 }
 98                 return true;
 99             }
100         });
101         
102         
103         
104     }
105 }

【5.3】增加画笔颜色的修改和画笔的变粗功能

 1     //点击按钮让画笔的颜色 变成红色 
 2     public void click1(View v) {
 3         //设置画笔颜色
 4         paint.setColor(Color.RED);
 5     }
 6     
 7     
 8     //让画笔颜色变粗
 9     public void click2(View v) {
10         //设置画笔的宽度
11         paint.setStrokeWidth(15);
12         
13     }

【5.4】保存画出来图片

 1     //保存大作 
 2     public void click3(View v) {
 3         /**
 4          * format 保存图片的格式 
 5          * 
 6          * quality 保存照片的质量
 7          */
 8         try {
 9             File file = new File(Environment.getExternalStorageDirectory().getPath(),"dazuo.png");
10             FileOutputStream fos = new FileOutputStream(file);
11             copyBitmap.compress(CompressFormat.PNG, 100, fos);
12             fos.close(); //关闭流            
13         } catch (Exception e) {
14             e.printStackTrace();
15         }
16 
17     }

【5.5】通知图册加载已经绘画保存的图片 

【源码】

 1 //保存大作 
 2     public void click3(View v) {
 3         /**
 4          * format 保存图片的格式 
 5          * 
 6          * quality 保存照片的质量
 7          */
 8         try {
 9             File file = new File(Environment.getExternalStorageDirectory().getPath(),"dazuo.png");
10             FileOutputStream fos = new FileOutputStream(file);
11             copyBitmap.compress(CompressFormat.PNG, 100, fos);
12             
13             //发送一条sd卡挂载上来的广播 欺骗一下系统图库应用 说sd卡被挂载了 你去加载图片吧
14             
15             Intent intent = new Intent();
16             //设置action
17             intent.setAction(Intent.ACTION_MEDIA_MOUNTED);
18             //设置data 
19             intent.setData(Uri.fromFile(Environment.getExternalStorageDirectory()));
20             
21             //发送无序广播
22             sendBroadcast(intent);
23             
24             fos.close(); //关闭流
25             
26             
27         } catch (Exception e) {
28             e.printStackTrace();
29         }
30         
31         
32     }

 【6】【实例】撕衣服小案例

【原理】让穿衣服的图片覆盖不穿衣服的图片,然后使用透明画笔将上面穿衣服的照片的抹掉;

 

【实际的效果】

 

【源码】

 1 package com.itheima.syf;
 2 
 3 import android.os.Bundle;
 4 import android.app.Activity;
 5 import android.graphics.Bitmap;
 6 import android.graphics.BitmapFactory;
 7 import android.graphics.Canvas;
 8 import android.graphics.Color;
 9 import android.graphics.Matrix;
10 import android.graphics.Paint;
11 import android.view.Menu;
12 import android.view.MotionEvent;
13 import android.view.View;
14 import android.view.View.OnTouchListener;
15 import android.widget.ImageView;
16 
17 public class MainActivity extends Activity {
18 
19     @Override
20     protected void onCreate(Bundle savedInstanceState) {
21         super.onCreate(savedInstanceState);
22         setContentView(R.layout.activity_main);
23 
24         // [1]找到iv 显示我们操作的图片
25         final ImageView iv = (ImageView) findViewById(R.id.iv);
26 
27         // [2]把我们要操作的图片转换成bitmap
28         Bitmap srcBitmap = BitmapFactory.decodeResource(getResources(),
29                 R.drawable.pre19);
30 
31         // [3]创建原图的副本
32 
33         // [3.1]创建模板
34         final Bitmap alterbBitmap = Bitmap.createBitmap(srcBitmap.getWidth(),
35                 srcBitmap.getHeight(), srcBitmap.getConfig());
36         // [3.2]以alterbBitmap bitmap为模板创建一个画布
37         Canvas canvas = new Canvas(alterbBitmap);
38         // [3.3]创建一个画笔
39         Paint paint = new Paint();
40         // [3.4]开始作画
41         canvas.drawBitmap(srcBitmap, new Matrix(), paint);
42 
43         // [4]把alterbitmap显示到iv上
44         iv.setImageBitmap(alterbBitmap);
45 
46         // [5]给iv设置一个触摸事件
47         iv.setOnTouchListener(new OnTouchListener() {
48 
49             @Override
50             public boolean onTouch(View v, MotionEvent event) {
51                 // [6]获取触摸事件的类型
52                 int action = event.getAction();
53                 switch (action) {
54                 case MotionEvent.ACTION_MOVE: // 移动
55                     for (int i = -7; i < 7; i++) { // 改变x
56                         for (int j = -7; j < 7; j++) {
57                             alterbBitmap.setPixel((int) event.getX()+i, (int) event.getY()+j,Color.TRANSPARENT);
58                        }
59                         
60                     }
61                     // 一定要记得更新iv
62                     iv.setImageBitmap(alterbBitmap);
63                     break;
64                 }
65                 return true;
66             }
67         });
68     }
69 }

【改进】将每次擦去的图片改为是圆

1 if (Math.sqrt(i * i + j * j) < 7) {
2     // 一次修改一个像素
3     try {
4     alterbBitmap.setPixel((int) event.getX()+i, (int) event.getY()+j,Color.TRANSPARENT);
5         } catch (Exception e) {
6         //e.printStackTrace();不要报异常,原因是图片在撕得时候可能会触碰发到负的坐标然后挂掉;
7     }
8 }

 

 【7】【实例】百度音乐盒完成

【说明】会涉及到一个新的类;此类可以播放音频和视频

【7.1】在之前的百度音乐盒(需要运行在服务中)中添加播放的功能

【7.1.1】异常的处理

【解决办法】

 

 【源码】

 1 //专门用来播放音乐的 
 2     public void playMusic(){
 3         System.out.println("音乐播放了");
 4         
 5         //[2]设置要播放的资源  path 可以是本地也可是网络路径 
 6                 try {
 7                     player.reset();
 8                     
 9                     player.setDataSource("/mnt/sdcard/xpg.mp3");
10                     
11                     //[3]准备播放 
12                     player.prepare(); 
13                     
14                     //[4]开始播放 
15                     player.start();
16     
17                 } catch (Exception e) {
18                     e.printStackTrace();
19                 }
20         
21         
22     }
23     
24     
25     // 音乐暂停了
26     public void pauseMusic() {
27         System.out.println("音乐暂停了");
28         // 暂停
29         player.pause();
30 
31     }
32 
33     // 音乐继续播放的方法
34     public void rePlayMusic() {
35         System.out.println("音乐继续播放了");
36 
37         player.start();
38 
39     }

【7.3】增加播放的进度条

【说明】原来的progressBar只会显示,没有进度条拖拽的功能;

现在使用的SeekBar可以进行拖拽;

【7.3.1】更新当前歌曲的播放进度

【说明】不使用while(true)循环的方法,学习使用java中的另外一种方法:Timer类;

【Timer的实例】

【执行的任务的取消】

1     @Override
2     protected void onDestroy() {
3         //当Activity销毁的时候取消timer
4         timer.cancel();
5         task.cancel();
6         super.onDestroy();
7     }

 【7.3.2】在更新进度条的时候需要将获取的参数传递seekBar对象(此时的SeekBar对象在MainActivity中),需要使用到handler;

  传递的参数多于1个(此处是2个);

【参数的接收设置】

 1 public class MainActivity extends Activity {
 2 
 3     private Iservice iservice; // 这个就是我们定义的中间人对象
 4     private MyConn conn;
 5     private static SeekBar sbar;  
 6     public  static Handler handler = new Handler(){
 7         //当 接收到消息该方法执行
 8         public void handleMessage(android.os.Message msg) {
 9             //[1]获取msg 携带的数据 
10             Bundle data = msg.getData();
11             //[2]获取当前进度和总进度
12             int duration = data.getInt("duration");
13             int currentPosition = data.getInt("currentPosition");
14             
15             //[3]设置seekbar的最大进度和当前进度 
16             sbar.setMax(duration);  //设置进度条的最大值
17             sbar.setProgress(currentPosition);//设置当前进度
18     
19         };
20     };

 【7.3.3】进度条的功能:需要将Service中的方法给MainActivity调用;使用接口暴露;

【服务中的代码的添加】

  1 package com.itheima.baidumusic;
  2 
  3 import java.util.Timer;
  4 import java.util.TimerTask;
  5 
  6 import android.app.Service;
  7 import android.content.Intent;
  8 import android.media.MediaPlayer;
  9 import android.media.MediaPlayer.OnCompletionListener;
 10 import android.os.Binder;
 11 import android.os.Bundle;
 12 import android.os.IBinder;
 13 import android.os.Message;
 14 
 15 //音乐播放服务
 16 public class MusicService extends Service {
 17 
 18     private MediaPlayer player;
 19 
 20     // [2]把我们定义的中间人对象 返回
 21     @Override
 22     public IBinder onBind(Intent intent) {
 23         return new MyBinder();
 24     }
 25 
 26     // 服务第一次开启的是调用
 27     @Override
 28     public void onCreate() {
 29 
 30         // [1]初始化mediaplayer
 31         player = new MediaPlayer();
 32 
 33         super.onCreate();
 34     }
 35 
 36     // 当服务销毁的时候调用
 37     @Override
 38     public void onDestroy() {
 39         super.onDestroy();
 40     }
 41 
 42     // 设置播放音乐指定位置的方法
 43     public void seekToPosition(int position) {
 44         player.seekTo(position);
 45     }
 46 
 47     // 专门用来播放音乐的
 48     public void playMusic() {
 49         System.out.println("音乐播放了");
 50 
 51         // [2]设置要播放的资源 path 可以是本地也可是网络路径
 52         try {
 53             player.reset();
 54 
 55             player.setDataSource("/mnt/sdcard/xpg.mp3");
 56 
 57             // [3]准备播放
 58             player.prepare();
 59 
 60             // [4]开始播放
 61             player.start();
 62 
 63             // [5]更新进度条
 64             updateSeekBar();
 65 
 66         } catch (Exception e) {
 67             e.printStackTrace();
 68         }
 69 
 70     }
 71 
 72     // 更新进度条的方法
 73     private void updateSeekBar() {
 74         // [1]获取当前歌曲总时长
 75         final int duration = player.getDuration();
 76         // [2]一秒钟获取一次当前进度
 77         final Timer timer = new Timer();
 78         final TimerTask task = new TimerTask() {
 79 
 80             @Override
 81             public void run() {
 82                 // [3]获取当前歌曲的进度
 83                 int currentPosition = player.getCurrentPosition();
 84 
 85                 // [4]创建message对象
 86                 Message msg = Message.obtain();
 87                 // [5]使用msg携带多个数据
 88                 Bundle bundle = new Bundle();
 89                 bundle.putInt("duration", duration);
 90                 bundle.putInt("currentPosition", currentPosition);
 91                 msg.setData(bundle);
 92                 // 发送消息 MainActivity的handlemessage方法会执行
 93                 MainActivity.handler.sendMessage(msg);
 94 
 95             }
 96         };
 97         // 300毫秒后 每隔1秒钟获取一次当前歌曲的进度
 98         timer.schedule(task, 300, 1000);
 99         // [3]当歌曲播放完成的时候 把timer 和task 取消
100         player.setOnCompletionListener(new OnCompletionListener() {
101 
102             // 当歌曲播放完成的回调
103             @Override
104             public void onCompletion(MediaPlayer mp) {
105                 System.out.println("歌曲播放完成了 ");
106 
107                 timer.cancel();
108                 task.cancel();
109 
110             }
111         });
112 
113     }
114 
115     // 音乐暂停了
116     public void pauseMusic() {
117         System.out.println("音乐暂停了");
118         // 暂停
119         player.pause();
120 
121     }
122 
123     // 音乐继续播放的方法
124     public void rePlayMusic() {
125         System.out.println("音乐继续播放了");
126 
127         player.start();
128 
129     }
130 
131     // [1]定义一个中间人对象(IBinder)
132     private class MyBinder extends Binder implements Iservice {
133 
134         // 调用播放音乐的方法
135         @Override
136         public void callPlayMusic() {
137 
138             playMusic();
139         }
140 
141         // 调用暂停音乐的方法
142         @Override
143         public void callPauseMusic() {
144 
145             pauseMusic();
146         }
147 
148         // 调用继续播放的方法
149         @Override
150         public void callrePlayMusic() {
151 
152             rePlayMusic();
153         }
154 
155         // 调用设置播放指定位置的方法
156         @Override
157         public void callSeekToPosition(int position) {
158 
159             seekToPosition(position);
160         }
161 
162     }
163 
164 }

【Mainactivity中的接收】

  1 package com.itheima.baidumusic;
  2 
  3 import android.os.Bundle;
  4 import android.os.Handler;
  5 import android.os.IBinder;
  6 import android.app.Activity;
  7 import android.content.ComponentName;
  8 import android.content.Intent;
  9 import android.content.ServiceConnection;
 10 import android.view.Menu;
 11 import android.view.View;
 12 import android.view.ViewGroup;
 13 import android.widget.BaseAdapter;
 14 import android.widget.SeekBar;
 15 import android.widget.SeekBar.OnSeekBarChangeListener;
 16 
 17 public class MainActivity extends Activity {
 18 
 19     private Iservice iservice; // 这个就是我们定义的中间人对象
 20     private MyConn conn;
 21     private static SeekBar sbar;  
 22     public  static Handler handler = new Handler(){
 23         //当 接收到消息该方法执行
 24         public void handleMessage(android.os.Message msg) {
 25             //[1]获取msg 携带的数据 
 26             Bundle data = msg.getData();
 27             //[2]获取当前进度和总进度
 28             int duration = data.getInt("duration");
 29             int currentPosition = data.getInt("currentPosition");
 30             
 31             //[3]设置seekbar的最大进度和当前进度 
 32             sbar.setMax(duration);  //设置进度条的最大值
 33             sbar.setProgress(currentPosition);//设置当前进度
 34     
 35         };
 36     };
 37     
 38     
 39 
 40     @Override
 41     protected void onCreate(Bundle savedInstanceState) {
 42         super.onCreate(savedInstanceState);
 43         setContentView(R.layout.activity_main);
 44 
 45         sbar = (SeekBar) findViewById(R.id.seekBar1);
 46         
 47         
 48         //[0]先调用startservice 方法开启服务 保证服务在后台长期运行
 49         Intent intent = new Intent(this, MusicService.class);
 50         startService(intent);
 51         
 52         // [1]调用bindservice 目的是为了获取我们定义的中间人对象
 53         conn = new MyConn();
 54         // 连接MusicService 服务 获取我们定义的中间人对象
 55         bindService(intent, conn, BIND_AUTO_CREATE);
 56 
 57         //[2]给seekbar 设置监听 
 58 
 59         sbar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
 60             //当停止拖动执行
 61             @Override
 62             public void onStopTrackingTouch(SeekBar seekBar) {
 63                 
 64                 //设置播放的位置 
 65                 iservice.callSeekToPosition(seekBar.getProgress());
 66             }
 67             //开始拖动
 68             @Override
 69             public void onStartTrackingTouch(SeekBar seekBar) {
 70                 
 71             }
 72             
 73             @Override
 74             public void onProgressChanged(SeekBar seekBar, int progress,
 75                     boolean fromUser) {
 76                 
 77             }
 78         });
 79         
 80     }
 81 
 82     // 点击按钮 进行 音乐播放
 83     public void click1(View v) {
 84 
 85         // 调用播放音乐的方法
 86         iservice.callPlayMusic();
 87     }
 88 
 89     // 暂停音乐
 90     public void click2(View v) {
 91 
 92         // 调用暂停音乐的方法
 93         iservice.callPauseMusic();
 94     }
 95 
 96     // 继续播放
 97     public void click3(View v) {
 98 
 99         // 调用继续播放
100         iservice.callrePlayMusic();
101     }
102 
103     // 当Activity销毁的时候调用
104     @Override
105     protected void onDestroy() {
106         // 在Activity销毁的时候 取消绑定服务
107         unbindService(conn);
108 
109         super.onDestroy();
110     }
111 
112     private class MyConn implements ServiceConnection {
113 
114         // 当连接成功时候调用
115         @Override
116         public void onServiceConnected(ComponentName name, IBinder service) {
117             // 获取我们定义的中间人对象
118             iservice = (Iservice) service;
119 
120         }
121 
122         @Override
123         public void onServiceDisconnected(ComponentName name) {
124 
125         }
126 
127     }
128 
129 }

【7.4】播放网络音乐

【同步播放存在的问题】因为网络的资源需要加载,因此在点击播放音乐之后会卡一下才会播放音乐;

改进的方法就使用异步准备;

【解决办法】

【7.5】音乐播放结束之后要完成回调

【8】MediaPlayer的生命周期

【8.1】MediaPlayer的生命周期的讲解

 

【8.2】在网络播放音乐的时候没有开子线程

【原因】查看源码:在调用下层的代码时候的已经开了子线程;

【9】【实例】视频的播放

【9.1】【说明】需要设置播放视频的内容播放显示的位置

 

【新的控件SurfaceView】将播放的视频在sfView中显示

   surfaceview介绍

   [1]surfaceview 控件是一个重量级控件  

   [2]内部维护了2个线程 

   A 获取数据  负责显示 

   B 负责显示  获取数据  

   [3]他可以直接在子线程更新ui   与进度相关的控件可以直接在子线程更新ui

1 // 找到控件
2         final SurfaceView sfv = (SurfaceView) findViewById(R.id.sfv);
3 
4         final SurfaceHolder surfaceHolder = sfv.getHolder();
5 -------------------------------------
6 // [2.1]设置播放视频的内容 SurfaceHolder 是用来维护视频播放的内容
7                     player.setDisplay(surfaceHolder);

【注意】

【1】播放网络视频需要增加网络的权限

【9.2】【2】没有播放出来

【解决方法1】

开辟子线程,并且睡眠200ms;

【解决方法2】引入surfaceHolder.addCallback;详细的代码见源码

 【9.3】视频播放记录位置,在下次进入的时候可以继续上次的播放

【1】在sfView销毁的时候拿到当前视频播放的位置

【2】再再次开始播放的时候读取上次的位置

【源码】

 1 package com.itheima.playmusic;
 2 
 3 import java.io.IOException;
 4 
 5 import android.media.MediaPlayer;
 6 import android.media.MediaPlayer.OnPreparedListener;
 7 import android.os.Bundle;
 8 import android.os.SystemClock;
 9 import android.app.Activity;
10 import android.view.Menu;
11 import android.view.SurfaceHolder;
12 import android.view.SurfaceHolder.Callback;
13 import android.view.SurfaceView;
14 import android.view.View;
15 import com.itheima.playvide.R;
16 
17 public class MainActivity extends Activity {
18 
19     private MediaPlayer player;
20     private int currentPosition; // 当前视频播放的位置
21 
22     @Override
23     protected void onCreate(Bundle savedInstanceState) {
24         super.onCreate(savedInstanceState);
25         setContentView(R.layout.activity_main);
26 
27         // 找到控件
28         final SurfaceView sfv = (SurfaceView) findViewById(R.id.sfv);
29 
30         final SurfaceHolder surfaceHolder = sfv.getHolder();
31 
32         // 添加一个callback
33         surfaceHolder.addCallback(new Callback() {
34             // 当surfaceview销毁的时候调用
35             @Override
36             public void surfaceDestroyed(SurfaceHolder holder) {
37                 System.out.println("surfaceDestroyed");
38                 if (player != null && player.isPlaying()) {
39                     // 获取当前视频播放的位置
40                     currentPosition = player.getCurrentPosition();
41                     player.stop();
42                 }
43             }
44 
45             // 当surfaceview 初始化了
46             @Override
47             public void surfaceCreated(SurfaceHolder holder) {
48                 // [1]初始化mediaplayer
49                 player = new MediaPlayer();
50 
51                 // [2]设置要播放的资源 path 可以是本地也可是网络路径
52                 try {
53                     player.setDataSource("http://192.168.13.89:8080/cc.MP4");
54 
55                     // [2.1]设置播放视频的内容 SurfaceHolder 是用来维护视频播放的内容
56                     player.setDisplay(surfaceHolder);
57 
58                     // [3]准备播放
59                     // player.prepare();
60                     player.prepareAsync();
61                     // 设置一个准备完成的监听
62                     player.setOnPreparedListener(new OnPreparedListener() {
63 
64                         @Override
65                         public void onPrepared(MediaPlayer mp) {
66                             // [4]开始播放
67                             player.start();
68                             // [5]继续上次的位置继续播放
69                             player.seekTo(currentPosition);
70 
71                         }
72                     });
73 
74                 } catch (Exception e) {
75                     e.printStackTrace();
76                 }
77             }
78 
79             @Override
80             public void surfaceChanged(SurfaceHolder holder, int format,
81                     int width, int height) {
82 
83             }
84         });
85 
86     }
87 
88 }

【10】VedioView控件

 VideoView

  [1]这个控件就是对surfaceview 和 meidiaplayer进行封装

  [2]meidiaplayer 播放视频他只支持 3gp MP4格式 不支持avi rmvb格式的视频;

   如果播放avi rmvb格式的视频可以使用下一节的框架进行操作;

 

【11】vitamio框架:是一个开源的工程 有专门的官方网站

【11.1】介绍认识

 是一个开源的Android项目,也可以下载到jar包,如果使用jar包的方式,就将jar包放入到Android自己的工程的lib目录下;

库的好处是可以看到源码,jar无法看到源码(此处是lib库)

 

【11.2】如何引用该lib包

【11.3】使用该框架的步骤

    
    1 引入vitamio框架  以library、
    
    2 在布局中定义VideoView
       <io.vov.vitamio.widget.VideoView 
        android:id="@+id/vv"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        />

    3 mainactivity代码
       插件vitamio框架检查是否可用
       if (!LibsChecker.checkVitamioLibs(this)) {
            return;
        }
        
        final VideoView vv = (VideoView) findViewById(R.id.vv);
        vv.setVideoPath("http://192.168.1.2:8080/haha.avi");
        vv.setOnPreparedListener(new OnPreparedListener() {
            
            @Override
            public void onPrepared(MediaPlayer mp) {
                vv.start();
                
            }
        });
        //设置video的控制器
        vv.setMediaController(new MediaController(this));


     4 一定要在清单文件初始化InitActivity
        <activity android:name="io.vov.vitamio.activity.InitActivity"></activity>

【11.4】实例的书写

 1 package com.itheima.videoview;
 2 
 3 import io.vov.vitamio.LibsChecker;
 4 import io.vov.vitamio.MediaPlayer;
 5 import io.vov.vitamio.MediaPlayer.OnPreparedListener;
 6 import io.vov.vitamio.widget.MediaController;
 7 import io.vov.vitamio.widget.VideoView;
 8 import android.os.Bundle;
 9 import android.app.Activity;
10 
11 public class MainActivity extends Activity {
12 
13     @Override
14     protected void onCreate(Bundle savedInstanceState) {
15         super.onCreate(savedInstanceState);
16         setContentView(R.layout.activity_main);
17         // [1]插件vitamio框架检查是否可用
18         if (!LibsChecker.checkVitamioLibs(this)) {
19             return;
20         }
21 
22         final VideoView vv = (VideoView) findViewById(R.id.vv);
23         // 设置播放的路径
24         vv.setVideoPath("http://192.168.13.89:8080/aa.avi");
25         vv.setOnPreparedListener(new OnPreparedListener() {
26 
27             @Override
28             public void onPrepared(MediaPlayer mp) {
29                 vv.start();
30 
31             }
32         });
33         // 设置video的控制器 设置一个进度条
34         vv.setMediaController(new MediaController(this));
35 
36     }
37 
38 }

【运行程序报错的解决】

【演示效果】 

  meidiaplayer 

  videoview 

  ffmpeg 是由好几十个C大神写的 

  不重复造轮子   谷歌

【as中导库出现的bug】-非常重要

【如何导库】参考:Android studio使用

【本例没有完成】导入第三方包之后存在问题,报错

 1 11-21 02:22:27.444 9346-9346/? E/dalvikvm: Could not find class 'android.graphics.drawable.RippleDrawable', referenced from method android.support.v7.widget.AppCompatImageHelper.hasOverlappingRendering
 2 11-21 02:22:27.454 9346-9346/? E/AndroidRuntime: FATAL EXCEPTION: main
 3                                                  java.lang.UnsatisfiedLinkError: Couldn't load vinit from loader dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/it.oztaking.com.demo-1.apk"],nativeLibraryDirectories=[/data/app-lib/it.oztaking.com.demo-1, /vendor/lib, /system/lib]]]: findLibrary returned null
 4                                                      at java.lang.Runtime.loadLibrary(Runtime.java:355)
 5                                                      at java.lang.System.loadLibrary(System.java:525)
 6                                                      at io.vov.vitamio.Vitamio.<clinit>(Vitamio.java:258)
 7                                                      at io.vov.vitamio.LibsChecker.checkVitamioLibs(LibsChecker.java:40)
 8                                                      at it.oztaking.com.demo.MainActivity.onCreate(MainActivity.java:19)
 9                                                      at android.app.Activity.performCreate(Activity.java:5133)
10                                                      at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1087)
11                                                      at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2175)
12                                                      at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2261)
13                                                      at android.app.ActivityThread.access$600(ActivityThread.java:141)
14                                                      at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1256)
15                                                      at android.os.Handler.dispatchMessage(Handler.java:99)
16                                                      at android.os.Looper.loop(Looper.java:137)
17                                                      at android.app.ActivityThread.main(ActivityThread.java:5103)
18                                                      at java.lang.reflect.Method.invokeNative(Native Method)
19                                                      at java.lang.reflect.Method.invoke(Method.java:525)
20                                                      at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:737)
21                                                      at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
22                                                      at dalvik.system.NativeStart.main(Native Method)

 

 

 【12】照相录像:直接使用隐式意图调用第三方的APP;

【说明】不自己写的原因是市面上的手机型号太多,使用谷歌的API可能无法调用起来相机的功能;

【源码】

 1 package com.itheima.carmea;
 2 
 3 import java.io.File;
 4 
 5 import android.net.Uri;
 6 import android.os.Bundle;
 7 import android.os.Environment;
 8 import android.provider.MediaStore;
 9 import android.app.Activity;
10 import android.content.Intent;
11 import android.view.Menu;
12 import android.view.View;
13 
14 public class MainActivity extends Activity {
15 
16     @Override
17     protected void onCreate(Bundle savedInstanceState) {
18         super.onCreate(savedInstanceState);
19         setContentView(R.layout.activity_main);
20     }
21 
22     // 点击按钮 进行照相
23     public void click1(View v) {
24 
25         // 照相 创建意图
26         Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
27         // 存放图片的路径
28         File file = new File(Environment.getExternalStorageDirectory()
29                 .getPath(), "haha.png");
30         intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(file)); // set the
31                                                                         // image
32                                                                         // file
33                                                                         // name
34         // 开启一个activity 并获取结果
35         startActivityForResult(intent, 1);
36 
37     }
38 
39     // 点击按钮 进行录像
40     public void click2(View v) {
41 
42         // 照相 创建意图
43         Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
44         // 存放图片的路径
45         File file = new File(Environment.getExternalStorageDirectory()
46                 .getPath(), "haha.3gp");
47         intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(file)); // set the
48                                                                         // image
49                                                                         // file
50                                                                         // name
51         // 开启一个activity 并获取结果
52         startActivityForResult(intent, 2);
53 
54     }
55 
56     // 当开启的这个Activity页面的关闭的时候调用
57     @Override
58     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
59 
60         System.out.println("哈哈 方法调用了");
61         super.onActivityResult(requestCode, resultCode, data);
62     }
63 
64 }

 

posted @ 2017-11-15 22:40  OzTaking  阅读(448)  评论(0)    收藏  举报