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里加入自己的其它button
4.设置相应的拍照button然后听事件
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

代码部分:

一、用系统的相机

button点击之后开启系统相机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;
}

button按下之后的推断:

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;
} }

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

  1. android调用系统的自定义裁剪后得到的图片不清晰,使用MediaStore.EXTRA_OUTPUT获取缓存下的清晰图片

    在使用系统自带的剪切图片工具时,通常只能设置一些比较小的值,像 intent.putExtra("outputX", 320); intent.putExtra("out ...

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

    Android调用系统相机和自定义相机实例 本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显示出来,该例子也会涉及到Android加载大图片时候的处理 ...

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

    Android上传图片之调用系统拍照和从相冊选择图片 本篇文章已授权微信公众号 guolin_blog (郭霖)独家公布 前言: 万丈高楼平底起,万事起于微末.不知不觉距离上篇博文已近四个月,2015 ...

  4. Java乔晓松-android中调用系统拍照功能并显示拍照的图片

    android中调用系统拍照功能并显示拍照的图片 如果你是拍照完,利用onActivityResult获取data数据,把data数据转换成Bitmap数据,这样获取到的图片,是拍照的照片的缩略图 代 ...

  5. Android调用系统相册和拍照的Demo

    最近我在群里看到有好几个人在交流说现在网上的一些Android调用系统相册和拍照的demo都有bug,有问题,没有一个完整的.确实是,我记得一个月前,我一同学也遇到了这样的问题,在低版本的系统中没问题 ...

  6. Android 调用系统分享文字、图片、文件,可直达微信、朋友圈、QQ、QQ空间、微博

    原文:Android 调用系统分享文字.图片.文件,可直达微信.朋友圈.QQ.QQ空间.微博 兼容SDK 18以上的系统,直接调用系统分享功能,分享文本.图片.文件到第三方APP,如:微信.QQ.微博 ...

  7. android调用系统相机并获取图片

    如果不是特别的要求,通过拍照的方式取得图片的话,我们一般调用系统的拍照来完成这项工作,而没必要再自己去实现一个拍照功能.调用系统相机很简单,只需要一个intent就可以跳转到相几界面,然后再通过onA ...

  8. Android 调用系统相机拍照保存以及调用系统相册的方法

    系统已经有的东西,如果我们没有新的需求的话,直接调用是最直接的.下面讲讲调用系统相机拍照并保存图片和如何调用系统相册的方法. 首先看看调用系统相机的核心方法: Intent camera = new ...

  9. Android调用系统相机和相册并解决data为空,OOM,图片角度不对的问题

    最近公司项目用到手机拍照的问题,好不容易在网上copy了一些代码,但是运行起来一大堆bug,先是三星手机上运行程序直接崩掉,debug了一下原来是onActivityResult中data返回为空,找 ...

随机推荐

  1. JavaScript 、ECMAScript、commonJS 发展历史 与标准化发展

    本文介绍下JavaScript和 ECMAScript的诞生及发展历史,以及标准化过程. 一.JavaScript诞生 1994年,网景公司(Netscape)发布了Navigator浏览器0.9版. ...

  2. 基于visual Studio2013解决C语言竞赛题之0518回文数

     题目

  3. Eclipse下执行main函数报java.lang.NoClassDefFoundError的解决

    今天执行eclipse下的一个java类,无论run还是debug,都报java.lang.NoClassDefFoundError.而且把main中函数都注释掉,执行还是报一样的错. 检查了一下这个 ...

  4. Java之GUI编程(一)

    GUI全称Graphical User Interfaces,意为图形用户户界面,又称为图形用户接口.GUI指的就是採用图形方式显示的计算机操作用户界面,打个例如吧.我们点击QQ图标,就会弹出一个QQ ...

  5. Qt删除指定文件

    Qt删除指定文件: QFile fileTemp(filename); fileTemp.remove();

  6. SecureCRT辅助解决方案

    SecureCRT辅助解决方案 1. 下载SecureCRT 7.3版本并激活: 2. SecureCRT linux配色方案: 3. SecureCRT设置log保存方案: 1. secureCRT ...

  7. BZOJ 2318: Spoj4060 game with probability Problem( 概率dp )

    概率dp... http://blog.csdn.net/Vmurder/article/details/46467899 ( from : [辗转山河弋流歌 by 空灰冰魂] ) 这个讲得很好 , ...

  8. 15-UIKit(view布局、Autoresizing)

    目录: 1. 纯代码布局 2. 在View中进行代码布局 3. Autoresizing 回到顶部 1. 纯代码布局 纯代码布局分VC下和V下 [MX1-layout-code] 在VC下覆盖view ...

  9. Qt 智能指针学习(7种QT智能指针和4种std智能指针)

    从内存泄露开始? 很简单的入门程序,应该比较熟悉吧 ^_^ #include <QApplication> #include <QLabel> int main(int arg ...

  10. 【Apache ZooKeeper】理解ZooKeeper中的ZNodes

    理解ZooKeeper中的ZNodes 翻译自:http://zookeeper.apache.org/doc/r3.1.2/zookeeperProgrammers.html ZooKeeper中的 ...