Chinaunix首页 | 论坛 | 博客
  • 博客访问: 515237
  • 博文数量: 107
  • 博客积分: 927
  • 博客等级: 大尉
  • 技术积分: 865
  • 用 户 组: 普通用户
  • 注册时间: 2011-04-13 17:50
文章分类

全部博文(107)

文章存档

2014年(2)

2013年(13)

2012年(16)

2011年(76)

分类: Java

2011-05-25 15:34:47

最近在学习Android多媒体方面的知识,经过几天的学习,实现了一个例子,给大家分享一下。同时,声明
这些例子是一本英文书上的,这本书对于多媒体的讲解浅显易懂,只要你拥有一定的英语阅读水平就可以了。

闲话不多说,直接看例子。由四个部分组成。

1、Image的获取和存储,涉及到Android自带Camera的调用,以及多媒体库MediaStore的使用
2、Image的检索和显示,涉及到MediaStore检索图像相关知识
3、定制自己的Camera,涉及到Android中使用hardware中的Camera对象来实现自己的Camera
4、实现像PhotoShop中的一些功能,对图像进行编辑,合成等。涉及到Matrix,Canvas,Paint等对象的使用。

下面看例子,注释很详细。按照书中自己翻译理解的,有什么不妥希望大家及时指出,以免对后面的人造成影响。
在这里先谢谢大家了。

