Android调用系统相机、自定义相机、处理大图片

Android调用系统相机和自定义相机实例

本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显示出来,该例子也会涉及到Android加载大图片时候的处理(避免OOM),还有简要提一下有些人SurfaceView出现黑屏的原因。

Android应用拍照的两种方式,下面为两种形式的Demo展示出来的效果。

  

知识点:

一、调用系统自带的相机应用

二、自定义我们自己的拍照界面


三、关于计算机解析图片原理(如何正确加载图片到Android应用中)

所需权限:

<uses-permission android:name="android.permission.CAMERA" />

<uses-feature android:name="android.hardware.camera" />

<uses-feature android:name="android.hardware.camera.autofocus" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

一、调用系统照相机程序拍照(方式一)

1.定义所需要的权限
2.我们需要定义调用系统相机App的Intent,当然是通过设定IntentFilter中的Action来打开我们想要的activity了。

MediaStore.ACTION_IMAGE_CAPTURE - 这个Action将打开拍照的系统相机。返回一个Image

MediaStore.ACTION_VIDEO_CAPTURE - 这个Action将打开录像的系统相机。返回一个Video

在MediaStore.ACTION_IMAGE_CAPTURE中,我们可以看到这段话:

【The caller may pass an extra EXTRA_OUTPUT to control where this image will be

written. If the EXTRA_OUTPUT is not present, then a small sized image is returned

as a Bitmap object in the extra field. This is useful for applications that only

need a small image. If the EXTRA_OUTPUT is present, then the full-sized image will

be written to the Uri value of EXTRA_OUTPUT.】

3.API规定我们传入拍照得到图片的存储位置的Uri。否则Bimmap将以一个压缩后的形式返回到我们当前Activity.

intent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri); // set the image file name

则会把拍照的图片存储到我们传入的Uri对应的File里面。

4.我们调用startActivityForResult(intent)来启动这样一个系统相机app之后,然后在当前应用Activity的onActivityResult()中接受到返回拍照成功或者失败的消息,做相应处理。

5.“压缩处理”(Android应用中加载大图片),并显示到ImageView中。

二、自定义照相机

1.检查相机是否存在,并获取相机Camera。

2.创建一个相机图像预览类:extends SurfaceView 并 implements SurfaceHolder (我定义:MySurfaceView)
3.把这个预览类放入一个自定义布局layout里面,并且可以在layout里添加自己的其他按钮
4.设置对应的拍照按钮然后听事件
5.捕获照片和保存图片

6.释放掉我们使用的相机Camera,不然之后其他应用将无法使用它。

三、计算机解析图片的方式和Android中大图片Bitmap的压缩显示处理

这个问题有点老生长谈了,平时我们经常遇到一些图片资源,我们把它加载到内存发现抛出内存不够用的异常,即OOM,当然加载图片时出现的OOM情况有很多种,比如单张图片没有做压缩,导致图片占用内存过大而发生内存溢出,也有多张图片一次性加载进来,导致的内存溢出。

通常单张大图,我们加载进来往往会经过一个图片的压缩处理的过程,而如果多张图片加载,我们可能就需要一些缓存机制,再加上一些算法来保证程序不出现OOM。

我们这里想要讲的知识点跟单张大图比较有关系

首先,我们知道一个图片,它是由很多像素点来表示的,而像素点的个数只跟图片的分辨率有关,而跟图片所占的内存空间大小无关。比如我们的桌面壁纸:1280 * 768 的分辨率,那么它就有 1280 * 768 = 983040个像素点,这意味着什么呢?我们知道我们要表示一个像素点的颜色,最经常我们需要RGB三种颜色来表示,而R:0~255,相当于两个FF的位置,就是8位,这样的话RGB合起来,一个像素点的表示就需要24位(这就是我们平衡听到的24位图),而加上透明度的8位,就是平时说的32位图。那么一张图片,它加载到内存中的话,它会占用多大的空间呢?

计算方法:(像素点 * 一个像素所占用的byte数) / 1024 / 1024 (MB)

以1280 * 768 的分辨率,32位图为例:所占内存大小: ((1280 * 768 * (32 / 8)) / 1024)/1024=3.75(MB)

