Android Day10-多媒体

简介:



多媒体指的就是   文字、图片、音频、视频

一、图片篇

    1.图片大小的计算

       图片大小  =   分辨率 * 位深/8            (以字节为单位)

       分辨率:就是图片的长*宽,单位是相素。

       位深:就是每个相素所占的二进制位数。(右击图片的属性可以查看图片的位深)

            

       对于BMP图片,有以下几种存储格式:

      1)单色:每个像素最多能够表示2种颜色, 2 = 2的1次方,只要使用长度为1的二进制位来表

        示,位深为1,那么一个像素占1/8个byte,也就是Log22  /  8。

      2)16色:每个像素最多能够表示16种颜色,16=2的4次方,只要使用长度为4的二进制位来表

        示,位深为4,那么一个像素占4/8个byte ,也就是Log216  /  8。

      3)256色:每个像素最多能够表示256种颜色,256=2的8次方,只要使用长度为8的二进制位来

        表示,位深为8,那么一个像素占8/8个byte  ,也就是Log2256  /  8。     

      4)24位:每个像素最多能够表示1600多万多种颜色,16777216 = 2的24次方,只要使用长度

         为24的二进制位来表示,位深就为24,那么一个像素占 24 / 8个byte,也就是

         Log216777216 / 8。

       RGB24使用24位来表示一个像素   

     下面这个是我截的图:

            wKioL1W3nymA9Y9XAAHX1je88tM897.jpg    
   对于其它常见格式的图片,如PNG、 JPG等,图片采用了压缩算法进行了压缩,所以图片像素的

  大小不能轻易的算出来,此处暂不花费过多的时间去研究。

     

  2.Android下图片大小的计算

     在Android系统下,通常用BitmapFactory来加载图片。

    Android中 Bitmap的默认加载使用ARGB_8888色彩模式,每个像素会占用4byte。

    比如:一个512*512的图片,无论什么格式,加载进入内存都占用512*512*4=1MB,所以,Android

    图片占用内存大小,只与图片的分辨率(像素)以及加载使用的色彩模式有关)               

     补充参考链接: http://www.cnblogs.com/fengzhblog/p/3227471.html

                                                       http://my.oschina.net/u/1389206/blog/324731  


  3.Android下加载大图片

    1)oom异常

      我利用Android提供的ImageView控件来直接加载一张2400*3200相素的图片

