`

Android学习11-----多媒体技术(4) 使用摄像头拍照,多点触控

阅读更多

一、摄像头拍照

前面说媒体播放 时了解了 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:xdwangiflytek@gmail.com
	 * 
	 * @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:xdwangiflytek@gmail.com
	 * 
	 * @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;
	}
}

 

 

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics