Android多媒体学习

一、Image的获取可以通过调Android自带的Camera应用来完成。该应用含有一个Intent-Filter。通过使用
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);startActivityForResult(intent)就可以启动Camera应用了。
二、Image存储,Android系统中含有一个多媒体库,其中包括所有Image、Video、Audio的数据。通过MediaStore对象可以访问相关数据。
闲话少说,直接看例子,这是一本英文书上的,觉得写的很好,自己翻译了一下,并加入了很多注释。以备查询。

 package demo.camera;  
import java.io.File;  
import android.app.Activity;  
import android.content.ContentValues;  
import android.content.Intent;  
import android.graphics.Bitmap;  
import android.graphics.BitmapFactory;  
import android.net.Uri;  
import android.os.Bundle;  
import android.os.Environment;  
import android.provider.MediaStore;  
import android.util.Log;  
import android.view.Display;  
import android.view.View;  
import android.widget.Button;  
import android.widget.ImageView;  
/** 
 * 这里多媒体第一个示例,主要介绍Image的获取和存储 
 * Image的获取可以通过Android自带的Camera应用来获得, 
 * 图片的存储需要用到MediaStore对象。Android中的多媒体库。 
 *  
 * @author Administrator 
 * 
 */  
public class MainActivity extends Activity {  
      
    private static final int RESULT_CODE = 1;  
    private Button btnCamera;  
    private ImageView imageView;  
      
    private Uri imageFilePath;  
      
    @Override  
    public void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.main);  
          
          
        imageView = (ImageView)this.findViewById(R.id.imageView);  
        btnCamera = (Button)this.findViewById(R.id.camera);  
        btnCamera.setOnClickListener(new View.OnClickListener() {  
              
            @Override  
            public void onClick(View v) {  
                  
                  
                /** 
                 * 由于Camara返回的是缩略图,我们可以传递给他一个参数EXTRA_OUTPUT, 
                 * 来将用Camera获取到的图片存储在一个指定的URI位置处。 
                 * 下面就指定image存储在SDCard上,并且文件名为123.jpg 
                 * imageFilePath = Environment.getExternalStorageDirectory().getAbsolutePath()+"123.jpg"; 
                 * File file = new File(imageFilePath); //创建一个文件 
                 * Uri imageUri = Uri.fromFile(file); 
                 * 然而Android已经提供了一个多媒体库,那里统一存放了设备上所有的多媒体数据。所以, 
                 * 我们可以将获取到的图片存放在那个多媒体库中。 
                 * Android提供了MediaStore类,该类是一个ContentProvider,管理着设备上自带的和外部的多媒体文件, 
                 * 同时包含着每一个多媒体文件的数据信息。 
                 * 为了将数据存储在多媒体库,使用ContentResolver对象来操纵MediaStore对象 
                 * 在MediaStore.Images.Media中有两个URI常量,一个是    EXTERNAL_CONTENT_URI,另一个是INTERNAL_CONTENT_URI 
                 * 第一个URI对应着外部设备(SDCard),第二个URI对应着系统设备内部存储位置。 
                 * 对于多媒体文件,一般比较大,我们选择外部存储方式 
                 * 通过使用ContentResolver对象的insert方法我们可以向MediaStore中插入一条数据 
                 * 这样在检索那张图片的时候,不再使用文件的路径,而是根据insert数据时返回的URI,获取一个InputStream 
                 * 并传给BitmapFactory 
                 */  
                //在这里启动Camera。 
  
                //Camera中定义了一个Intent-Filter,其中Action是android.media.action.IMAGE_CAPTURE 
  
                //我们使用的时候,最好不要直接使用这个,而是用MediaStore中的常量ACTION_IMAGE_CAPTURE. 
  
                //这个常量就是对应的上面的action   
                Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);  
              
                //这里我们插入一条数据,ContentValues是我们希望这条记录被创建时包含的数据信息 
  
                //这些数据的名称已经作为常量在MediaStore.Images.Media中,有的存储在MediaStore.MediaColumn中了 
  
                //ContentValues values = new ContentValues(); 
  
                ContentValues values = new ContentValues(3);  
                values.put(MediaStore.Images.Media.DISPLAY_NAME, "testing");  
                values.put(MediaStore.Images.Media.DESCRIPTION, "this is description");  
                values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");  
                imageFilePath = MainActivity.this.getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);  
                intent.putExtra(MediaStore.EXTRA_OUTPUT, imageFilePath); //这样就将文件的存储方式和uri指定到了Camera应用中   
                  
                //由于我们需要调用完Camera后,可以返回Camera获取到的图片, 
  
                //所以,我们使用startActivityForResult来启动Camera                     
  
                startActivityForResult(intent, RESULT_CODE);  
                  
            }  
        });  
    }  
    /** 
     * 为了获取Camera返回的图片信息,重写该方法。 
     */  
    @Override  
    public void onActivityResult(int requestCode, int resultCode, Intent data){  
        super.onActivityResult(requestCode, resultCode, data);  
        if(resultCode == RESULT_CODE){  
            //说明是由Camera返回的数据 
  
            //由Camera应用返回的图片数据是一个Camera对象,存储在一个名为data的extra域 
  
            //然后将获取到的图片存储显示在ImageView中 
  
              
            try {  
                Bundle extra = data.getExtras();  
                /** 
                 * 然而为了节约内存的消耗,这里返回的图片是一个121*162的缩略图。 
                 * 那么如何返回我们需要的大图呢?看上面 
                 * 然而存储了图片。有了图片的存储位置,能不能直接将图片显示出来呢》 
                 * 这个问题就设计到对于图片的处理和显示,是非常消耗内存的,对于PC来说可能不算什么,但是对于手机来说 
                 * 很可能使你的应用因为内存耗尽而死亡。不过还好,Android为我们考虑到了这一点 
                 * Android中可以使用BitmapFactory类和他的一个内部类BitmapFactory.Options来实现图片的处理和显示 
                 * BitmapFactory是一个工具类,里面包含了很多种获取Bitmap的方法。BitmapFactory.Options类中有一个inSampleSize,比如设定他的值为8,则加载到内存中的图片的大小将 
                 * 是原图片的1/8大小。这样就远远降低了内存的消耗。 
                 * BitmapFactory.Options op = new BitmapFactory.Options(); 
                 * op.inSampleSize = 8; 
                 * Bitmap pic = BitmapFactory.decodeFile(imageFilePath, op); 
                 * 这是一种快捷的方式来加载一张大图,因为他不用考虑整个显示屏幕的大小和图片的原始大小 
                 * 然而有时候,我需要根据我们的屏幕来做相应的缩放,如何操作呢? 
                 *  
                 */  
                //首先取得屏幕对象 
  
                Display display = this.getWindowManager().getDefaultDisplay();  
                //获取屏幕的宽和高 
  
                int dw = display.getWidth();  
                int dh = display.getHeight();  
                /** 
                 * 为了计算缩放的比例,我们需要获取整个图片的尺寸,而不是图片 
                 * BitmapFactory.Options类中有一个布尔型变量inJustDecodeBounds,将其设置为true 
                 * 这样,我们获取到的就是图片的尺寸,而不用加载图片了。 
                 * 当我们设置这个值的时候,我们接着就可以从BitmapFactory.Options的outWidth和outHeight中获取到值 
                 */  
                BitmapFactory.Options op = new BitmapFactory.Options();  
                //op.inSampleSize = 8; 
  
                op.inJustDecodeBounds = true;  
                //Bitmap pic = BitmapFactory.decodeFile(imageFilePath, op);//调用这个方法以后,op中的outWidth和outHeight就有值了 
  
                //由于使用了MediaStore存储,这里根据URI获取输入流的形式 
  
                Bitmap pic = BitmapFactory.decodeStream(this  
                        .getContentResolver().openInputStream(imageFilePath),  
                        null, op);  
                int wRatio = (int) Math.ceil(op.outWidth / (float) dw); //计算宽度比例 
  
                int hRatio = (int) Math.ceil(op.outHeight / (float) dh); //计算高度比例 
  
                Log.v("Width Ratio:", wRatio + "");  
                Log.v("Height Ratio:", hRatio + "");  
                /** 
                 * 接下来,我们就需要判断是否需要缩放以及到底对宽还是高进行缩放。 
                 * 如果高和宽不是全都超出了屏幕,那么无需缩放。 
                 * 如果高和宽都超出了屏幕大小,则如何选择缩放呢》 
                 * 这需要判断wRatio和hRatio的大小 
                 * 大的一个将被缩放,因为缩放大的时,小的应该自动进行同比率缩放。 
                 * 缩放使用的还是inSampleSize变量 
                 */  
                if (wRatio > 1 && hRatio > 1) {  
                    if (wRatio > hRatio) {  
                        op.inSampleSize = wRatio;  
                    } else {  
                        op.inSampleSize = hRatio;  
                    }  
                }  
                op.inJustDecodeBounds = false; //注意这里,一定要设置为false,因为上面我们将其设置为true来获取图片尺寸了 
  
                pic = BitmapFactory.decodeStream(this.getContentResolver()  
                        .openInputStream(imageFilePath), null, op);  
                imageView.setImageBitmap(pic);  
            } catch (Exception e) {  
                e.printStackTrace();  
            }   
        }  
    }  
}