1
2
3
4
              ImageView iv = (ImageView) findViewById(R.id.iv);
         
              Bitmap srcBitmap = BitmapFactory.decodeFile(Environment.getExternalStorageDirectory().getPath() +  "/bigpic.bmp" );
              iv.setImageBitmap(srcBitmap);


      然后报了下面这个异常:    

                 wKiom1W3pofiZO5oAAEuYPq2X0E663.jpg     

      上面我用红色矩形标记的那一行,意思就是说30720012字节分配超过了heap size的最大值

    16777216字节。那么这两个数字是怎么来的呢?

       在创建AVD的时候,我给heap size分配的内存为16MB = 16777216字节,所以16777216

     指的是AVD的heap size,这个参数是虚拟机给每个应用程序分配的最大内存空间。

       在上面图片大小的计算中,已经说到了 Android中图片大小计算的方法,我的代码是采用

     Bitmap来加载图片的,所以图片的大小为2400*3200*4 = 30720000(与30720012相比少了12个

    字节???)

       可见,Android在加载图片的时候,图片的分辨率如果过大的话,就会发生内存溢出异常out

    of memory。那么下面就介绍Android中如何避免这个异常出现。

     2)缩放加载大图片

       图片分辨率(尺寸)过大,那么加载的时候就把图片尺寸变小,那么应该怎么变小呢?一般

      情况就是根据手机的屏幕分辨率来加载图片的。步骤如下:

      第1步:获取手机分辨率

      第2步:获取图片分辨率

      第3步:计算绽放比

      第4步:按绽放比去加载图片

      代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
         //[1]获取手机分辨率    通过WindowManager这个类去实现。
         //得到WindowManager对象
             WindowManager windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
         //得到display对象
             Display display = windowManager.getDefaultDisplay();
             int  screen_width  = display.getWidth();
             int  screen_height = display.getHeight();
         
         //[2]获取图片的分辨率
             File file =  new  File(Environment.getExternalStorageDirectory().getPath() +  "/dog.bmp" );
             BitmapFactory.Options opts =  new  Options();
             opts.inJustDecodeBounds =  true ;         //表示不会为图片的像素分配内存空间
             BitmapFactory.decodeFile(file.getPath(), opts);        
             //当opts的上面这个属性设为true时,这个方法会返回null,但是会将
             //图片的size信息存放到opts对象里。关于这个方法的使用,API说得很明白。
             int  pic_width = opts.outWidth;
             int  pic_height = opts.outHeight;
         //[3]计算缩放比
             int  scale =  1 ;     //最终用的缩放比
             //我个人认为严密的计算缩放比应该是向上取舍
             int  scale_x = ( int ) Math.ceil(pic_width *  1.0  / screen_width);
             int  scale_y = ( int ) Math.ceil(pic_height *  1.0  / screen_height);
             scale =  scale_x > scale_y ? scale_x:scale_y ;
             //如果缩放比小于或等于1,就没有必要缩放了。
             scale = scale >  1  ? scale: 1 ;
         //[4]按绽放比去加载图片
             opts.inSampleSize = scale;    
             //指定加载图片时的缩放比,如果为4,图片的大小会变成原来的1/16。
             //关于这个属性,API说得很明白。
             opts.inJustDecodeBounds =  false ;
             Bitmap scaledBitmap = BitmapFactory.decodeFile(file.getPath(), opts);
             iv.setImageBitmap(scaledBitmap);

  4.创建原图的副本

    为什么要创建原图的副本,因为原图不可以被修改的。

    创建副本的代码如下:

    

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
         //前面的缩放步骤,在此就省略不写了。
             src_Bitmap = BitmapFactory.decodeFile(pathName, opts);
         //【第一步】获取原图的参数
             int  src_width = src_Bitmap.getWidth();
             int  src_height = src_Bitmap.getHeight();
             Config src_config = src_Bitmap.getConfig();          //颜色模式
         //【第二步】创建一个副本作为模板,用原图的参数。  
             copy_Bitmap = Bitmap.createBitmap(src_width, src_height, src_config);
         //【第三步】创建一个画布,以模板为参数   
             canvas =  new  Canvas(copy_Bitmap);
         //【第四步】创建画笔
             paint =  new  Paint();
         //【第五步】开始作画     包括对图片的旋转、缩放、平移等操作。
             //canvas.DrawXxx();
         //【第六步】 将副本图片显示到控件上

   5.图形处理的常见API

    主要是通过Matrix这个类的几个方法来实现下面这些效果的


    • 旋转   setRotate    

    • 缩放   setScale

    • 平移   setTranslate

    • 镜像   包括2种:倒影和镜像,通过缩放和平移的组合为实现.

        倒影:将图片沿图片的X轴镜像(水平向右为X轴的正方向)

       镜像:将图片沿图片的Y轴镜像(水平向下为Y轴的正方向)  

     关于镜像的话,理解起来有些困难,下面我用图形来说明它的实现原理:

     wKiom1W49Z3RetkoAAECURGZ6qQ319.jpg

  6.案例1_画画板

    也是要创建一个原图的副本,并给控件添加一个触摸事件,判断事件的类型来进行相应的操作,

   UI界面如下:

    wKioL1W4-amC55G_AADw19Ak0MM691.jpg

   代码如下:

     其中触摸事件的逻辑和保存图片和发送虚假广播更新图库为重点.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
package  com.itheima.drawpic;
 
import  java.io.File;
import  java.io.FileNotFoundException;
import  java.io.FileOutputStream;
 
import  android.app.Activity;
import  android.content.Intent;
import  android.graphics.Bitmap;
import  android.graphics.Canvas;
import  android.graphics.Color;
import  android.graphics.Paint;
import  android.net.Uri;
import  android.os.Bundle;
import  android.os.Environment;
import  android.view.Display;
import  android.view.MotionEvent;
import  android.view.View;
import  android.view.View.OnTouchListener;
import  android.view.WindowManager;
import  android.widget.ImageView;
import  android.widget.Toast;
 
public  class  MainActivity  extends  Activity {
 
     private  Paint paint;
     private  Bitmap createBitmap;
 