说了这么多,那么我们再来说下Android系统的规定吧,Android系统严格规定了每个应用所能分配的最大的内存为多少,我们知道有一个VM值(在我们创建模拟器的时候),这个VM值里面便是我们所说的堆空间(Heap Size),当你的应用占用的空间已经超出我们定义的堆空间大小,那么不好意思,OOM

这样的话,我们明白了图片的大小占据原理,还有尽量不要超出这个堆空间,那么OK,现在问题变得简单了。如果我们有一种方式可以在图片加载进来之前,知道图片的大小,然后改变它的长、宽,这样的话,分辨率便变小了,这样出来的乘积也就变小了。比如:我们的屏幕只有320 * 240, 这时候你加载大分辨的图片进来最多也只能显示成这样,所以我们常采用的是对图片进行压缩处理。这里有个概念叫压缩比:

长:1024 / 320 = 3.2  约等于 3

宽:768 / 240 = 3.2

那这样我们如果把图片压缩成这样大小的,最后的图片加载进来的大小便是

((320 * 240 * (32 / 8)) / 1024)/1024=0.29(MB)

希望我这样讲完,大家都能听懂了,我这里先把照相机实例中出现的关于如果处理这块图片的代码先粘出来

//-----------------------Android大图的处理方式---------------------------
	private void setPicToImageView(ImageView imageView, File imageFile){
		int imageViewWidth = imageView.getWidth();
		int imageViewHeight = imageView.getHeight();
		BitmapFactory.Options opts = new Options();

		//设置这个,只得到Bitmap的属性信息放入opts,而不把Bitmap加载到内存中
		opts.inJustDecodeBounds = true;
		BitmapFactory.decodeFile(imageFile.getPath(), opts);

		int bitmapWidth = opts.outWidth;
		int bitmapHeight = opts.outHeight;
		//取最大的比例,保证整个图片的长或者宽必定在该屏幕中可以显示得下
		int scale = Math.max(imageViewWidth / bitmapWidth, imageViewHeight / bitmapHeight);

		//缩放的比例
		opts.inSampleSize = scale;
		//内存不足时可被回收
		opts.inPurgeable = true;
		//设置为false,表示不仅Bitmap的属性,也要加载bitmap
		opts.inJustDecodeBounds = false;

		Bitmap bitmap = BitmapFactory.decodeFile(imageFile.getPath(), opts);
		imageView.setImageBitmap(bitmap);
	}

关于堆空间:

堆(HEAP)是VM中占用内存最多的部分,通常是动态分配的。堆的大小不是一成不变的,通常有一个分配机制来控制它的大小。比如初始的HEAP是4M大,当4M的空间被占用超过75%的时候,重新分配堆为8M大;当8M被占用超过75%,分配堆为16M大。倒过来,当16M的堆利用不足30%的时候,缩减它的大小为8M大。重新设置堆的大小,尤其是压缩,一般会涉及到内存的拷贝,所以变更堆的大小对效率有不良影响。

废话少说下面就看代码咯~~为了大家看起来方便点,代码的结构可能不是很规范!

源码下载地址:http://download.csdn.net/detail/u011133213/7844683

代码部分:

一、用系统的相机

按钮点击之后开启系统相机Activity

findViewById(R.id.system_camera_btn).setOnClickListener(new OnClickListener() {

			@Override
			public void onClick(View v) {
				Intent intent = new Intent();
				intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE);
				imageFileUri = getOutFileUri(TYPE_FILE_IMAGE);//得到一个File Uri
				intent.putExtra(MediaStore.EXTRA_OUTPUT, imageFileUri);
				startActivityForResult(intent, SYSTEM_CAMERA_REQUESTCODE);
			}
		});

生成File文件,并得到Uri