检索并显示媒体库中的图片

如果你在模拟器已经启动的情况下,push了几张图片到SDCard中,建议将模拟器关了,再重新启动一下,否则,刚刚添加的图片,是没有办法获取到的。这是因为Android是在系统启动的时候来扫描模拟器上SDCard中多媒体文件的。

 package demo.camera;  
import android.app.Activity;  
import android.database.Cursor;  
import android.graphics.Bitmap;  
import android.graphics.BitmapFactory;  
import android.os.Bundle;  
import android.provider.MediaStore.Images.Media;  
import android.util.Log;  
import android.view.View;  
import android.widget.ImageButton;  
import android.widget.TextView;  
/** 
 * 该类完成图片的检索,显示功能 
 * @author Administrator 
 * 
 */  
public class PhotoManager extends Activity {  
      
    public static final float DISPLAY_WIDTH = 200;  
    public static final float DISPLAY_HEIGHT = 200;  
      
    //这里采用ImageButton的原因是有Button的作用 
  
    private ImageButton photoView;  
    private TextView nameView;  
      
    private Cursor cursor;  
      
    private String photoPath; //存放某张图片对应的位置信息   
    private Bitmap currPhoto;  
      
    //这三个变量主要用来保存Media.DATA,Media.TITLE,Media.DISPLAY_NAME的索引号,来获取每列的数据 
  
    private int photoIndex;  
    //private int titleIndex; 
  
    private int nameIndex;  
      
    public void onCreate(Bundle savedInstanceState){  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.photo_view);  
          
        photoView = (ImageButton)this.findViewById(R.id.image_view);  
        photoView.setOnClickListener(clickListener);  
        nameView = (TextView)this.findViewById(R.id.view_name);  
          
        //指定获取的列 
  
        String columns[] = new String[]{  
                Media.DATA,Media._ID,Media.TITLE,Media.DISPLAY_NAME  
        };  
        //cursor = this.managedQuery(Media.EXTERNAL_CONTENT_URI, columns, null, null, null); 
  
        cursor = this.getContentResolver().query(Media.EXTERNAL_CONTENT_URI, columns, null, null, null);  
        photoIndex = cursor.getColumnIndexOrThrow(Media.DATA);  
        //titleIndex = cursor.getColumnIndexOrThrow(Media.TITLE); 
  
        nameIndex = cursor.getColumnIndexOrThrow(Media.DISPLAY_NAME);  
          
