Android相机使用(系统相机、自定义相机、大图片处理)
本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显示出来,该例子也会涉及到Android加载大图片时候的处理(避免OOM),还有简要提一下有些人SurfaceView出现黑屏的原因。
知识点:
一、调用系统自带的相机应用
二、自定义我们自己的拍照界面
三、关于计算机解析图片原理(如何正确加载图片到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;
- }
- }
Android相机使用(系统相机、自定义相机、大图片处理)的更多相关文章
- Android开发中如何解决加载大图片时内存溢出的问题
Android开发中如何解决加载大图片时内存溢出的问题 在Android开发过程中,我们经常会遇到加载的图片过大导致内存溢出的问题,其实类似这样的问题已经屡见不鲜了,下面将一些好的解决方案分享给 ...
- JS中调用android和ios系统手机打开相机并可选择相册功能
编写不易,如有转载,请声明出处: 梦回河口:http://blog.csdn.net/zxc514257857/article/details/57626154 实现android手机打开相机选择相册 ...
- Android使用BitmapFactory.Options解决加载大图片内存溢出问题
由于Android对图片使用内存有限制,若是加载几兆的大图片便内存溢出.Bitmap会将图片的所有像素(即长x宽)加载到内存中,如果图片分辨率过大,会直接导致内存溢出(java.lang.OutOfM ...
- Android 解决调用系统相册打不开图片 DecodeServices报解码错误
这是由于系统相册不知道你图片目录是一个相册.打开前需要向系统相册“注册一下”,说白了就是让系统相册知道你这个图片所在的文件夹是个相册. private static void scanImageFil ...
- 解决 在Android开发上使用KSOAP2上传大图片到服务器经常报错的问题
原文首发我的主力博客 http://anforen.com/wp/2017/04/android_ksoap2_unexpected_type_position_end_document_null_j ...
- Android调用系统相机、自定义相机、处理大图片
Android调用系统相机和自定义相机实例 本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显示出来,该例子也会涉及到Android加载大图片时候的处理 ...
- Android调用系统相机、自己定义相机、处理大图片
Android调用系统相机和自己定义相机实例 本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,而且因为涉及到要把拍到的照片显示出来,该样例也会涉及到Android载入大图片时候的处 ...
- Android调用系统相机以及自定义相机
0.综述 自定义相机,此处展示简单的相机功能,官方文档中还有相应关于视频拍摄的内容,此处不提 1.添加权限 <!--相机权限,数据存储--> <uses-permission and ...
- android 自定义相机
老规矩,先上一下项目地址:GitHub:https://github.com/xiangzhihong/CameraDemo 方式: 调用Camera API 自定义相机 调用系统相机 由于需求不同, ...
随机推荐
- python 向上取整ceil 向下取整floor 四舍五入round
#encoding:utf-8 import math #向上取整 http://www.manongjc.com/article/1335.html print "math.ceil--- ...
- path和classpath
对于Java的初学者,这两个环境变量,总是要遇到的.这里做一下总结. 1.path和classpath的含义 path是Windows操作系统的一个环境变量. 当操作系统需要运行一个程序,它需要知道该 ...
- android 横竖屏切换
解决Android手机 屏幕横竖屏切换 Android中当屏幕横竖屏切换时,Activity的生命周期是重新加载(说明当前的Activity给销毁了,但又重新执行加载),怎么使屏幕横竖屏切换时,当前的 ...
- 【T电商】 maven初识
PS:本篇博客,就是对于maven的一个简单的总结,认识.可能更多的是借鉴别人的看法,然后结合自己的使用,再加以说明. 首先,什么是maven: Apache Maven is a software ...
- MySQL 批量插入 Update时Replace
建一张试验表如下: 一.批量插入 MySQL的INSERT有一种写法如下: INSERT INTO person VALUES (NULL,'关羽', '2016-04-22 10:00:00'), ...
- 给日志添加“复制”效果
给日志添加如上效果的实现方法: 在日志编辑页面,源代码中,添加如下代码,包裹住 目标内容style1: <div class="cnblogs_code"><di ...
- JavaScript 对象 之继承对象 学习笔记
假设,我们有个这样的需求: 两个种族,每个种族都有 名字.血量(默认200).行为(行为有 跳跃.移动速度 这些属性)等共有属性. 人族能量值比兽人多10%,兽人血量比人族多10%. 职业有战士和法师 ...
- [转]A Guide To using IMU (Accelerometer and Gyroscope Devices) in Embedded Applications.
原文地址http://www.starlino.com/imu_guide.html Introduction There’s now a FRENCH translation of this art ...
- Windows service无法删除怎么办?
自己用c#创建的windows service, 安装后,执行installutil /u 状态变成disable,却删除不了.删除的时候提示"The specified service ...
- -Dmaven.multiModuleProjectDirectory system propery is not set问题解决
-Dmaven.multiModuleProjectDirectory system propery is not set问题解决 eclipse中使用maven插件的时候,运行run as mave ...