     @Override
     protected  void  onCreate(Bundle savedInstanceState) {
         super .onCreate(savedInstanceState);
         setContentView(R.layout.activity_main);
         
         final  ImageView iv_display = (ImageView) findViewById(R.id.iv_display);
         
         
         //[1]获取手机分辨率    通过WindowManager这个类去实现。
         //得到WindowManager对象
             WindowManager windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
         //得到display对象
             Display display = windowManager.getDefaultDisplay();
             int  screen_width  = display.getWidth();
             int  screen_height = display.getHeight();
             
         createBitmap = Bitmap.createBitmap(screen_width,screen_height/ 2  ,Bitmap.Config.ARGB_8888);
         //用填充相素给画布图片添加颜色
         for ( int  x =  0 ; x < createBitmap.getWidth(); x++)
         {
             for  ( int  y =  0  ; y < createBitmap.getHeight();y++)
             {
                 createBitmap.setPixel(x, y, Color.GRAY);
             }
         }
         
         //创建一个画布
         final  Canvas canvas =  new  Canvas(createBitmap);
 
         paint =  new  Paint();
         
         iv_display.setImageBitmap(createBitmap);
         
         paint.setColor(Color.RED);
         paint.setStrokeWidth( 3 );
         //给ImageView添加触摸事件,开始作画。
         //(*****************************重点*********************************)
         iv_display.setOnTouchListener( new  OnTouchListener() {
             
             float  start_x =  0 ;
             float  start_y =  0 ;
             @Override
             public  boolean  onTouch(View v, MotionEvent event) {
                 //得到事件类型
                 int  action = event.getAction();
                 //判断事件的类型
                 switch  (action) {
                 case  MotionEvent.ACTION_DOWN:
                     System.out.println( "按下了..." );
                     //手按下的时候记录起始坐标
                     start_x = event.getX();
                     start_y = event.getY();
                     break ;
                 case  MotionEvent.ACTION_MOVE:
                     System.out.println( "移动中..." );
                     //手拖动的时候开始作画
                     float  stop_x = event.getX();
                     float  stop_y = event.getY();
                     canvas.drawLine(start_x, start_y, stop_x,stop_y, paint);
                     
                     //记得要更新起点的坐标
                     start_x = stop_x;
                     start_y = stop_y;
                     
                     //更新UI
                     iv_display.setImageBitmap(createBitmap);
                     break ;
                 case  MotionEvent.ACTION_UP:
                     System.out.println( "抬起了..." );
                     break ;
 
                 default :
                     break ;
                 }
                 
                 return  true ;
             }
         });
     }
     
     
     //注册各个按钮的点击事件 
     public  void  click(View v)
     {
         switch  (v.getId()) {
         case  R.id.bt_changePaintColor:
             changePaintColor();
             break ;
         case  R.id.bt_changePaintWidth:
             changePaintWidth();
             break ;
         case  R.id.bt_save:
             save();
             break ;
 
         default :
             break ;
         }
     }
     
     
     //改变画笔的颜色 
     public  void  changePaintColor()
     {
         paint.setColor(Color.BLUE);
     }
     
     //改变画笔的粗细 
     public  void  changePaintWidth()
     {
         paint.setStrokeWidth( 5 );
     }
     
     //保存图片(*****************************重点*********************************)
     public  void  save()
     {
         //调用Bitmap的compress方法
         try  {
             File file =  new  File(Environment.getExternalStorageDirectory().getPath() +  "/paintBoard.png" );
             FileOutputStream out =  new  FileOutputStream(file);
             createBitmap.compress(Bitmap.CompressFormat.PNG,  100 , out);
             out.close();
             
             //给系统发送一条广播(SD卡状态的改变、开关机),以便相册及时更新。
             //下面给手机发送一条欺骗的sd卡状态改变的广播
             Intent intent =  new  Intent();
             intent.setAction( "android.intent.action.MEDIA_MOUNTED" );
             //注意还有一条data的scheme信息
             intent.setData(Uri.fromFile(Environment.getExternalStorageDirectory()));
             sendBroadcast(intent);
             Toast.makeText( this "保存成功" 0 ).show();
         catch  (Exception e) {
             e.printStackTrace();
         }
     }
     
}

 

 7.案例2_撕衣服

    原理:利用相对布局控件重叠的特性,将2张图片叠在一起.其中,没有穿衣服的图片放下面,穿衣服

  的图片放在上面,而且是原图的副本,这样才能结合触摸事件,修改副本的像素颜色为透明的.

    效果图:

    wKioL1W4_B6jwkZ4AADNRAIpmPw290.jpg

   代码如下:

    

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68