        Log.v("HERE First:", "First Debug");  
        //显示第一张图片,但是首先要判断一下,Cursor是否有值   
        if(cursor.moveToFirst()){  
            showImage();  
        }  
    }  
      
    private View.OnClickListener clickListener = new View.OnClickListener() {  
          
        @Override  
        public void onClick(View v) {  
              
            if(cursor.moveToNext()){  
                showImage();  
            }  
        }  
    };  
      
    /** 
     * 显示图像信息 
     */  
    private void showImage(){  
        photoPath = cursor.getString(photoIndex); //这里获取到的就是图片存储的位置信息   
        //这里怎样获取图片呢?看decodeBitmap 
  
        Log.v("Photo Path:", photoPath);  
        currPhoto = decodeBitmap(photoPath);  
        photoView.setImageBitmap(currPhoto);  
        nameView.setText(cursor.getString(nameIndex));        
    }  
      
    /** 
     * 从path中获取图片信息 
     * @param path 
     * @return 
     */  
    private Bitmap decodeBitmap(String path){  
        BitmapFactory.Options op = new BitmapFactory.Options();  
        op.inJustDecodeBounds = true;  
        Bitmap bmp = BitmapFactory.decodeFile(path, op); //获取尺寸信息   
        //获取比例大小 
  
        int wRatio = (int)Math.ceil(op.outWidth/DISPLAY_WIDTH);  
        int hRatio = (int)Math.ceil(op.outHeight/DISPLAY_HEIGHT);  
        //如果超出指定大小,则缩小相应的比例 
  
        if(wRatio > 1 && hRatio > 1){  
            if(wRatio > hRatio){  
                op.inSampleSize = wRatio;  
            }else{  
                op.inSampleSize = hRatio;  
            }  
        }  
        op.inJustDecodeBounds = false;  
        bmp = BitmapFactory.decodeFile(path, op);  
        return bmp;  
    }  
      
}

Android自带的Camera应用虽然可以满足大多数情景,但是其灵活性上还有不足。但是Android允许我们定制自己的Camera。

在Android的hardware包中有一个Camera类。这个类就是获取Camera服务的,可以定制Camera等。

可以通过open()方法获取其实例。在使用这个类是需要在AndroidManifest.xml文件中加入相应的权限和特性 

如:  <uses-permission android:name = "android.permission.CAMERA" />
  <uses-feature android:name = "android.hardware.camera" />
  <uses-feature android:name = "android.hardware.camera.autofocus" /> 等。

package demo.camera;  
import java.io.OutputStream;  
import java.util.Iterator;  
import java.util.List;  
import android.app.Activity;  
import android.content.ContentValues;  
import android.content.res.Configuration;  
import android.hardware.Camera;  
import android.net.Uri;  
import android.os.Bundle;  
import android.provider.MediaStore;  
import android.view.SurfaceHolder;  
import android.view.SurfaceView;  
import android.view.View;  
import android.widget.LinearLayout;  
/** 
 * Android自带的Camera应用程序可以完成很多功能。但是当其不能满足我们需要的时候 
 * 我们可以定制自己的Camera。Android提供了Camera类来辅助我们实现自己的Camera。 
 * 这个例子就来定义一个自己的Camera 
 * 首先,在Manifest中需要引入权限<uses-permission android:name="android:permission.CAMERA"/> 
 * 我们需要用来存放取景器的容器,这个容器就是SurfaceView。 
 * 使用SurfaceView的同时,我们还需要使用到SurfaceHolder,SurfaceHolder相当于一个监听器,可以监听 
 * Surface上的变化,通过其内部类CallBack来实现。 
 * 为了可以获取图片,我们需要使用Camera的takePicture方法同时我们需要实现Camera.PictureCallBack类,实现onPictureTaken方法 
 * @author Administrator 
 * 
 */  
public class MyCamera extends Activity implements SurfaceHolder.Callback,Camera.PictureCallback{  
      
    public static final int MAX_WIDTH = 200;  
    public static final int MAX_HEIGHT = 200;  
      
    private SurfaceView surfaceView;  
      
    private Camera camera; //这个是hardare的Camera对象   
      