1、Android中Image的获取和存储
  1. package demo.camera;

  2. import java.io.File;

  3. import android.app.Activity;
  4. import android.content.ContentValues;
  5. import android.content.Intent;
  6. import android.graphics.Bitmap;
  7. import android.graphics.BitmapFactory;
  8. import android.net.Uri;
  9. import android.os.Bundle;
  10. import android.os.Environment;
  11. import android.provider.MediaStore;
  12. import android.util.Log;
  13. import android.view.Display;
  14. import android.view.View;
  15. import android.widget.Button;
  16. import android.widget.ImageView;
  17. /**
  18. * 这里多媒体第一个示例,主要介绍Image的获取和存储
  19. * Image的获取可以通过Android自带的Camera应用来获得,
  20. * 图片的存储需要用到MediaStore对象。Android中的多媒体库。

  21. * @author Administrator
  22. *
  23. */
  24. public class MainActivity extends Activity {
  25.         
  26.         private static final int RESULT_CODE = 1;
  27.         private Button btnCamera;
  28.         private ImageView imageView;
  29.         
  30.         private Uri imageFilePath;
  31.     
  32.     @Override
  33.     public void onCreate(Bundle savedInstanceState) {
  34.         super.onCreate(savedInstanceState);
  35.         setContentView(R.layout.main);
  36.         
  37.         
  38.         imageView = (ImageView)this.findViewById(R.id.imageView);
  39.         btnCamera = (Button)this.findViewById(R.id.camera);
  40.         btnCamera.setOnClickListener(new View.OnClickListener() {
  41.                         
  42.                         public void onClick(View v) {
  43.                                 
  44.                                 
  45.                                 /**
  46.                                  * 由于Camara返回的是缩略图,我们可以传递给他一个参数EXTRA_OUTPUT,
  47.                                  * 来将用Camera获取到的图片存储在一个指定的URI位置处。
  48.                                  * 下面就指定image存储在SDCard上,并且文件名为123.jpg
  49.                                  * imageFilePath = Environment.getExternalStorageDirectory().getAbsolutePath()+"123.jpg";
  50.                                  * File file = new File(imageFilePath); //创建一个文件
  51.                                  * Uri imageUri = Uri.fromFile(file);
  52.                                  * 然而Android已经提供了一个多媒体库,那里统一存放了设备上所有的多媒体数据。所以,
  53.                                  * 我们可以将获取到的图片存放在那个多媒体库中。
  54.                                  * Android提供了MediaStore类,该类是一个ContentProvider,管理着设备上自带的和外部的多媒体文件,
  55.                                  * 同时包含着每一个多媒体文件的数据信息。
  56.                                  * 为了将数据存储在多媒体库,使用ContentResolver对象来操纵MediaStore对象
  57.                                  * 在MediaStore.Images.Media中有两个URI常量,一个是         EXTERNAL_CONTENT_URI,另一个是INTERNAL_CONTENT_URI
  58.                                  * 第一个URI对应着外部设备(SDCard),第二个URI对应着系统设备内部存储位置。
  59.                                  * 对于多媒体文件,一般比较大,我们选择外部存储方式
  60.                                  * 通过使用ContentResolver对象的insert方法我们可以向MediaStore中插入一条数据
  61.                                  * 这样在检索那张图片的时候,不再使用文件的路径,而是根据insert数据时返回的URI,获取一个InputStream
  62.                                  * 并传给BitmapFactory
  63.                                  */
  64.                                 //在这里启动Camera。
  65.                                 //Camera中定义了一个Intent-Filter,其中Action是android.media.action.IMAGE_CAPTURE
  66.                                 //我们使用的时候,最好不要直接使用这个,而是用MediaStore中的常量ACTION_IMAGE_CAPTURE.
  67.                                 //这个常量就是对应的上面的action
  68.                                 Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
  69.                         
  70.                                 //这里我们插入一条数据,ContentValues是我们希望这条记录被创建时包含的数据信息
  71.                                 //这些数据的名称已经作为常量在MediaStore.Images.Media中,有的存储在MediaStore.MediaColumn中了
  72.                                 //ContentValues values = new ContentValues();
  73.                                 ContentValues values = new ContentValues(3);
  74.                                 values.put(MediaStore.Images.Media.DISPLAY_NAME, "testing");
  75.                                 values.put(MediaStore.Images.Media.DESCRIPTION, "this is description");
  76.                                 values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
  77.                                 imageFilePath = MainActivity.this.getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
  78.                                 intent.putExtra(MediaStore.EXTRA_OUTPUT, imageFilePath); //这样就将文件的存储方式和uri指定到了Camera应用中
  79.                                 
  80.                                 //由于我们需要调用完Camera后,可以返回Camera获取到的图片,
  81.                                 //所以,我们使用startActivityForResult来启动Camera                                        
  82.                                 startActivityForResult(intent, RESULT_CODE);
  83.                         }
  84.                 });
  85.     }
  86.     /**
  87.      * 为了获取Camera返回的图片信息,重写该方法。
  88.      */
  89.     @Override
  90.     public void onActivityResult(int requestCode, int resultCode, Intent data){
  91.             super.onActivityResult(requestCode, resultCode, data);
  92.             if(resultCode == RESULT_CODE){
  93.                     //说明是由Camera返回的数据
  94.                     //由Camera应用返回的图片数据是一个Camera对象,存储在一个名为data的extra域
  95.                     //然后将获取到的图片存储显示在ImageView中
  96.                     
  97.                     try {
  98.                                 Bundle extra = data.getExtras();
  99.                                 /**
  100.                                  * 然而为了节约内存的消耗,这里返回的图片是一个121*162的缩略图。
  101.                                  * 那么如何返回我们需要的大图呢?看上面
  102.                                  * 然而存储了图片。有了图片的存储位置,能不能直接将图片显示出来呢》
  103.                                  * 这个问题就设计到对于图片的处理和显示,是非常消耗内存的,对于PC来说可能不算什么,但是对于手机来说
  104.                                  * 很可能使你的应用因为内存耗尽而死亡。不过还好,Android为我们考虑到了这一点
  105.                                  * Android中可以使用BitmapFactory类和他的一个内部类BitmapFactory.Options来实现图片的处理和显示
  106.                                  * BitmapFactory是一个工具类,里面包含了很多种获取Bitmap的方法。BitmapFactory.Options类中有一个inSampleSize,比如设定他的值为8,则加载到内存中的图片的大小将
  107.                                  * 是原图片的1/8大小。这样就远远降低了内存的消耗。
  108.                                  * BitmapFactory.Options op = new BitmapFactory.Options();
  109.                                  * op.inSampleSize = 8;
  110.                                  * Bitmap pic = BitmapFactory.decodeFile(imageFilePath, op);
  111.                                  * 这是一种快捷的方式来加载一张大图,因为他不用考虑整个显示屏幕的大小和图片的原始大小
  112.                                  * 然而有时候,我需要根据我们的屏幕来做相应的缩放,如何操作呢?
  113.                                  * 
  114.                                  */
  115.                                 //首先取得屏幕对象
  116.                                 Display display = this.getWindowManager().getDefaultDisplay();
  117.                                 //获取屏幕的宽和高
  118.                                 int dw = display.getWidth();
  119.                                 int dh = display.getHeight();
  120.                                 /**
  121.                                  * 为了计算缩放的比例,我们需要获取整个图片的尺寸,而不是图片
  122.                                  * BitmapFactory.Options类中有一个布尔型变量inJustDecodeBounds,将其设置为true
  123.                                  * 这样,我们获取到的就是图片的尺寸,而不用加载图片了。
  124.                                  * 当我们设置这个值的时候,我们接着就可以从BitmapFactory.Options的outWidth和outHeight中获取到值
  125.                                  */
  126.                                 BitmapFactory.Options op = new BitmapFactory.Options();
  127.                                 //op.inSampleSize = 8;
  128.                                 op.inJustDecodeBounds = true;
  129.                                 //Bitmap pic = BitmapFactory.decodeFile(imageFilePath, op);//调用这个方法以后,op中的outWidth和outHeight就有值了
  130.                                 //由于使用了MediaStore存储,这里根据URI获取输入流的形式
  131.                                 Bitmap pic = BitmapFactory.decodeStream(this
  132.                                                 .getContentResolver().openInputStream(imageFilePath),
  133.                                                 null, op);
  134.                                 int wRatio = (int) Math.ceil(op.outWidth / (float) dw); //计算宽度比例
  135.                                 int hRatio = (int) Math.ceil(op.outHeight / (float) dh); //计算高度比例
  136.                                 Log.v("Width Ratio:", wRatio + "");
  137.                                 Log.v("Height Ratio:", hRatio + "");
  138.                                 /**
  139.                                  * 接下来,我们就需要判断是否需要缩放以及到底对宽还是高进行缩放。
  140.                                  * 如果高和宽不是全都超出了屏幕,那么无需缩放。
  141.                                  * 如果高和宽都超出了屏幕大小,则如何选择缩放呢》
  142.                                  * 这需要判断wRatio和hRatio的大小
  143.                                  * 大的一个将被缩放,因为缩放大的时,小的应该自动进行同比率缩放。
  144.                                  * 缩放使用的还是inSampleSize变量
  145.                                  */
  146.                                 if (wRatio > 1 && hRatio > 1) {
  147.                                         if (wRatio > hRatio) {
  148.                                                 op.inSampleSize = wRatio;
  149.                                         } else {
  150.                                                 op.inSampleSize = hRatio;
  151.                                         }
  152.                                 }
  153.                                 op.inJustDecodeBounds = false; //注意这里,一定要设置为false,因为上面我们将其设置为true来获取图片尺寸了
  154.                                 pic = BitmapFactory.decodeStream(this.getContentResolver()
  155.                                                 .openInputStream(imageFilePath), null, op);
  156.                                 imageView.setImageBitmap(pic);
  157.                         } catch (Exception e) {
  158.                                 e.printStackTrace();
  159.                         } 
  160.             }
  161.     }
  162. }
