一、摄像头拍照
前面说媒体播放 时了解了 SurfaceView 最大的特点就是提供了一个高速的更新空间,那么如果说现在要进行图像的捕获,那么肯定要跟随摄像头一起变化,那么这块空间很明显必须使用高速的刷新频率。
使用 SurfaceView 组件可以进行视频文件的播放,而同样可以继续利用 SurfaceView 实现拍照的浏览功能,在支持拍照的手机上,都会为用户提供一个预览的屏幕显示当前摄像头所采集到的图片,而这种功能可以利用 SurfaceView 实现, SurfaceView 之中的操作核心就是在于 android.view. SurfaceHolder 对象的操作,前面说了可以通过 SurfaceView 取得一个 SurfaceHolder 对象,可是如果要想实现拍照的功能,首先用户必须手动实现 android.view. SurfaceHolder.Callback 这个操作接口,在此接口中定义了高速图像浏览时的各个操作。
No. |
方法 |
描述 |
1 |
Public abstract void surfaceChanged(SurfaceHolder holder,int format,int width,int height) |
当预览界面的格式和大小发生改变时会触发此操作 |
2 |
Public abstract void surfaceCreated(SurfaceHolder holder) |
当预览界面被创建时会触发此操作 |
3 |
Public abstract void surfaceDestroyed(SurfaceHolder holder) |
当预览界面关闭时会触发此操作 |
Android.hardware.Camera 进行调用摄像头的操作类,此类主要负责完成拍照图片的参数设置及保存,需要注意的是摄像头只能被一个设备所支持。
Camera 类中定义的内部接口:
No. |
接口名称 |
描述 |
1 |
Android.hardware.Camera.AutoFocusCallback |
自动对焦的回调操作 |
2 |
Android.hardware.Camera.ErrorCallback |
错误出现时的回调操作 |
3 |
Android.hardware.Camera.OnZoomChangedListener |
显示区域改变时的回调操作 |
4 |
Android.hardware.Camera.PictureCallback |
图片生成时的回调操作 |
5 |
Android.hardware.Camera.PreviewCallback |
预览时的回调操作 |
6 |
Android.hardware.Camera.ShutterCallback |
按下快门后的回调操作 |
范例:预览,拍照
因为需要将照片保存在 sdcard 下,所以需要配置一些权限,同时将屏幕的方向设置为横屏
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.iflytek.demo" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="10" /> <application android:icon="@drawable/ic_launcher" android:label="@string/app_name" > <activity android:label="@string/app_name" android:name=".CameraActivity" android:screenOrientation="landscape" > <intent-filter > <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> <uses-feature android:name="android.hardware.camera" /> <uses-feature android:name="android.hardware.camera.autofocus" /> <!-- 摄像头 --> <uses-permission android:name="android.permission.CAMERA" /> <!-- SDCard权限 --> <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" /> <!-- 写入扩展设备 --> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> </manifest>
main.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <Button android:id="@+id/but" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="照相" /> <SurfaceView android:id="@+id/surface" android:layout_width="fill_parent" android:layout_height="fill_parent" /> </LinearLayout>
下面我们通过 SurfaceView 捕获图像,而后使用按钮进行图像的拍照。
现在必须将摄像头捕获到的内容设置到 SurfaceView 之中。
如果要想实现拍照的功能,那么前提一定是已经对焦成功。
CameraActivity.java
package com.iflytek.demo; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.text.MessageFormat; import java.util.List; import android.app.Activity; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.PixelFormat; import android.hardware.Camera; import android.hardware.Camera.AutoFocusCallback; import android.hardware.Camera.Parameters; import android.hardware.Camera.PictureCallback; import android.hardware.Camera.ShutterCallback; import android.os.Bundle; import android.os.Environment; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.Toast; public class CameraActivity extends Activity { private SurfaceView surfaceView = null; private Button btn = null; private SurfaceHolder surfaceHolder = null; private Camera camera = null; private boolean previewRunning = true; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); this.btn = (Button) super.findViewById(R.id.but); this.surfaceView = (SurfaceView) super.findViewById(R.id.surface); this.surfaceHolder = this.surfaceView.getHolder(); this.surfaceHolder.addCallback(new MySurfaceViewCallback()); this.surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); this.surfaceHolder.setFixedSize(960, 640); this.btn.setOnClickListener(new OnClickListenerImpl()); } private class OnClickListenerImpl implements OnClickListener { @Override public void onClick(View v) { if (CameraActivity.this.camera != null) { // 实现自动对焦功能,这里最好不要写自动对焦的,否则可能会对焦不成功 // CameraActivity.this.camera // .autoFocus(new AutoFocusCallbackImpl()); CameraActivity.this.camera.takePicture(shutterCallback, pictureCallback, jpgCallback); } } } /** * * @author xdwang * * @create 2012-11-13 下午10:24:18 * * @email:[email protected] * * @description 捕获屏幕 * */ private class MySurfaceViewCallback implements SurfaceHolder.Callback { @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } @Override public void surfaceCreated(SurfaceHolder holder) { CameraActivity.this.camera = Camera.open(0); // 取得第一个摄像头,可能存在多个,0表示后面的,1表示前置摄像头 Parameters parameters = CameraActivity.this.camera.getParameters(); List<Camera.Size> previewSizes = parameters.getSupportedPreviewSizes(); Camera.Size pictureSize = previewSizes.get(0); parameters.setPreviewSize(pictureSize.width, pictureSize.height); //用以下方式会报:java.lang.RuntimeException: setParameters failed,因为不同手机的分辨率是不一样的 //WindowManager manager = (WindowManager) CameraActivity.this.getSystemService(Context.WINDOW_SERVICE); //Display display = manager.getDefaultDisplay(); //parameters.setPreviewSize(display.getWidth(), display.getHeight());// 设置预览大小,即手机屏幕大小 parameters.setPreviewFrameRate(5); // 每秒5帧 parameters.setPictureFormat(PixelFormat.JPEG); // 图片形式 parameters.set("jpen-quality", 80);// 设置图片质量,最高100 CameraActivity.this.camera.setParameters(parameters); try { CameraActivity.this.camera .setPreviewDisplay(CameraActivity.this.surfaceHolder); } catch (IOException e) { } CameraActivity.this.camera.startPreview(); // 进行预览 CameraActivity.this.previewRunning = true; // 已经开始预览 } @Override public void surfaceDestroyed(SurfaceHolder holder) { if (CameraActivity.this.camera != null) { if (CameraActivity.this.previewRunning) { CameraActivity.this.camera.stopPreview(); // 停止预览 CameraActivity.this.previewRunning = false; } CameraActivity.this.camera.release(); } } } /** * * @author xdwang * * @create 2012-11-13 下午10:46:57 * * @email:[email protected] * * @description 自动对焦 * */ private class AutoFocusCallbackImpl implements AutoFocusCallback { @Override public void onAutoFocus(boolean success, Camera camera) { if (success) { // 对焦成功 CameraActivity.this.camera.takePicture(shutterCallback, pictureCallback, jpgCallback); } } } /** * 生成新的图片 */ private PictureCallback jpgCallback = new PictureCallback() { @Override public void onPictureTaken(byte[] data, Camera camera) { // 保存图片的操作 // 所有的数据保存在byte数组中,将byte数组转换为Bitmap Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length); String fullFileName = MessageFormat.format( "{0}{1}xdwang{1}xdwang_{2}.jpg", Environment .getExternalStorageDirectory().toString(), File.separator, System.currentTimeMillis()); File file = new File(fullFileName); if (!file.getParentFile().exists()) { file.getParentFile().mkdirs(); // 创建文件夹 } try { BufferedOutputStream bos = new BufferedOutputStream( new FileOutputStream(file)); bitmap.compress(Bitmap.CompressFormat.JPEG, 80, bos); // 向缓冲区之中压缩图片 bos.flush(); bos.close(); Toast.makeText(CameraActivity.this, "拍照成功,照片已保存在" + fullFileName + "文件之中!", Toast.LENGTH_SHORT).show(); } catch (Exception e) { Toast.makeText(CameraActivity.this, "拍照失败!", Toast.LENGTH_SHORT) .show(); } //为下一次拍照做准备 CameraActivity.this.camera.stopPreview(); CameraActivity.this.camera.startPreview(); } }; /** * 按下快门后的操作 */ private ShutterCallback shutterCallback = new ShutterCallback() { @Override public void onShutter() { // 按下快门之后进行的操作 } }; /** * 原始图像 */ private PictureCallback pictureCallback = new PictureCallback() { @Override public void onPictureTaken(byte[] data, Camera camera) { } }; }
二、多点触控
多点触控指可以同事对用户的多个屏幕触摸点进行监听,并进行相应处理的一种操作,在 Activity 类中,使用 onTouchEvent() 方法完成多点触控,此方法定义如下:
Public Boolean onTouchEvent(MotionEvent event){}
可以发现,在此方法中有一个 android.view.MotionEvent 类的时间对象,实际上用户可以通过该事件对象完成对多个触摸点的操作监听,而 MotionEvent 类的常用方法如下:
No. |
方法 |
描述 |
1 |
Public final int getAction() |
返回操作的 Action 类型,如按下 or 松开 |
2 |
Public final long getDownTimea() |
返回按下的时间 |
3 |
Public final long getEventTime() |
事件操作的结束时间 |
4 |
Public final int getPointerCount() |
返回同时触摸点的个数 |
5 |
Public final float getX(int pointerIndex) |
取得指定触摸点的 X 坐标 |
6 |
Public final float getY(intpointerIndex) |
取得指定触摸点的 Y 坐标 |
比如完成图片的缩放操作:
package com.iflytek.demo; import android.app.Activity; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Matrix; import android.graphics.Paint; import android.os.Bundle; import android.view.MotionEvent; import android.view.SurfaceHolder; import android.view.SurfaceView; public class MultitouchActivity extends Activity { private static final int SCALEBASIC = 3;// 调整的比率 private int imageX = 0; // 计算图片的X轴 private int imageY = 0; // 计算图片的Y轴 private SurfaceHolder surfaceHolder = null; private int screenWidth = 0; private int screenHeight = 0; private int imageWidth = 0; private int imageHeight = 0; private Bitmap bitmap = null; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); this.screenWidth = super.getWindowManager().getDefaultDisplay() .getWidth();// 取得屏幕的宽度 this.screenHeight = super.getWindowManager().getDefaultDisplay() .getHeight(); this.bitmap = BitmapFactory.decodeResource(super.getResources(), R.drawable.abc); this.imageWidth = this.bitmap.getWidth(); this.imageHeight = this.bitmap.getHeight(); this.imageX = (this.screenWidth - this.imageWidth) / 2; this.imageY = (this.screenHeight - this.imageHeight) / 2; super.setContentView(new MySurfaceView(this));// 使用SurfaceView封装 } private class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback { public MySurfaceView(Context context) { super(context); MultitouchActivity.this.surfaceHolder = super.getHolder(); MultitouchActivity.this.surfaceHolder.addCallback(this);// 添加Callback操作 super.setFocusable(true); // 获得焦点,进行触摸事件 } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {// SurfaceView改变 } @Override public void surfaceCreated(SurfaceHolder holder) {// SurfaceView创建 MultitouchActivity.this.setImage(1.0f, 350, 500);// 设置默认显示图片 } @Override public void surfaceDestroyed(SurfaceHolder holder) {// 销毁 } } /** * @descrption 设置图片 * @author xdwang * @create 2012-11-14下午8:08:41 * @param scale * @param width * @param height */ private void setImage(float scale, int width, int height) { // 改变之后修改图片 Canvas canvas = MultitouchActivity.this.surfaceHolder.lockCanvas(); // 获取画布 Paint paint = new Paint();// 填充底色 canvas.drawRect(0, 0, MultitouchActivity.this.screenWidth, MultitouchActivity.this.screenHeight, paint);// 绘制矩形 Matrix matrix = new Matrix();// 控制图像 matrix.postScale(scale, scale); // 缩放设置,等量缩放 Bitmap target = Bitmap.createBitmap(MultitouchActivity.this.bitmap, 0, 0, width, height, matrix, true);// 创建新图片 this.imageWidth = target.getWidth(); // 取得新图片宽度 this.imageHeight = target.getHeight(); this.imageX = (this.screenWidth - this.imageWidth) / 2;// 重新计算X坐标 this.imageY = (this.screenHeight - this.imageHeight) / 2; canvas.translate(this.imageX, this.imageY); // 图像平移,平移到指定的位置 canvas.drawBitmap(this.bitmap, matrix, paint);// 重新绘图 MultitouchActivity.this.surfaceHolder.unlockCanvasAndPost(canvas);// 解锁画布,并提交图象 } /** * 触摸事件 */ @Override public boolean onTouchEvent(MotionEvent event) { int pointCount = event.getPointerCount();// 取得触控点数量 if (pointCount == 2) { float pointA = event.getY(0);// 取得第一个触摸点的Y坐标 float pointB = event.getY(1);// 取得第2个触摸点的Y坐标 if (pointA < pointB) {// 让pointA保存最大点 float temp = pointA; pointA = pointB; pointB = temp; } if (!(event.getAction() == MotionEvent.ACTION_UP)) {// 用户按下 float scale = this.getScale(pointA, pointB) / SCALEBASIC;// 计算缩放量 MultitouchActivity.this.setImage(scale, 350, 500);// 重设图片 } } return super.onTouchEvent(event); } /** * @descrption 得到缩放比率 * @author xdwang * @create 2012-11-14下午8:15:25 * @param pointA * @param pointB * @return */ private float getScale(float pointA, float pointB) { float scale = pointA / pointB; return scale; } }