    public void onCreate(Bundle savedInstanceState){  
        super.onCreate(savedInstanceState);  
        this.setContentView(R.layout.camera);  
        surfaceView = (SurfaceView)this.findViewById(R.id.myCameraView);  
        surfaceView.setFocusable(true);   
        surfaceView.setFocusableInTouchMode(true);  
        surfaceView.setClickable(true);  
        surfaceView.setOnClickListener(new View.OnClickListener() {  
              
            @Override  
            public void onClick(View v) {  
                  
                camera.takePicture(null, null, null, MyCamera.this);  
                  
            }  
        });  
        //SurfaceView中的getHolder方法可以获取到一个SurfaceHolder实例 
  
        SurfaceHolder holder = surfaceView.getHolder();  
        //为了实现照片预览功能,需要将SurfaceHolder的类型设置为PUSH 
  
        //这样,画图缓存就由Camera类来管理,画图缓存是独立于Surface的 
  
        holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);  
        holder.addCallback(this);  
    }  
    @Override  
    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {  
    }  
    @Override  
    public void surfaceCreated(SurfaceHolder holder) {  
        // 当Surface被创建的时候,该方法被调用,可以在这里实例化Camera对象 
  
        //同时可以对Camera进行定制 
  
        camera = Camera.open(); //获取Camera实例   
      
          
        /** 
         * Camera对象中含有一个内部类Camera.Parameters.该类可以对Camera的特性进行定制 
         * 在Parameters中设置完成后,需要调用Camera.setParameters()方法,相应的设置才会生效 
         * 由于不同的设备,Camera的特性是不同的,所以在设置时,需要首先判断设备对应的特性,再加以设置 
         * 比如在调用setEffects之前最好先调用getSupportedColorEffects。如果设备不支持颜色特性,那么该方法将 
         * 返回一个null 
         */  
        try {  
              
            Camera.Parameters param = camera.getParameters();  
            if(this.getResources().getConfiguration().orientation != Configuration.ORIENTATION_LANDSCAPE){  
                //如果是竖屏 
  
                param.set("orientation", "portrait");  
                //在2.2以上可以使用 
  
                //camera.setDisplayOrientation(90); 
  
            }else{  
                param.set("orientation", "landscape");  
                //在2.2以上可以使用 
  
                //camera.setDisplayOrientation(0);               
  
            }  
            //首先获取系统设备支持的所有颜色特效,有复合我们的,则设置;否则不设置 
  
            List<String> colorEffects = param.getSupportedColorEffects();  
            Iterator<String> colorItor = colorEffects.iterator();  
            while(colorItor.hasNext()){  
                String currColor = colorItor.next();  
                if(currColor.equals(Camera.Parameters.EFFECT_SOLARIZE)){  
                    param.setColorEffect(Camera.Parameters.EFFECT_SOLARIZE);  
                    break;  
                }  
            }  
            //设置完成需要再次调用setParameter方法才能生效 
  
            camera.setParameters(param);  
              
            camera.setPreviewDisplay(holder);  
              
            /** 
             * 在显示了预览后,我们有时候希望限制预览的Size 
             * 我们并不是自己指定一个SIze而是指定一个Size,然后 
             * 获取系统支持的SIZE,然后选择一个比指定SIZE小且最接近所指定SIZE的一个 
             * Camera.Size对象就是该SIZE。 
             *  
             */  
            int bestWidth = 0;  
            int bestHeight = 0;  
              
            List<Camera.Size> sizeList = param.getSupportedPreviewSizes();  
            //如果sizeList只有一个我们也没有必要做什么了,因为就他一个别无选择 
  
            if(sizeList.size() > 1){  
                Iterator<Camera.Size> itor = sizeList.iterator();  
                while(itor.hasNext()){  
                    Camera.Size cur = itor.next();  
                    if(cur.width > bestWidth && cur.height>bestHeight && cur.width <MAX_WIDTH && cur.height < MAX_HEIGHT){  
                        bestWidth = cur.width;  
                        bestHeight = cur.height;  
                    }  
                }  
                if(bestWidth != 0 && bestHeight != 0){  
                    param.setPreviewSize(bestWidth, bestHeight);  
                    //这里改变了SIze后,我们还要告诉SurfaceView,否则,Surface将不会改变大小,进入Camera的图像将质量很差 
  
                    surfaceView.setLayoutParams(new LinearLayout.LayoutParams(bestWidth, bestHeight));  
                }  
            }  
            camera.setParameters(param);  
        } catch (Exception e) {  
            // 如果出现异常,则释放Camera对象 
  
            camera.release();  
        }  
          
        //启动预览功能 
  
        camera.startPreview();  
          
    }  
    @Override  
    public void surfaceDestroyed(SurfaceHolder holder) {  
        // 当Surface被销毁的时候,该方法被调用 
  
        //在这里需要释放Camera资源 
  
        camera.stopPreview();  
        camera.release();  
          
    }  
    @Override  
    public void onPictureTaken(byte[] data, Camera camera) {  
        // data是一个原始的JPEG图像数据, 
  
        //在这里我们可以存储图片,很显然可以采用MediaStore   
        //注意保存图片后,再次调用startPreview()回到预览 
  
        Uri imageUri = this.getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, new ContentValues());  
        try {  
            OutputStream os = this.getContentResolver().openOutputStream(imageUri);  
            os.write(data);  
            os.flush();  
            os.close();  
        } catch (Exception e) {  
            // TODO: handle exception 
  
            e.printStackTrace();  
        }  
          
        camera.startPreview();  
    }  
      
      
      
}

图像的编辑和合成

package demo.camera;  
import java.io.FileNotFoundException;  
import android.app.Activity;  
import android.content.Intent;  
import android.graphics.Bitmap;  
import android.graphics.BitmapFactory;  
import android.graphics.Canvas;  
import android.graphics.ColorMatrix;  
import android.graphics.ColorMatrixColorFilter;  
import android.graphics.Matrix;  
import android.graphics.Paint;  
import android.graphics.PorterDuff;  
import android.graphics.PorterDuffXfermode;  
import android.net.Uri;  
import android.os.Bundle;  
import android.provider.MediaStore;  
import android.util.Log;  
import android.view.Menu;  
import android.view.MenuItem;  
import android.view.View;  
import android.widget.Button;  
import android.widget.ImageView;  
/** 
 * 在Android中我们可以对图像进行编辑处理等操作 
 * 包括放大缩小,旋转,偏移,裁剪,以及更改亮度,饱和度等 
 *  
 * 1、首先,从SDCard中选择图片,采用Android自带的Callery应用获得 
 * Gallery是Android自带的图片和视频管理应用 
 * 使用Intent来启动Gallery应用,需要指定两个参数,一个是Action,另一个是多媒体存放的URI 
 * Action是一个通用的Action叫ACTION_PICK,来告诉Gallery,我们想检索数据。 
 * 第二个是Data,是一个URI,这里当然是MediaStore.Images.Media.EXTERNAL_CONTENT_URI 
 * 当在Gallery中选择了一个图片的时候,返回的Intent中的Data域就是所选图片对应的URI 
 *  
 * @author Administrator 
 * 
 */  
public class PhotoProcess extends Activity{  
    public static final int FIRST_PIC = 0;  
    public static final int SECOND_PIC = 1;  
    public static final int MAX_WIDTH = 240;  
    public static final int MAX_HEIGHT = 180;  
    private Button btnSelect,btnSelect2;  
    private ImageView srcImageView, dstImageView;  
      