//-----------------------生成Uri---------------------------------------
	//得到输出文件的URI
	private Uri getOutFileUri(int fileType) {
		return Uri.fromFile(getOutFile(fileType));
	}

	//生成输出文件
	private File getOutFile(int fileType) {

		String storageState = Environment.getExternalStorageState();
		if (Environment.MEDIA_REMOVED.equals(storageState)){
			Toast.makeText(getApplicationContext(), "oh,no, SD卡不存在", Toast.LENGTH_SHORT).show();
			return null;
		}

		File mediaStorageDir = new File (Environment
				.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)
				,"MyPictures");
		if (!mediaStorageDir.exists()){
			if (!mediaStorageDir.mkdirs()){
				Log.i("MyPictures", "创建图片存储路径目录失败");
				Log.i("MyPictures", "mediaStorageDir : " + mediaStorageDir.getPath());
				return null;
			}
		}

		File file = new File(getFilePath(mediaStorageDir,fileType));

		return file;
	}
	//生成输出文件路径
	private String getFilePath(File mediaStorageDir, int fileType){
		String timeStamp =new SimpleDateFormat("yyyyMMdd_HHmmss")
							.format(new Date());
		String filePath = mediaStorageDir.getPath() + File.separator;
		if (fileType == TYPE_FILE_IMAGE){
			filePath += ("IMG_" + timeStamp + ".jpg");
		}else if (fileType == TYPE_FILE_VEDIO){
			filePath += ("VIDEO_" + timeStamp + ".mp4");
		}else{
			return null;
		}
		return filePath;
	}
	

二、用自定义的相机

检测相机设备是否存在:

/*检测相机是否存在*/
	private boolean checkCameraHardWare(Context context){
		PackageManager packageManager = context.getPackageManager();
		if (packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA)){
			return true;
		}
		return false;
	}

按钮按下之后的判断:

findViewById(R.id.myapp_camera_btn).setOnClickListener(new OnClickListener() {

			@Override
			public void onClick(View v) {
				if (checkCameraHardWare(getApplicationContext())){
					Intent intent = new Intent(getApplicationContext(), MyCameraActivity.class);
					startActivity(intent);
				}else{
					Toast.makeText(getApplicationContext(), "没有相机存在", Toast.LENGTH_SHORT).show();
				}
			}
		});
		

自定义的SurfaceView类:

package cn.panghu.camera;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.hardware.Camera;
import android.util.AttributeSet;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback{

	private Camera camera = null;
	private SurfaceHolder surfaceHolder = null;

	public MySurfaceView(Context context, Camera camera) {
		super(context);
		this.camera = camera;
		surfaceHolder = getHolder();
		surfaceHolder.addCallback(this);
		surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
	}
	public MySurfaceView(Context context) {
		super(context);
		// TODO Auto-generated constructor stub
	}

	@Override
	public void surfaceCreated(SurfaceHolder holder) {
		try{
			camera.setPreviewDisplay(surfaceHolder);
			camera.startPreview();
		}catch(Exception e){
			e.printStackTrace();
		}
	}

	@Override
	public void surfaceChanged(SurfaceHolder holder, int format, int width,
			int height) {
		//根本没有可处理的SurfaceView
		if (surfaceHolder.getSurface() == null){
			return ;
		}

		//先停止Camera的预览
		try{
			camera.stopPreview();
		}catch(Exception e){
			e.printStackTrace();
		}

		//这里可以做一些我们要做的变换。

		//重新开启Camera的预览功能
		try{
			camera.setPreviewDisplay(surfaceHolder);
			camera.startPreview();
		}catch(Exception e){
			e.printStackTrace();
		}
	}

	@Override
	public void surfaceDestroyed(SurfaceHolder holder) {

	}

}

自定义相机Activity类:(为了避免当用户按下Home键,之后再回到我们App中,SurfaceView变黑屏,我们需要将SurfaceView加载到FrameLayout中的代码写在onResume中)

package cn.panghu.camera;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;

import android.app.Activity;
import android.hardware.Camera;
import android.hardware.Camera.PictureCallback;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.Toast;

import com.example.camerademoapp.R;

public class MyCameraActivity extends Activity {
	private Button btn_camera_capture = null;
	private Button btn_camera_cancel = null;
	private Button btn_camera_ok = null;

	private Camera camera = null;
	private MySurfaceView mySurfaceView = null;

	private byte[] buffer = null;

	private final int TYPE_FILE_IMAGE = 1;
	private final int TYPE_FILE_VEDIO = 2;

	private PictureCallback pictureCallback = new PictureCallback() {

		@Override
		public void onPictureTaken(byte[] data, Camera camera) {
			if (data == null){
				Log.i("MyPicture", "picture taken data: null");
			}else{
				Log.i("MyPicture", "picture taken data: " + data.length);
			}

			buffer = new byte[data.length];
			buffer = data.clone();
		}
	};
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		super.onCreate(savedInstanceState);
		setContentView(R.layout.mycamera_layout);

