一、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中需要引入权限* 我们需要用来存放取景器的容器,这个容器就是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 colorEffects = param.getSupportedColorEffects(); Iterator 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 sizeList = param.getSupportedPreviewSizes(); //如果sizeList只有一个我们也没有必要做什么了,因为就他一个别无选择 if(sizeList.size() > 1){ Iterator itor = sizeList.iterator(); while(itor.hasNext()){ Camera.Size cur = itor.next(); if(cur.width > bestWidth && cur.height>bestHeight && cur.width < 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" />