    private Bitmap srcBitmap, dstBitmap;  
    private Uri imageUri;  
      
      
    public void onCreate(Bundle savedInstanceState){  
        super.onCreate(savedInstanceState);  
        this.setContentView(R.layout.process);  
          
        this.btnSelect = (Button)this.findViewById(R.id.btn_select);  
        btnSelect.setOnClickListener(clickListener);  
        this.btnSelect2 = (Button)this.findViewById(R.id.btn_select2);  
        btnSelect2.setOnClickListener(clickListener2);  
        srcImageView = (ImageView)this.findViewById(R.id.img_src);  
        dstImageView = (ImageView)this.findViewById(R.id.img_dst);  
    }  
      
    private View.OnClickListener clickListener = new View.OnClickListener() {  
          
        @Override  
        public void onClick(View arg0) {  
            // 启动Gallery应用 
  
            Intent intent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);  
            startActivityForResult(intent, FIRST_PIC);  
        }  
    };  
    private View.OnClickListener clickListener2 = new View.OnClickListener() {  
          
        @Override  
        public void onClick(View arg0) {  
            // 启动Gallery应用 
  
            Intent intent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);  
            startActivityForResult(intent, SECOND_PIC);  
              
        }  
    };    
      
    public boolean onCreateOptionsMenu(Menu menu){  
        //super.onCreateOptionsMenu(menu); 
  
        //MenuInflater menuInflater = new MenuInflater(this); 
  
        //menuInflater.inflate(R.layout.image, menu) 
  
        menu.add(Menu.NONE,1,Menu.NONE,"复制");  
        menu.add(Menu.NONE,2,Menu.NONE,"变换");  
        menu.add(Menu.NONE,3,Menu.NONE,"亮度");  
        menu.add(Menu.NONE,4,Menu.NONE,"合成");  
        return super.onCreateOptionsMenu(menu);  
    }  
      
    public boolean onOptionsItemSelected(MenuItem item){  
        int id = item.getItemId();  
        switch(id){  
        case 1:  
            //复制一个图像 
  
            if(srcBitmap != null){  
                dstBitmap = getDstImage(null);//这里没有变换 
  
                dstImageView.setImageBitmap(dstBitmap);  
            }  
            break;  
        case 2:  
            //对复制后的图像进行变换 
  
            if(srcBitmap != null){  
                dstBitmap = transferImage();  
                dstImageView.setImageBitmap(dstBitmap);  
            }  
            break;  
        case 3:  
            //改变图像的色彩 
  
            if(srcBitmap != null){  
                dstBitmap = ajustImage();  
                dstImageView.setImageBitmap(dstBitmap);  
            }  
            break;  
        case 4:  
            if(srcBitmap != null && dstBitmap != null){  
                dstBitmap = compositeImages();  
                dstImageView.setImageBitmap(dstBitmap);  
            }  
            break;  
        }  
        return true;  
    }  
      
    /** 
     * 为了创建一个图像的副本,我们可以在创建一个新的空的Bitmap,然后在这个Bitmap上绘制一个Bitmap 
     * 这个空的Bitmap应该和已存在的Bitmap具有相同的尺寸和颜色深度 
     *  
     * 然后我们需要一个Canvas对象,一个Canvas简单说,就是一个画布,存放Bitmap,在构造时,就可以传入Bitmap对象 
     * 同时,Canvas中定义了很多便捷的画图方法,方便我们绘制各种图形 
     * 接下来,如果我们需要处理颜色和对比度,我们需要一个Paint对象,通过Paint我们可以设置画笔的各种特性。 
     *  
     * 最后,我们调用Canvas的drawBitmap就可以将原Bitmap绘制在dstBitmap上了 
     *  
     */  
    private Bitmap getDstImage(Matrix matrix){  
          
        Bitmap bmp = null;  
        //下面这个Bitmap中创建的函数就可以创建一个空的Bitmap 
  
        //返回的是一个可以改变的Bitmap对象,这样我们后面就可以对其进行变换和颜色调整等操作了 
  
        bmp = Bitmap.createBitmap(srcBitmap.getWidth(), srcBitmap.getHeight(), srcBitmap.getConfig());  
        //创建Canvas对象, 
  
        Canvas canvas = new Canvas(bmp);   
        //创建Paint对象,这里先不用 
  
        Paint paint = new Paint();  
        //在Canvas上绘制一个已经存在的Bitmap。这样,dstBitmap就和srcBitmap一摸一样了 
  
          
        if(matrix != null){  
            //如果matrix存在,则采用变换 
  
            canvas.drawBitmap(dstBitmap, matrix, paint);  
        }else{  
            canvas.drawBitmap(srcBitmap, 0, 0, paint);  
        }  
          
          
        return bmp;  
  
    }  
      
      
    /** 
     * 重载getDstImage函数,传入定制的Paint对象 
     * @param matrix 
     * @param paint 
     * @return 
     */  
    private Bitmap getDstImage(Matrix matrix, Paint paint){  
          
        Bitmap bmp = null;  
        //下面这个Bitmap中创建的函数就可以创建一个空的Bitmap 
  
        //返回的是一个可以改变的Bitmap对象,这样我们后面就可以对其进行变换和颜色调整等操作了 
  
        bmp = Bitmap.createBitmap(srcBitmap.getWidth(), srcBitmap.getHeight(), srcBitmap.getConfig());  
        //创建Canvas对象, 
  
        Canvas canvas = new Canvas(bmp);   
          
        //在Canvas上绘制一个已经存在的Bitmap。这样,dstBitmap就和srcBitmap一摸一样了 
  
          
        if(matrix != null){  
            //如果matrix存在,则采用变换 
  
            canvas.drawBitmap(dstBitmap, matrix, paint);  
        }else{  
            canvas.drawBitmap(srcBitmap, 0, 0, paint);  
        }  
          
          
        return bmp;  
  
    }     
      
    /** 
     * 为了放大缩小、旋转图像,我们要使用Matrix类。Matrix类是一个三维矩阵。 
     * 在Android屏幕中,图像的每个像素对应都是一个坐标,一个坐标由x/y/z组成 
     * ------------------------ 
     * cosX -sinX translateX 
     * sinX cosX  translateY 
     * 0    0     scale 
     * ------------------------ 
     * 第一行的值,影响着x坐标。比如 1 0 0 =>x = 1*x + 0*y + 0*z 
     * 第二行的值,影响着y坐标。比如0 1 0 => y = 0*x + 1*y + 0*z 
     * 第三行的值,影响着z坐标。比如 0 0 1 => z = 0*x + 0*y + 1*z 
     *  
     * 我们自己计算一个矩阵然后通过Matrax.setValues设置。 
     * 这样,在调用canvas的drawBitmap方法时,传入matrix 
     *  
     * Matrix类并不提倡我们使用这种方式来操作变换,Matrix针对不同的变换都相应的有pre,set,post三种方法 
     * 可以使用。 
     * pre是矩阵前乘 
     * set是直接设置 
     * post是矩阵后乘 
     */  
    private Bitmap transferImage(){  
        Matrix matrix = new Matrix();  
        matrix.setValues(new float[]{  
            .5f,0,0,//这里只会影响到x轴,所以,图片的长度将是原来的一半   
            0,1,0,  
            0,0,1  
        });  
        return this.getDstImage(matrix);  
    }  
      
    /** 
     * 该方法中我们将对图像的颜色,亮度,对比度等进行设置 
     * 需要用到ColorMatrix类。ColorMatrix类是一个四行五列的矩阵 
     * 每一行影响着[R,G,B,A]中的一个 
     * ------------------------- 
     * a1 b1 c1 d1 e1 
     * a2 b2 c2 d2 e2 
     * a3 b3 c3 d3 e3 
     * a4 b4 c4 d4 e4 
     * ------------------------- 
     * Rnew => a1*R+b1*G+c1*B+d1*A+e1 
     * Gnew => a2*R+b2*G+c2*B+d2*A+e2 
     * Bnew => a3*R+b3*G+c3*B+d3*A+e3 
     * Gnew => a4*R+b4*G+c4*B+d4*A+e4 
     * 其中R,G,B的值是128,A的值是0 
     *  
     * 最后将颜色的修改,通过Paint.setColorFilter应用到Paint对象中。 
     * 主要对于ColorMatrix,需要将其包装成ColorMatrixColorFilter对象,再传给Paint对象 
     *  
     * 同样的,ColorMatrix提供给我们相应的方法,setSaturation()就可以设置一个饱和度 
     */  
    private Bitmap ajustImage(){  
        ColorMatrix cMatrix = new ColorMatrix();  
//      int brightIndex = -25; 
  
//      int doubleColor = 2; 
  
//      cMatrix.set(new float[]{ 
  
//              doubleColor,0,0,0,brightIndex, //这里将1改为2则我们让Red的值为原来的两倍 
  
//              0,doubleColor,0,0,brightIndex,//改变最后一列的值,我们可以不改变RGB同道颜色的基础上,改变亮度 
  
//              0,0,doubleColor,0,brightIndex, 
  
//              0,0,0,doubleColor,0 
  
//      }); 
  
        //cMatrix.setSaturation(2.0f);//设置饱和度 
  
        cMatrix.setScale(2.0f, 2.0f, 2.0f, 2.0f);//设置颜色同道色彩缩放 
  
        Paint paint = new Paint();  
        paint.setColorFilter(new ColorMatrixColorFilter(cMatrix));  
        return this.getDstImage(null, paint);  
    }  
      
    /** 
     * 图像的合成,可以通过在同一个Canvas中绘制两张图片。 
     * 只是在绘制第二章图片的时候,需要给Paint指定一个变幻模式TransferMode。 
     * 在Android中有一个XFermode所有的变幻模式都是这个类的子类 
     * 我们需要用到它的一个子类PorterDuffXfermode,关于这个类,其中用到PorterDuff类 
     * 这个类很简单,就包含一个Enum是Mode,其中定义了一组规则,这组规则就是如何将 
     * 一张图像和另一种图像进行合成 
     * 关于图像合成有四种模式,LIGHTEN,DRAKEN,MULTIPLY,SCREEN 
     */  
    private Bitmap compositeImages(){  
          
        Bitmap bmp = null;  
        //下面这个Bitmap中创建的函数就可以创建一个空的Bitmap 
  
        bmp = Bitmap.createBitmap(srcBitmap.getWidth(), srcBitmap.getHeight(), srcBitmap.getConfig());  
        Paint paint = new Paint();  
        Canvas canvas = new Canvas(bmp);  
        //首先绘制第一张图片,很简单,就是和方法中getDstImage一样 
  
        canvas.drawBitmap(srcBitmap, 0, 0, paint);        
          
        //在绘制第二张图片的时候,我们需要指定一个Xfermode   
        //这里采用Multiply模式,这个模式是将两张图片的对应的点的像素相乘 
  
        //,再除以255,然后以新的像素来重新绘制显示合成后的图像   
        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY));  
        canvas.drawBitmap(dstBitmap, 0, 0, paint);  
          
        return bmp;  
    }  
    public void onActivityResult(int requestCode, int resultCode, Intent data){  
        super.onActivityResult(requestCode, resultCode, data);  
          
        Log.v("Result OK Value:", resultCode+"");  
        Log.v("RequestCode Value", requestCode+"");  
          
        if(resultCode == RESULT_OK){  
            imageUri = data.getData();    
            if(requestCode == FIRST_PIC){  
                //在Gallery中选中一个图片时,返回来的Intent中的Data就是选择图片的Uri 
  
                srcBitmap = getSrcImage(imageUri);  
                srcImageView.setImageBitmap(srcBitmap);               
            }else if(requestCode == SECOND_PIC){  
                //这里处理用户选择的第二张图片 
  
                  
                dstBitmap = getSrcImage(imageUri);  
                dstImageView.setImageBitmap(dstBitmap);  
            }  
        }  
    }  
      
    /** 
     * 需要加载的图片可能是大图,我们需要对其进行合适的缩小处理 
     * @param imageUri 
     */  
    private Bitmap getSrcImage(Uri imageUri){  
        //Display display = this.getWindowManager().getDefaultDisplay(); 
  
        try {  
            BitmapFactory.Options ops = new BitmapFactory.Options();  
            ops.inJustDecodeBounds = true;  
            Bitmap bmp = BitmapFactory.decodeStream(this.getContentResolver().openInputStream(imageUri),null,ops);  
            int wRatio = (int)Math.ceil(ops.outWidth/(float)MAX_WIDTH);  
            int hRatio = (int)Math.ceil(ops.outHeight/(float)MAX_HEIGHT);  
              
            if(wRatio > 1 && hRatio > 1){  
                if(wRatio > hRatio){  
                    ops.inSampleSize = wRatio;  
                }else{  
                    ops.inSampleSize = hRatio;  
                }  
            }  
              
            ops.inJustDecodeBounds = false;  
            bmp = BitmapFactory.decodeStream(this.getContentResolver().openInputStream(imageUri),null,ops);  
              
            return bmp;  
              
        } catch (FileNotFoundException e) {  
            // TODO Auto-generated catch block 
  
            e.printStackTrace();  
            Log.e(this.getClass().getName(), e.getMessage());  
        }  
          
        return null;  
    }  
}

调用Android自带的播放器播放audio

Android有其自带的播放器,我们可以使用隐式Intent来调用它:通过传入一个Action为ACTION_VIEW同时,指定Data为所要播放的Audio的Uri对象,并指定格式信息,则我们就可以调用播放器来播放该Audio了。

Intent intent = new Intent(Intent.ACTION_VIEW);

intent.setDataAndType(Uri, MimeType);

startActivity(intent);

本文我们需要访问MediaStore,来获取所有Audio信息,我们首先将获取所有的Album,然后当用户点击某个Album时,显示该Album下所有的Audio,然后当用户点击某个Audio时,调用系统自带的播放器播放该Audio。

package demo.camera;  
import java.io.File;  
import org.apache.http.client.utils.URIUtils;  
import android.app.Activity;  
import android.app.ListActivity;  
import android.content.Intent;  
import android.database.Cursor;  
import android.net.Uri;  
import android.os.Bundle;  
import android.os.Environment;  
import android.provider.MediaStore;  
import android.provider.MediaStore.Audio;  
import android.provider.MediaStore.Audio.Albums;  
import android.view.View;  
import android.widget.Button;  
import android.widget.CursorAdapter;  
import android.widget.ListView;  
import android.widget.SimpleCursorAdapter;  
/** 
 * 本示例演示如何利用Android自带的Music来播放程序 
 * 和Camera一样,可以通过Intent来启动它。 
 * 我们需要指定一个ACTION_VIEW的Action 
 * 同时一个Uri来指定我们要播放文件的路径 
 * 最后指定一个MIME类型,指定所要播放的文件类型 
 * 每种文件类型对应的都有一个MIME,他一般是类似于audio/mp3格式 
 * 前部分是一个较大的类型,后面是更具体的类型 
 *  
 * 同样的,对于Audio类型的多媒体,系统存储在MediaStore.Audio中 
 * 包括Media,Album,Genre等信息体 
 *  
 * 本文将以列表的形式列出所有的Album信息,供用户选择 
 * 当用户选择某个Album时,系统将打开这个ALbum下的所有Audio 
 * @author Administrator 
 * 
 */  