		btn_camera_capture = (Button) findViewById(R.id.camera_capture);
		btn_camera_ok = (Button) findViewById(R.id.camera_ok);
		btn_camera_cancel = (Button) findViewById(R.id.camera_cancel);

		btn_camera_capture.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {

				camera.takePicture(null, null, pictureCallback);

				btn_camera_capture.setVisibility(View.INVISIBLE);
				btn_camera_ok.setVisibility(View.VISIBLE);
				btn_camera_cancel.setVisibility(View.VISIBLE);
			}
		});
		btn_camera_ok.setOnClickListener(new OnClickListener() {

			@Override
			public void onClick(View v) {
				//保存图片
				saveImageToFile();

				camera.startPreview();
				btn_camera_capture.setVisibility(View.VISIBLE);
				btn_camera_ok.setVisibility(View.INVISIBLE);
				btn_camera_cancel.setVisibility(View.INVISIBLE);
			}
		});
		btn_camera_cancel.setOnClickListener(new OnClickListener() {

			@Override
			public void onClick(View v) {

				camera.startPreview();
				btn_camera_capture.setVisibility(View.VISIBLE);
				btn_camera_ok.setVisibility(View.INVISIBLE);
				btn_camera_cancel.setVisibility(View.INVISIBLE);
			}
		});
	}
	@Override
	protected void onPause() {
		// TODO Auto-generated method stub
		super.onPause();

		camera.release();
		camera = null;
	}

	@Override
	protected void onResume() {
		// TODO Auto-generated method stub
		super.onResume();
		if (camera == null){
			camera = getCameraInstance();
		}
		//必须放在onResume中,不然会出现Home键之后,再回到该APP,黑屏
		mySurfaceView = new MySurfaceView(getApplicationContext(), camera);
		FrameLayout preview = (FrameLayout) findViewById(R.id.camera_preview);
		preview.addView(mySurfaceView);
	}

	/*得到一相机对象*/
	private Camera getCameraInstance(){
		Camera camera = null;
		try{
			camera = camera.open();
		}catch(Exception e){
			e.printStackTrace();
		}
		return camera;
	}

	//-----------------------保存图片---------------------------------------
	private void saveImageToFile(){
		File file = getOutFile(TYPE_FILE_IMAGE);
		if (file == null){
			Toast.makeText(getApplicationContext(), "文件创建失败,请检查SD卡读写权限", Toast.LENGTH_SHORT).show();
			return ;
		}
		Log.i("MyPicture", "自定义相机图片路径:" + file.getPath());
		Toast.makeText(getApplicationContext(), "图片保存路径:" + file.getPath(), Toast.LENGTH_SHORT).show();
		if (buffer == null){
			Log.i("MyPicture", "自定义相机Buffer: null");
		}else{
			try{
				FileOutputStream fos = new FileOutputStream(file);
				fos.write(buffer);
				fos.close();
			}catch(IOException e){
				e.printStackTrace();
			}
		}
	}

	//-----------------------生成Uri---------------------------------------
	//得到输出文件的URI
	private Uri getOutFileUri(int fileType) {
		return Uri.fromFile(getOutFile(fileType));
	}

	//生成输出文件
	private File getOutFile(int fileType) {

		String storageState = Environment.getExternalStorageState();
		if (Environment.MEDIA_REMOVED.equals(storageState)){
			Toast.makeText(getApplicationContext(), "oh,no, SD卡不存在", Toast.LENGTH_SHORT).show();
			return null;
		}

		File mediaStorageDir = new File (Environment
				.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)
				,"MyPictures");
		if (!mediaStorageDir.exists()){
			if (!mediaStorageDir.mkdirs()){
				Log.i("MyPictures", "创建图片存储路径目录失败");
				Log.i("MyPictures", "mediaStorageDir : " + mediaStorageDir.getPath());
				return null;
			}
		}

		File file = new File(getFilePath(mediaStorageDir,fileType));

		return file;
	}
	//生成输出文件路径
	private String getFilePath(File mediaStorageDir, int fileType){
		String timeStamp =new SimpleDateFormat("yyyyMMdd_HHmmss")
							.format(new Date());
		String filePath = mediaStorageDir.getPath() + File.separator;
		if (fileType == TYPE_FILE_IMAGE){
			filePath += ("IMG_" + timeStamp + ".jpg");
		}else if (fileType == TYPE_FILE_VEDIO){
			filePath += ("VIDEO_" + timeStamp + ".mp4");
		}else{
			return null;
		}
		return filePath;
	}

}