复制代码


2、Image的检索和显示
  1. package demo.camera;

  2. import android.app.Activity;
  3. import android.database.Cursor;
  4. import android.graphics.Bitmap;
  5. import android.graphics.BitmapFactory;
  6. import android.os.Bundle;
  7. import android.provider.MediaStore.Images.Media;
  8. import android.util.Log;
  9. import android.view.View;
  10. import android.widget.ImageButton;
  11. import android.widget.TextView;

  12. /**
  13. * 该类完成图片的检索,显示功能
  14. * @author Administrator
  15. *
  16. */
  17. public class PhotoManager extends Activity {
  18.         
  19.         public static final float DISPLAY_WIDTH = 200;
  20.         public static final float DISPLAY_HEIGHT = 200;
  21.         
  22.         //这里采用ImageButton的原因是有Button的作用
  23.         private ImageButton photoView;
  24.         private TextView nameView;
  25.         
  26.         private Cursor cursor;
  27.         
  28.         private String photoPath; //存放某张图片对应的位置信息
  29.         private Bitmap currPhoto;
  30.         
  31.         //这三个变量主要用来保存Media.DATA,Media.TITLE,Media.DISPLAY_NAME的索引号,来获取每列的数据
  32.         private int photoIndex;
  33.         //private int titleIndex;
  34.         private int nameIndex;
  35.         
  36.         public void onCreate(Bundle savedInstanceState){
  37.                 super.onCreate(savedInstanceState);
  38.                 setContentView(R.layout.photo_view);
  39.                 
  40.                 photoView = (ImageButton)this.findViewById(R.id.image_view);
  41.                 photoView.setOnClickListener(clickListener);
  42.                 nameView = (TextView)this.findViewById(R.id.view_name);
  43.                 
  44.                 //指定获取的列
  45.                 String columns[] = new String[]{
  46.                                 Media.DATA,Media._ID,Media.TITLE,Media.DISPLAY_NAME
  47.                 };
  48.                 //cursor = this.managedQuery(Media.EXTERNAL_CONTENT_URI, columns, null, null, null);
  49.                 cursor = this.getContentResolver().query(Media.EXTERNAL_CONTENT_URI, columns, null, null, null);
  50.                 photoIndex = cursor.getColumnIndexOrThrow(Media.DATA);
  51.                 //titleIndex = cursor.getColumnIndexOrThrow(Media.TITLE);
  52.                 nameIndex = cursor.getColumnIndexOrThrow(Media.DISPLAY_NAME);
  53.                 
  54.                 Log.v("HERE First:", "First Debug");
  55.                 //显示第一张图片,但是首先要判断一下,Cursor是否有值
  56.                 if(cursor.moveToFirst()){
  57.                         showImage();
  58.                 }
  59.         }
  60.         
  61.         private View.OnClickListener clickListener = new View.OnClickListener() {
  62.                 
  63.                 @Override
  64.                 public void onClick(View v) {
  65.                         
  66.                         if(cursor.moveToNext()){
  67.                                 showImage();
  68.                         }
  69.                 }
  70.         };
  71.         
  72.         /**
  73.          * 显示图像信息
  74.          */
  75.         private void showImage(){
  76.                 photoPath = cursor.getString(photoIndex); //这里获取到的就是图片存储的位置信息
  77.                 //这里怎样获取图片呢?看decodeBitmap
  78.                 Log.v("Photo Path:", photoPath);
  79.                 currPhoto = decodeBitmap(photoPath);
  80.                 photoView.setImageBitmap(currPhoto);
  81.                 nameView.setText(cursor.getString(nameIndex));                
  82.         }
  83.         
  84.         /**
  85.          * 从path中获取图片信息
  86.          * @param path
  87.          * @return
  88.          */
  89.         private Bitmap decodeBitmap(String path){
  90.                 BitmapFactory.Options op = new BitmapFactory.Options();
  91.                 op.inJustDecodeBounds = true;
  92.                 Bitmap bmp = BitmapFactory.decodeFile(path, op); //获取尺寸信息
  93.                 //获取比例大小
  94.                 int wRatio = (int)Math.ceil(op.outWidth/DISPLAY_WIDTH);
  95.                 int hRatio = (int)Math.ceil(op.outHeight/DISPLAY_HEIGHT);
  96.                 //如果超出指定大小,则缩小相应的比例
  97.                 if(wRatio > 1 && hRatio > 1){
  98.                         if(wRatio > hRatio){
  99.                                 op.inSampleSize = wRatio;
  100.                         }else{
  101.                                 op.inSampleSize = hRatio;
  102.                         }
  103.                 }
  104.                 op.inJustDecodeBounds = false;
  105.                 bmp = BitmapFactory.decodeFile(path, op);
  106.                 return bmp;
  107.         }
  108.         

  109. }