public class AudioDemo extends ListActivity {  
      
    private Button btnMusic;  
      
    private boolean isAlbum = true; //true时,说明当前列表的内容是Album,false时,说明是Media 
  
      
    private Cursor cursor; //游标对象,   
      
    public void onCreate(Bundle savedInstanceState){  
        super.onCreate(savedInstanceState);  
        this.setContentView(R.layout.audio);  
        btnMusic = (Button)this.findViewById(R.id.btn_music);  
        btnMusic.setOnClickListener(new View.OnClickListener() {  
              
            @Override  
            public void onClick(View v) {  
                  
//              Intent intent = new Intent(Intent.ACTION_VIEW); 
  
//              //这里我们先从SDCard文件中获取指定文件的URi 
  
//              File sdcard = Environment.getExternalStorageDirectory(); //这个是获取SDCard路径 
  
//              File audioFile = new File(sdcard.getPath()+"/music/tt.mp3"); 
  
//              //然后需要获取该文件的Uri 
  
//              Uri audioUri = Uri.fromFile(audioFile); 
  
//              //然后指定Uri和MIME 
  
//              intent.setDataAndType(audioUri, "audio/mp3"); 
  
//              startActivity(intent); 
  
                  
                //获取Album列表 
  
                getAlbums();  
                isAlbum = true;  
            }  
        });  
          
    }  
      
    public void onListItemClick(ListView l, View v, int position, long id){  
          
        //判断当前是哪个列表 
  
        if(isAlbum){  
            //如果是Album,当用户点击某一个时,获取该Album下的所有media 
  
            //l.getChildAt(position); 
  
            if(cursor.moveToPosition(position)){  
                getMedias(cursor.getInt(cursor.getColumnIndexOrThrow(Albums._ID)));  
                isAlbum = false;  
            }  
        }else{  
            //如果是Media,则当用户点击某一个时,则播放该Media 
  
            //调用系统自带的MediaPlayer来播放   
            if(cursor.moveToPosition(position)){  
                String mediaUri = cursor.getString(cursor.getColumnIndexOrThrow(Audio.Media.DATA));  
                String type = cursor.getString(cursor.getColumnIndexOrThrow(Audio.Media.MIME_TYPE));  
                Uri data = Uri.fromFile(new File(mediaUri));  
                Intent intent = new Intent(Intent.ACTION_VIEW);  
                intent.setDataAndType(data, type);  
                startActivity(intent);  
            }  
        }  
          
        //super.onListItemClick(l, v, position, id); 
  
    }  
      
      
    //获取所有Albums 
  