时间: 2024-10-27 09:44:10

Android调用系统相机、自定义相机、处理大图片的相关文章

Android调用系统相册和相机选择图片并显示在imageview中

Android调用系统相册和相机选择图片并显示在imageview中,在系统调用相机拍摄中,直接返回的是经过压缩处理后的图像,当你直接把返还后的图片放在imageview中时 图片就会非常的模糊,所以要经过先存放在sd中,然后在处理并显示.当调用系统相册时,因为Android系统从4.4版本以后系统不再返回真实的uri路径,而是封装过后的uri路径,所以当你写代码时必须注意,4.4是一个分水岭,4.4以上的版本必须就通过解析和相应的处理才能获取到真实的uri路径. 先上程序运行的结果. 这个是调

android 调用系统图库查看指定路径的图片

//使用IntentIntent intent = new Intent(Intent.ACTION_VIEW);//Uri mUri = Uri.parse("file://" + picFile.getPath());Android3.0以后最好不要通过该方法,存在一些小Bugintent.setDataAndType(Uri.fromFile(picFile), "image/*");startActivity(intent);

android 调用系统相机获取图片、调用系统相册获取图片,并对图片进行截取

打开系统相册获取图片并截取,代码相对简单 1 Intent intent = new Intent(Intent.ACTION_GET_CONTENT,null); 2 intent.setType("image/*"); 3 intent.putExtra("crop", "true"); 4 5 //WIDTH 和 HEIGHT指的是截取框的宽高比例,如设WIDTH = 1,HEIGHT = 1,截取框就为正方形 6 intent.putEx

android调用系统相机拍照并保存在本地

import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.util.Calendar; import java.util.Locale; import android.annotation.SuppressLint; import android.app.Activity; import an

Android 调用系统相机以及相册源码

Android 调用系统相机拍照.以及相册.完成之后图片是上传到app上.前面的功能已经测试过了.没有上传到服务器,因为我没服务器测试.但项目里面有个类可以参考上传图片到服务器,我就没测试了.接下来看代码,虽然注释写得少,但其作用看英文单词意思,又在或是查看调用. 项目源码下载地址:http://download.csdn.net/detail/qq_16064871/8585169 转载请注明出处: http://blog.csdn.net/qq_16064871 package com.ex

调用系统相册或相机工具类

需求:点击修改头像,弹出对话框提示选择相册还是相机,从而调用系统相册或相机 import android.app.Activity; import android.content.Context; import android.content.Intent; import android.database.Cursor; import android.graphics.Bitmap; import android.net.Uri; import android.os.Bundle; import

Swift—调用系统相册和相机

p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 24.0px Menlo; color: #000000 } p.p2 { margin: 0.0px 0.0px 0.0px 0.0px; font: 24.0px Menlo; color: #000000; min-height: 28.0px } p.p3 { margin: 0.0px 0.0px 0.0px 0.0px; font: 24.0px Menlo; color: #294c50 }

iOS调用系统相册、相机 显示中文标题

最近做头像上传功能需要使用系统相册.相机,在调用系统相册.相机发现是英文的系统相簿界面后标题显示"photos",但是手机语言已经设置显示中文,纠结半天,最终在info.plist设置解决问题. 发现在项目的info.plist里面添加Localized resources can be mixed YES(表示是否允许应用程序获取框架库内语言)即可解决这个问题.特此记录下以便以后查看和别人解决问题

Android上传图片之调用系统拍照和从相册选择图片

Android上传图片之调用系统拍照和从相册选择图片 前言: 万丈高楼平底起,万事起于微末.不知不觉距离上篇博文已近四个月,2015年12月17日下午发了第一篇博文,现在是2016年4月6日.时间间隔长的过分啊,我自己都看不下去了.原因呢?当然是自己的原因,其实是有很多时间来些博客的,但是这些时间都花在DOTA上了(还是太年轻啊).请原谅我的过错--. 一.概述: 现在几乎应用都会用到上传图片的功能,而要上传图片,首先得选择图片,本文不针对如何上传图片到服务器(每个项目与服务器交互的方式不同,因