复制代码


3、定制自己的Camera
  1. package demo.camera;

  2. import java.io.OutputStream;
  3. import java.util.Iterator;
  4. import java.util.List;

  5. import android.app.Activity;
  6. import android.content.ContentValues;
  7. import android.content.res.Configuration;
  8. import android.hardware.Camera;
  9. import android.net.Uri;
  10. import android.os.Bundle;
  11. import android.provider.MediaStore;
  12. import android.view.SurfaceHolder;
  13. import android.view.SurfaceView;
  14. import android.view.View;
  15. import android.widget.LinearLayout;
  16. /**
  17. * Android自带的Camera应用程序可以完成很多功能。但是当其不能满足我们需要的时候
  18. * 我们可以定制自己的Camera。Android提供了Camera类来辅助我们实现自己的Camera。
  19. * 这个例子就来定义一个自己的Camera
  20. * 首先,在Manifest中需要引入权限
  21. * 我们需要用来存放取景器的容器,这个容器就是SurfaceView。
  22. * 使用SurfaceView的同时,我们还需要使用到SurfaceHolder,SurfaceHolder相当于一个监听器,可以监听
  23. * Surface上的变化,通过其内部类CallBack来实现。
  24. * 为了可以获取图片,我们需要使用Camera的takePicture方法同时我们需要实现Camera.PictureCallBack类,实现onPictureTaken方法
  25. * @author Administrator
  26. *
  27. */
  28. public class MyCamera extends Activity implements SurfaceHolder.Callback,Camera.PictureCallback{
  29.         
  30.         public static final int MAX_WIDTH = 200;
  31.         public static final int MAX_HEIGHT = 200;
  32.         
  33.         private SurfaceView surfaceView;
  34.         
  35.         private Camera camera; //这个是hardare的Camera对象
  36.         
  37.         public void onCreate(Bundle savedInstanceState){
  38.                 super.onCreate(savedInstanceState);
  39.                 this.setContentView(R.layout.camera);
  40.                 surfaceView = (SurfaceView)this.findViewById(R.id.myCameraView);
  41.                 surfaceView.setFocusable(true); 
  42.                 surfaceView.setFocusableInTouchMode(true);
  43.                 surfaceView.setClickable(true);
  44.                 surfaceView.setOnClickListener(new View.OnClickListener() {
  45.                         
  46.                         @Override
  47.                         public void onClick(View v) {
  48.                                 
  49.                                 camera.takePicture(null, null, null, MyCamera.this);
  50.                                 
  51.                         }
  52.                 });
  53.                 //SurfaceView中的getHolder方法可以获取到一个SurfaceHolder实例
  54.                 SurfaceHolder holder = surfaceView.getHolder();
  55.                 //为了实现照片预览功能,需要将SurfaceHolder的类型设置为PUSH
  56.                 //这样,画图缓存就由Camera类来管理,画图缓存是独立于Surface的
  57.                 holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
  58.                 holder.addCallback(this);
  59.         }

  60.         @Override
  61.         public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {

  62.         }

  63.         @Override
  64.         public void surfaceCreated(SurfaceHolder holder) {
  65.                 // 当Surface被创建的时候,该方法被调用,可以在这里实例化Camera对象
  66.                 //同时可以对Camera进行定制
  67.                 camera = Camera.open(); //获取Camera实例
  68.         
  69.                 
  70.                 /**
  71.                  * Camera对象中含有一个内部类Camera.Parameters.该类可以对Camera的特性进行定制
  72.                  * 在Parameters中设置完成后,需要调用Camera.setParameters()方法,相应的设置才会生效
  73.                  * 由于不同的设备,Camera的特性是不同的,所以在设置时,需要首先判断设备对应的特性,再加以设置
  74.                  * 比如在调用setEffects之前最好先调用getSupportedColorEffects。如果设备不支持颜色特性,那么该方法将
  75.                  * 返回一个null
  76.                  */

  77.                 try {
  78.                         
  79.                         Camera.Parameters param = camera.getParameters();
  80.                         if(this.getResources().getConfiguration().orientation != Configuration.ORIENTATION_LANDSCAPE){
  81.                                 //如果是竖屏
  82.                                 param.set("orientation", "portrait");
  83.                                 //在2.2以上可以使用
  84.                                 //camera.setDisplayOrientation(90);
  85.                         }else{
  86.                                 param.set("orientation", "landscape");
  87.                                 //在2.2以上可以使用
  88.                                 //camera.setDisplayOrientation(0);                                
  89.                         }
  90.                         //首先获取系统设备支持的所有颜色特效,有复合我们的,则设置;否则不设置
  91.                         List colorEffects = param.getSupportedColorEffects();
  92.                         Iterator colorItor = colorEffects.iterator();
  93.                         while(colorItor.hasNext()){
  94.                                 String currColor = colorItor.next();
  95.                                 if(currColor.equals(Camera.Parameters.EFFECT_SOLARIZE)){
  96.                                         param.setColorEffect(Camera.Parameters.EFFECT_SOLARIZE);
  97.                                         break;
  98.                                 }
  99.                         }
  100.                         //设置完成需要再次调用setParameter方法才能生效
  101.                         camera.setParameters(param);
  102.                         
  103.                         camera.setPreviewDisplay(holder);
  104.                         
  105.                         /**
  106.                          * 在显示了预览后,我们有时候希望限制预览的Size
  107.                          * 我们并不是自己指定一个SIze而是指定一个Size,然后
  108.                          * 获取系统支持的SIZE,然后选择一个比指定SIZE小且最接近所指定SIZE的一个
  109.                          * Camera.Size对象就是该SIZE。
  110.                          * 
  111.                          */
  112.                         int bestWidth = 0;
  113.                         int bestHeight = 0;
  114.                         
  115.                         List sizeList = param.getSupportedPreviewSizes();
  116.                         //如果sizeList只有一个我们也没有必要做什么了,因为就他一个别无选择
  117.                         if(sizeList.size() > 1){
  118.                                 Iterator itor = sizeList.iterator();
  119.                                 while(itor.hasNext()){
  120.                                         Camera.Size cur = itor.next();
  121.                                         if(cur.width > bestWidth && cur.height>bestHeight && cur.width
  122.                                                 bestWidth = cur.width;
  123.                                                 bestHeight = cur.height;
  124.                                         }
  125.                                 }
  126.                                 if(bestWidth != 0 && bestHeight != 0){
  127.                                         param.setPreviewSize(bestWidth, bestHeight);
  128.                                         //这里改变了SIze后,我们还要告诉SurfaceView,否则,Surface将不会改变大小,进入Camera的图像将质量很差
  129.                                         surfaceView.setLayoutParams(new LinearLayout.LayoutParams(bestWidth, bestHeight));
  130.                                 }
  131.                         }
  132.                         camera.setParameters(param);
  133.                 } catch (Exception e) {
  134.                         // 如果出现异常,则释放Camera对象
  135.                         camera.release();
  136.                 }
  137.                 
  138.                 //启动预览功能
  139.                 camera.startPreview();
  140.                 
  141.         }

  142.         @Override
  143.         public void surfaceDestroyed(SurfaceHolder holder) {
  144.                 // 当Surface被销毁的时候,该方法被调用
  145.                 //在这里需要释放Camera资源
  146.                 camera.stopPreview();
  147.                 camera.release();
  148.                 
  149.         }

  150.         @Override
  151.         public void onPictureTaken(byte[] data, Camera camera) {
  152.                 // data是一个原始的JPEG图像数据,
  153.                 //在这里我们可以存储图片,很显然可以采用MediaStore
  154.                 //注意保存图片后,再次调用startPreview()回到预览
  155.                 Uri imageUri = this.getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, new ContentValues());
  156.                 try {
  157.                         OutputStream os = this.getContentResolver().openOutputStream(imageUri);
  158.                         os.write(data);
  159.                         os.flush();
  160.                         os.close();
  161.                 } catch (Exception e) {
  162.                         // TODO: handle exception
  163.                         e.printStackTrace();
  164.                 }
  165.                 
  166.                 camera.startPreview();
  167.         }
  168.         
  169.         
  170.         
  171. }





 


  1. package demo.camera;

  2. import java.io.FileNotFoundException;

  3. import android.app.Activity;
  4. import android.content.Intent;
  5. import android.graphics.Bitmap;
  6. import android.graphics.BitmapFactory;
  7. import android.graphics.Canvas;
  8. import android.graphics.ColorMatrix;
  9. import android.graphics.ColorMatrixColorFilter;
  10. import android.graphics.Matrix;
  11. import android.graphics.Paint;
  12. import android.graphics.PorterDuff;
  13. import android.graphics.PorterDuffXfermode;
  14. import android.net.Uri;
  15. import android.os.Bundle;
  16. import android.provider.MediaStore;
  17. import android.util.Log;
  18. import android.view.Menu;
  19. import android.view.MenuItem;
  20. import android.view.View;
  21. import android.widget.Button;
  22. import android.widget.ImageView;

  23. /**
  24. * 在Android中我们可以对图像进行编辑处理等操作
  25. * 包括放大缩小,旋转,偏移,裁剪,以及更改亮度,饱和度等

  26. * 1、首先,从SDCard中选择图片,采用Android自带的Callery应用获得
  27. * Gallery是Android自带的图片和视频管理应用
  28. * 使用Intent来启动Gallery应用,需要指定两个参数,一个是Action,另一个是多媒体存放的URI
  29. * Action是一个通用的Action叫ACTION_PICK,来告诉Gallery,我们想检索数据。
  30. * 第二个是Data,是一个URI,这里当然是MediaStore.Images.Media.EXTERNAL_CONTENT_URI
  31. * 当在Gallery中选择了一个图片的时候,返回的Intent中的Data域就是所选图片对应的URI

  32. * @author Administrator
  33. *
  34. */
  35. public class PhotoProcess extends Activity{
  36.         public static final int FIRST_PIC = 0;
  37.         public static final int SECOND_PIC = 1;
  38.         public static final int MAX_WIDTH = 240;
  39.         public static final int MAX_HEIGHT = 180;
  40.         private Button btnSelect,btnSelect2;
  41.         private ImageView srcImageView, dstImageView;
  42.         
  43.         private Bitmap srcBitmap, dstBitmap;
  44.         private Uri imageUri;
  45.         
  46.         
  47.         public void onCreate(Bundle savedInstanceState){
  48.                 super.onCreate(savedInstanceState);
  49.                 this.setContentView(R.layout.process);
  50.                 
  51.                 this.btnSelect = (Button)this.findViewById(R.id.btn_select);
  52.                 btnSelect.setOnClickListener(clickListener);
  53.                 this.btnSelect2 = (Button)this.findViewById(R.id.btn_select2);
  54.                 btnSelect2.setOnClickListener(clickListener2);
  55.                 srcImageView = (ImageView)this.findViewById(R.id.img_src);
  56.                 dstImageView = (ImageView)this.findViewById(R.id.img_dst);
  57.         }
  58.         
  59.         private View.OnClickListener clickListener = new View.OnClickListener() {
  60.                 
  61.                 @Override
  62.                 public void onClick(View arg0) {
  63.                         // 启动Gallery应用
  64.                         Intent intent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
  65.                         startActivityForResult(intent, FIRST_PIC);
  66.                 }
  67.         };
  68.         private View.OnClickListener clickListener2 = new View.OnClickListener() {
  69.                 
  70.                 @Override
  71.                 public void onClick(View arg0) {
  72.                         // 启动Gallery应用
  73.                         Intent intent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
  74.                         startActivityForResult(intent, SECOND_PIC);
  75.                         
  76.                 }
  77.         };        
  78.         
  79.         public boolean onCreateOptionsMenu(Menu menu){
  80.                 //super.onCreateOptionsMenu(menu);
  81.                 //MenuInflater menuInflater = new MenuInflater(this);
  82.                 //menuInflater.inflate(R.layout.image, menu)
  83.                 menu.add(Menu.NONE,1,Menu.NONE,"复制");
  84.                 menu.add(Menu.NONE,2,Menu.NONE,"变换");
  85.                 menu.add(Menu.NONE,3,Menu.NONE,"亮度");
  86.                 menu.add(Menu.NONE,4,Menu.NONE,"合成");
  87.                 return super.onCreateOptionsMenu(menu);
  88.         }
  89.         
  90.         public boolean onOptionsItemSelected(MenuItem item){
  91.                 int id = item.getItemId();
  92.                 switch(id){
  93.                 case 1:
  94.                         //复制一个图像
  95.                         if(srcBitmap != null){
  96.                                 dstBitmap = getDstImage(null);//这里没有变换
  97.                                 dstImageView.setImageBitmap(dstBitmap);
  98.                         }
  99.                         break;
  100.                 case 2:
  101.                         //对复制后的图像进行变换
  102.                         if(srcBitmap != null){
  103.                                 dstBitmap = transferImage();
  104.                                 dstImageView.setImageBitmap(dstBitmap);
  105.                         }
  106.                         break;
  107.                 case 3:
  108.                         //改变图像的色彩
  109.                         if(srcBitmap != null){
  110.                                 dstBitmap = ajustImage();
  111.                                 dstImageView.setImageBitmap(dstBitmap);
  112.                         }
  113.                         break;
  114.                 case 4:
  115.                         if(srcBitmap != null && dstBitmap != null){
  116.                                 dstBitmap = compositeImages();
  117.                                 dstImageView.setImageBitmap(dstBitmap);
  118.                         }
  119.                         break;
  120.                 }
  121.                 return true;
  122.         }
  123.         
  124.         /**
  125.          * 为了创建一个图像的副本,我们可以在创建一个新的空的Bitmap,然后在这个Bitmap上绘制一个Bitmap
  126.          * 这个空的Bitmap应该和已存在的Bitmap具有相同的尺寸和颜色深度
  127.          * 
  128.          * 然后我们需要一个Canvas对象,一个Canvas简单说,就是一个画布,存放Bitmap,在构造时,就可以传入Bitmap对象
  129.          * 同时,Canvas中定义了很多便捷的画图方法,方便我们绘制各种图形
  130.          * 接下来,如果我们需要处理颜色和对比度,我们需要一个Paint对象,通过Paint我们可以设置画笔的各种特性。
  131.          * 
  132.          * 最后,我们调用Canvas的drawBitmap就可以将原Bitmap绘制在dstBitmap上了
  133.          * 
  134.          */
  135.         private Bitmap getDstImage(Matrix matrix){
  136.                 
  137.                 Bitmap bmp = null;

  138.                 //下面这个Bitmap中创建的函数就可以创建一个空的Bitmap
  139.                 //返回的是一个可以改变的Bitmap对象,这样我们后面就可以对其进行变换和颜色调整等操作了
  140.                 bmp = Bitmap.createBitmap(srcBitmap.getWidth(), srcBitmap.getHeight(), srcBitmap.getConfig());
  141.                 //创建Canvas对象,
  142.                 Canvas canvas = new Canvas(bmp); 
  143.                 //创建Paint对象,这里先不用
  144.                 Paint paint = new Paint();
  145.                 //在Canvas上绘制一个已经存在的Bitmap。这样,dstBitmap就和srcBitmap一摸一样了
  146.                 
  147.                 if(matrix != null){
  148.                         //如果matrix存在,则采用变换
  149.                         canvas.drawBitmap(dstBitmap, matrix, paint);
  150.                 }else{
  151.                         canvas.drawBitmap(srcBitmap, 0, 0, paint);
  152.                 }
  153.                 
  154.                 
  155.                 return bmp;


  156.         }
  157.         
  158.         
  159.         /**
  160.          * 重载getDstImage函数,传入定制的Paint对象
  161.          * @param matrix
  162.          * @param paint
  163.          * @return
  164.          */
  165.         private Bitmap getDstImage(Matrix matrix, Paint paint){
  166.                 
  167.                 Bitmap bmp = null;

  168.                 //下面这个Bitmap中创建的函数就可以创建一个空的Bitmap
  169.                 //返回的是一个可以改变的Bitmap对象,这样我们后面就可以对其进行变换和颜色调整等操作了
  170.                 bmp = Bitmap.createBitmap(srcBitmap.getWidth(), srcBitmap.getHeight(), srcBitmap.getConfig());
  171.                 //创建Canvas对象,
  172.                 Canvas canvas = new Canvas(bmp); 
  173.                 
  174.                 //在Canvas上绘制一个已经存在的Bitmap。这样,dstBitmap就和srcBitmap一摸一样了
  175.                 
  176.                 if(matrix != null){
  177.                         //如果matrix存在,则采用变换
  178.                         canvas.drawBitmap(dstBitmap, matrix, paint);
  179.                 }else{
  180.                         canvas.drawBitmap(srcBitmap, 0, 0, paint);
  181.                 }
  182.                 
  183.                 
  184.                 return bmp;


  185.         }        
  186.         
  187.         /**
  188.          * 为了放大缩小、旋转图像,我们要使用Matrix类。Matrix类是一个三维矩阵。
  189.          * 在Android屏幕中,图像的每个像素对应都是一个坐标,一个坐标由x/y/z组成
  190.          * ------------------------
  191.          * cosX -sinX translateX
  192.          * sinX cosX  translateY
  193.          * 0        0          scale
  194.          * ------------------------
  195.          * 第一行的值,影响着x坐标。比如 1 0 0 =>x = 1*x + 0*y + 0*z
  196.          * 第二行的值,影响着y坐标。比如0 1 0 => y = 0*x + 1*y + 0*z
  197.          * 第三行的值,影响着z坐标。比如 0 0 1 => z = 0*x + 0*y + 1*z
  198.          * 
  199.          * 我们自己计算一个矩阵然后通过Matrax.setValues设置。
  200.          * 这样,在调用canvas的drawBitmap方法时,传入matrix
  201.          * 
  202.          * Matrix类并不提倡我们使用这种方式来操作变换,Matrix针对不同的变换都相应的有pre,set,post三种方法
  203.          * 可以使用。
  204.          * pre是矩阵前乘
  205.          * set是直接设置
  206.          * post是矩阵后乘
  207.          */
  208.         private Bitmap transferImage(){
  209.                 Matrix matrix = new Matrix();
  210.                 matrix.setValues(new float[]{
  211.                         .5f,0,0,//这里只会影响到x轴,所以,图片的长度将是原来的一半
  212.                         0,1,0,
  213.                         0,0,1
  214.                 });
  215.                 return this.getDstImage(matrix);
  216.         }
  217.         
  218.         /**
  219.          * 该方法中我们将对图像的颜色,亮度,对比度等进行设置
  220.          * 需要用到ColorMatrix类。ColorMatrix类是一个四行五列的矩阵
  221.          * 每一行影响着[R,G,B,A]中的一个
  222.          * -------------------------
  223.          * a1 b1 c1 d1 e1
  224.          * a2 b2 c2 d2 e2
  225.          * a3 b3 c3 d3 e3
  226.          * a4 b4 c4 d4 e4
  227.          * -------------------------
  228.          * Rnew => a1*R+b1*G+c1*B+d1*A+e1
  229.          * Gnew => a2*R+b2*G+c2*B+d2*A+e2
  230.          * Bnew => a3*R+b3*G+c3*B+d3*A+e3
  231.          * Gnew => a4*R+b4*G+c4*B+d4*A+e4
  232.          * 其中R,G,B的值是128,A的值是0
  233.          * 
  234.          * 最后将颜色的修改,通过Paint.setColorFilter应用到Paint对象中。
  235.          * 主要对于ColorMatrix,需要将其包装成ColorMatrixColorFilter对象,再传给Paint对象
  236.          * 
  237.          * 同样的,ColorMatrix提供给我们相应的方法,setSaturation()就可以设置一个饱和度
  238.          */
  239.         private Bitmap ajustImage(){
  240.                 ColorMatrix cMatrix = new ColorMatrix();
  241. //                int brightIndex = -25;
  242. //                int doubleColor = 2;
  243. //                cMatrix.set(new float[]{
  244. //                                doubleColor,0,0,0,brightIndex, //这里将1改为2则我们让Red的值为原来的两倍
  245. //                                0,doubleColor,0,0,brightIndex,//改变最后一列的值,我们可以不改变RGB同道颜色的基础上,改变亮度
  246. //                                0,0,doubleColor,0,brightIndex,
  247. //                                0,0,0,doubleColor,0
  248. //                });
  249.                 //cMatrix.setSaturation(2.0f);//设置饱和度
  250.                 cMatrix.setScale(2.0f, 2.0f, 2.0f, 2.0f);//设置颜色同道色彩缩放
  251.                 Paint paint = new Paint();
  252.                 paint.setColorFilter(new ColorMatrixColorFilter(cMatrix));
  253.                 return this.getDstImage(null, paint);
  254.         }
  255.         
  256.         /**
  257.          * 图像的合成,可以通过在同一个Canvas中绘制两张图片。
  258.          * 只是在绘制第二章图片的时候,需要给Paint指定一个变幻模式TransferMode。
  259.          * 在Android中有一个XFermode所有的变幻模式都是这个类的子类
  260.          * 我们需要用到它的一个子类PorterDuffXfermode,关于这个类,其中用到PorterDuff类
  261.          * 这个类很简单,就包含一个Enum是Mode,其中定义了一组规则,这组规则就是如何将
  262.          * 一张图像和另一种图像进行合成
  263.          * 关于图像合成有四种模式,LIGHTEN,DRAKEN,MULTIPLY,SCREEN
  264.          */
  265.         private Bitmap compositeImages(){
  266.                 
  267.                 Bitmap bmp = null;

  268.                 //下面这个Bitmap中创建的函数就可以创建一个空的Bitmap
  269.                 bmp = Bitmap.createBitmap(srcBitmap.getWidth(), srcBitmap.getHeight(), srcBitmap.getConfig());
  270.                 Paint paint = new Paint();
  271.                 Canvas canvas = new Canvas(bmp);
  272.                 //首先绘制第一张图片,很简单,就是和方法中getDstImage一样
  273.                 canvas.drawBitmap(srcBitmap, 0, 0, paint);                
  274.                 
  275.                 //在绘制第二张图片的时候,我们需要指定一个Xfermode
  276.                 //这里采用Multiply模式,这个模式是将两张图片的对应的点的像素相乘
  277.                 //,再除以255,然后以新的像素来重新绘制显示合成后的图像
  278.                 paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY));
  279.                 canvas.drawBitmap(dstBitmap, 0, 0, paint);
  280.                 
  281.                 return bmp;
  282.         }
  283.         public void onActivityResult(int requestCode, int resultCode, Intent data){
  284.                 super.onActivityResult(requestCode, resultCode, data);
  285.                 
  286.                 Log.v("Result OK Value:", resultCode+"");
  287.                 Log.v("RequestCode Value", requestCode+"");
  288.                 
  289.                 if(resultCode == RESULT_OK){
  290.                         imageUri = data.getData();        
  291.                         if(requestCode == FIRST_PIC){
  292.                                 //在Gallery中选中一个图片时,返回来的Intent中的Data就是选择图片的Uri
  293.                                 srcBitmap = getSrcImage(imageUri);
  294.                                 srcImageView.setImageBitmap(srcBitmap);                                
  295.                         }else if(requestCode == SECOND_PIC){
  296.                                 //这里处理用户选择的第二张图片
  297.                                 
  298.                                 dstBitmap = getSrcImage(imageUri);
  299.                                 dstImageView.setImageBitmap(dstBitmap);
  300.                         }

  301.                 }
  302.         }
  303.         
  304.         /**
  305.          * 需要加载的图片可能是大图,我们需要对其进行合适的缩小处理
  306.          * @param imageUri
  307.          */
  308.         private Bitmap getSrcImage(Uri imageUri){
  309.                 //Display display = this.getWindowManager().getDefaultDisplay();
  310.                 try {
  311.                         BitmapFactory.Options ops = new BitmapFactory.Options();
  312.                         ops.inJustDecodeBounds = true;
  313.                         Bitmap bmp = BitmapFactory.decodeStream(this.getContentResolver().openInputStream(imageUri),null,ops);
  314.                         int wRatio = (int)Math.ceil(ops.outWidth/(float)MAX_WIDTH);
  315.                         int hRatio = (int)Math.ceil(ops.outHeight/(float)MAX_HEIGHT);
  316.                         
  317.                         if(wRatio > 1 && hRatio > 1){
  318.                                 if(wRatio > hRatio){
  319.                                         ops.inSampleSize = wRatio;
  320.                                 }else{
  321.                                         ops.inSampleSize = hRatio;
  322.                                 }
  323.                         }
  324.                         
  325.                         ops.inJustDecodeBounds = false;
  326.                         bmp = BitmapFactory.decodeStream(this.getContentResolver().openInputStream(imageUri),null,ops);
  327.                         
  328.                         return bmp;
  329.                         
  330.                 } catch (FileNotFoundException e) {
  331.                         // TODO Auto-generated catch block
  332.                         e.printStackTrace();
  333.                         Log.e(this.getClass().getName(), e.getMessage());
  334.                 }
  335.                 
  336.                 return null;
  337.         }

  338. }
复制代码
阅读(3041) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~