    public void getAlbums(){  
        String[] columns = new String[]{  
                Albums._ID,  
                Albums.ALBUM  
        };  
        String[] from = new String[]{  
                Albums.ALBUM  
        };  
        int[] to = new int[]{  
                android.R.id.text1  
        };  
        cursor = this.managedQuery(Albums.EXTERNAL_CONTENT_URI, columns, null, null, Albums.DEFAULT_SORT_ORDER);  
        CursorAdapter adapter = new SimpleCursorAdapter(this, android.R.layout.simple_list_item_1, cursor, from, to);  
        this.setListAdapter(adapter);  
        //this.isAlbum = true; 
  
          
    }  
      
    //获取某个Albums下对应的medias 
  
    public void getMedias(int albumId){  
        String[] columns = new String[]{  
                Audio.Media._ID,  
                Audio.Media.DATA,  
                Audio.Media.DISPLAY_NAME,  
                Audio.Media.MIME_TYPE  
        };  
        String selection = Audio.Media.ALBUM_ID + "=?";  
        String[] selectionArgs = new String[]{  
                albumId+""  
        };  
          
        String[] from = new String[]{  
                Audio.Media.DISPLAY_NAME  
        };  
        int[] to = new int[]{  
                android.R.id.text1  
        };  
          
        cursor = this.managedQuery(Audio.Media.EXTERNAL_CONTENT_URI, columns, selection, selectionArgs, Audio.Media.TITLE);  
        CursorAdapter adapter = new SimpleCursorAdapter(this, android.R.layout.simple_list_item_1,cursor,from,to);  
        this.setListAdapter(adapter);  
    }  
}
利用Service实现背景音乐的播放

Activity类

package demo.camera;  
import android.app.Activity;  
import android.content.ComponentName;  
import android.content.Context;  
import android.content.Intent;  
import android.content.ServiceConnection;  
import android.os.Bundle;  
import android.os.IBinder;  
import android.view.View;  
/** 
 * 演示Activity如何利用Service来完成后台Audio的播放功能 
 * 同时如何将Service和Activity进行绑定 
 * @author Administrator 
 * 
 */  
public class BackgroundAudioDemo extends Activity {  
      
    private AudioService audioService;  
      
    //使用ServiceConnection来监听Service状态的变化 
  
    private ServiceConnection conn = new ServiceConnection() {  
          
        @Override  
        public void onServiceDisconnected(ComponentName name) {  
            // TODO Auto-generated method stub 
  
            audioService = null;  
        }  
          
        @Override  
        public void onServiceConnected(ComponentName name, IBinder binder) {  
            //这里我们实例化audioService,通过binder来实现 
  
            audioService = ((AudioService.AudioBinder)binder).getService();  
              
        }  
    };  
      
    public void onCreate(Bundle savedInstanceState){  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.back_audio);  
    }  
      
      
    public void onClick(View v){  
        int id = v.getId();  
        Intent intent = new Intent();  
        intent.setClass(this, AudioService.class);        
        if(id == R.id.btn_start){  
            //启动Service,然后绑定该Service,这样我们可以在同时销毁该Activity,看看歌曲是否还在播放 
  
            startService(intent);  
            bindService(intent, conn, Context.BIND_AUTO_CREATE);  
            finish();  
        }else if(id == R.id.btn_end){  
            //结束Service 
  
            unbindService(conn);  
            stopService(intent);  
            finish();  
        }else if(id == R.id.btn_fun){  
            audioService.haveFun();  
        }  
    }  
}

Service类

 package demo.camera;  
import android.app.Service;  
import android.content.Intent;  
import android.media.MediaPlayer;  
import android.os.Binder;  
import android.os.IBinder;  
import android.widget.MediaController.MediaPlayerControl;  
/** 
 * 为了可以使得在后台播放音乐,我们需要Service 
 * Service就是用来在后台完成一些不需要和用户交互的动作 
 * @author Administrator 
 * 
 */  
public class AudioService extends Service implements MediaPlayer.OnCompletionListener{  
      
    MediaPlayer player;  
      
    private final IBinder binder = new AudioBinder();  
    @Override  
    public IBinder onBind(Intent arg0) {  
        // TODO Auto-generated method stub 
  
        return binder;  
    }  
    /** 
     * 当Audio播放完的时候触发该动作 
     */  
    @Override  
    public void onCompletion(MediaPlayer player) {  
        // TODO Auto-generated method stub 
  
        stopSelf();//结束了,则结束Service 
  
    }  
      
    //在这里我们需要实例化MediaPlayer对象 
  
    public void onCreate(){  
        super.onCreate();  
        //我们从raw文件夹中获取一个应用自带的mp3文件 
  
        player = MediaPlayer.create(this, R.raw.tt);  
        player.setOnCompletionListener(this);  
    }  
      
    /** 
     * 该方法在SDK2.0才开始有的,替代原来的onStart方法 
     */  
    public int onStartCommand(Intent intent, int flags, int startId){  
        if(!player.isPlaying()){  
            player.start();  
        }  
        return START_STICKY;  
    }  
      
    public void onDestroy(){  
        //super.onDestroy(); 
  
        if(player.isPlaying()){  
            player.stop();  
        }  
        player.release();  
    }  
      
    //为了和Activity交互,我们需要定义一个Binder对象 
  
    class AudioBinder extends Binder{  
          
        //返回Service对象 
  
        AudioService getService(){  
            return AudioService.this;  
        }  
    }  
      
    //后退播放进度 
  
    public void haveFun(){  
        if(player.isPlaying() && player.getCurrentPosition()>2500){  
            player.seekTo(player.getCurrentPosition()-2500);  
        }  
    }  
}

在清单文件AndroidManifest.xml中配置Service

<service
            android:name=".AudioService" />

 

你可能感兴趣的:(Android多媒体学习)