转载请指明出处@https://blog.csdn.net/qq_41912447
版权归博主所有
/**
**本文由自己搜集资料编程 如有错误请指明
**李紫洋著
*/
相机成为了一个app必不可少的一个重要组成 它可以实现拍照,扫描二维码,视频聊天等 今天博主就来讲一下安卓开发相机 今天就带大家入门 更多资料请参考谷歌安卓开发官网android developers
简介
相机是属于系统资源 本身硬件就自带相机 你可以直接调用他来大大减少你的开发时间 像美颜相机这种app就需要自定义相机 现在大部分都有第三方平台 提供api供你使用 这同时大大减少你的代码量和团队创建的资金 第三方框架的使用大大减少你的工作量 同时他们的体系更加的完善 会减少bug的出现
设备兼容的问题需要大家考虑 像小米 oppo vivo 华为等设备都对硬件系统做了一定的处理 用来优化他们的设备性能性 所以请参考他们这些设备的官网开发资料进行一定的处理
一般的处理方法是在功能清单注册这些设备的权限 在代码中对这些设备进行一定的处理
》》》1如果有必不可少的权限则退出 像相机app如果不给予相机权限只能强制退出软件了 这时就要提示用户如果拒绝则不再可以使用此软件
》》》2如果此权限并不正常影响你的app正常启动 则可以选择在使用你的功能时再次询问是否打开权限 这时可以使用intent信使让用户跳转到系统权限打开的类手动打开权限
》》为了避免多次对话框出现对用户体验造成影响 甚至会卸载app
用户可能随时关闭权限 对你的app进行管理 有可能你的app是一个相机软件 用户不能理解你使用电话权限的理由 你可以在用户拒绝的时候提示用户此权限的意义 不要在权限申请的时候进行处理 过多的提示会降低用户的体验
3对手机系统版本做出判断 低版本安卓设备权限处理与6.0版本以上处理方法不同 主要是6.0动态权限加入危险权限和安全权限 安全权限则只需要在功能清单注册即可 一般一组权限只需要注册一个即可 用户同意则会给予你这组权限的所有
**需要对设备本地数据库具有一定的理解 用来存储一些重要数据永久保存 将在软件卸载的时候消失 同时也要对网络传输数据库有一定的理解 了解json xml传输数据 json有一个好的工具是谷歌的gson 后续会编写相关的博客
6.0动态权限有一张步骤图 这里就不公告了 我用自己的理解把他用通俗的语言写出来了 请不要觉得文字太多 每个点都是需要考虑的
注 所有资源请以谷歌开发者文档官网为标准
好了废话不多说 现在来看我的一个简单示例代码
这里示例调用系统相机 和自定义相机两个部分
调用系统相机
下面来示例这个java类的编码
package com.liziyang.dall;
//这里是自己的包名 必不可少的
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import java.io.File;
import java.util.EventListener;
//这里是导入的api库 有些是安卓的 有些是java的 也可以是框架的库 在写本类的时候需要查看导入的库 别导错库
public class MainActivity extends Activity {
//新建一个类的的初始格式 这个类继承了活动 用来实现UI界面的逻辑实现
//extends是继承属性 你可以根据需要继承所需的类
//声明控件
private Button button,button2;
private ImageView imageView;
private TextView textView;
private String fileName="";
//定义一个 路径的字符串 这里是一个空值 下面将会用到
//定义一个string类型的fileName方便以后使用 用来作为拍摄的图片名字
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate( savedInstanceState );
//四大组件活动的生命周期 oncreate
//UI界面控件的逻辑实现 需要在这里实现
setContentView( R.layout.activity_main );
//设置上下文对象
//当java类继承活动就需要设置上下文
button=findViewById( R.id.button );
button2=findViewById( R.id.button2 );
imageView=findViewById( R.id.imageView );
textView=findViewById( R.id.textView );
//声明对象绑定UI控件ID
button.setOnClickListener( new View.OnClickListener() {
//按钮实现点击功能 不要在xml布局文件中使用onclick 可能会出现bug 部分机型不匹配的可能
public void onClick(View view) {
//使用intent启动组件
//系统相机的activity位置 mediaStore.ACTION_IMAGE_CAPTURE
//这里使用意图 将界面跳转到系统相机界面 这些系统界面的代码简写在官方文档查询 最好把他们下载下来 方便使用的时候查询
Intent intent = new Intent( MediaStore.ACTION_IMAGE_CAPTURE );
//意图intent 也可以写成下面两行 相信大家都懂
//Intent intent=new Intent();
//ntent.setClass(MainActivity.this,MediaStore.ACTION_IMAGE_CAPTURE .class);
//定义一个file路径 设置为内存卡路径
File dir= Environment.getExternalStorageDirectory();//存在sd卡上
//如果没有sd卡或sd卡内存已满则会保存到手机设备中
//最好把网络需要的图片缓存下来 像app内置图片最好保存到分配的内置包中 防止给用户带来差的体验
//假如你在自己相册看到你不想要的图片视频等 你就会卸载
//命名图片 得到系统毫秒
fileName="hey"+System.currentTimeMillis()+".jpg";
//初始化路径 把是存在在sd卡上和 保存图片的名字设置进去
File file=new File( dir,fileName );
//封装路径 把文件路径和名字两个参数封装进去
Uri fileuri=Uri.fromFile( file );
//设置图片路径 可以为网络等 最好选择一个框架 例如glide和ImageLoader框架对图片进行优化 如果是网络获取文件 例如okHttp或xUtils 这些框架建议大家去了解一下
//设置uri 其他需要图片解码 uri途径就是存储设备和图片名
//选择拍摄照片存放位置 第二个可以给这个图片一个uri
//将intent添加额外数据 额外 读出 uri位置
intent.putExtra( MediaStore.EXTRA_OUTPUT,fileuri );
//意图添加其他传输数据
//需要接受的方法 用类似方法将他取出来
//有返回启动activity intent传输 返回码为0 返回码是自己写的
startActivityForResult( intent,0);
//带传值的启动活动 这个方法常用
//startActivity不太常用 他不可以传值
}
} );
/**启动系统相机的方法 只需要用intent启动系统组件 指明系统组件
* 自定义存储位置 得到外部存储 定义路径 自定义图片名字 写一个string数组为图片名字 为了防止名字相同 使用毫秒
* 将自定义名字和路径包装到uri中
*指明intent需要的功能 回传数据
*添加intent功能和uri地址
* 带回传启动activity 指明intent和回传码
*/
button2.setOnClickListener( new View.OnClickListener() {
@Override
public void onClick(View view) {
//这里就直接使用意图跳转界面 所需的放在另一个类方法实现
Intent intent=new Intent( MainActivity.this,Main2Activity.class );
startActivity( intent );
}
} );
}
//有返回值会回调这个方法
@Override
protected void onActivityResult(int requestCode,int resultCode,Intent data) {
super.onActivityResult( requestCode,resultCode,data );
//打印log 方便测试
Log.i( "TEST","onActivityResult" );
//如果回传码为0和回传码生成
if(requestCode==0&&resultCode==RESULT_OK) {
//指明路径
File dir = Environment.getExternalStorageDirectory();//存在sd卡上
//命名图片 得到系统毫秒
File file = new File( dir,fileName );
Uri fileuri = Uri.fromFile( file );
//intent启动组件 返回数据的方法 onActivityResult
//设置图片uri uri图片不用解码
imageView.setImageURI( fileuri );
//建议使用URI方法 不要需要解码否则可能会出现bug 要不然使用框架
}
}
}
布局不是很美观 只是一个demo 有需要可以参考网络资料 对布局优化
来看一下布局代码
来看自定义相机的java具体实现类的代码编写
package com.liziyang.dall;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.hardware.Camera;
import android.os.Bundle;
import android.app.Activity;
import android.os.Environment;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.widget.ImageButton;
import android.widget.ImageView;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class Main2Activity extends Activity {
//指明需要使用到相机
//声明相机 因为需要用到自定义相机
private Camera camera;
//使用屏幕surface
//只要是自定义的相机视频动画等需要用到屏幕view
private SurfaceView surfaceView;
//屏幕管理者
private SurfaceHolder surfaceHolder;
//屏幕管理的方法名
private ImageButton imageButton2;
private ImageView imageView3;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate( savedInstanceState );
setContentView( R.layout.activity_main2 );
surfaceView=findViewById( R.id.surfaceView2 );
surfaceHolder=surfaceView.getHolder();
imageButton2=findViewById( R.id.imageButton2 );
imageView3=findViewById( R.id.imageView3 );
imageButton2.setOnClickListener( new View.OnClickListener() {
@Override
public void onClick(View view) {
//照片拍摄图片 照片回调
//括号中需要传入几个参数
camera.takePicture( null,null,new Camera.PictureCallback() {
@Override
public void onPictureTaken(byte[] data,Camera camera) {
//预览
//bitmap位图 array数组 数据 数据的长度
//得到数据bitmap 数组 数据为0 数据的长度
Bitmap bitmap= BitmapFactory.decodeByteArray( data,0,data.length );
//预览视图设置图片位图
imageView3.setImageBitmap( bitmap );
//这里把拍摄的图片设置到UI界面的图片上
//并没有进行处理 你也可以把他储存到内存卡上 把bitmap进行封装
//buffer缓冲 outPut输出 steam流 缓冲输入流初始化
BufferedOutputStream bufferedOutputStream=null;
//对缓存进行初始化
//可能会出现bug 对可能出现的异常进行抛出
try {
String fileName="hey"+System.currentTimeMillis()+".jpg";
//设置文件名
bufferedOutputStream = new BufferedOutputStream( new FileOutputStream( new File( Environment.getExternalStorageDirectory(),fileName ) )) ;
//保存 100压缩率 按原图保存
//compress压缩 format格式 100 缓冲输出流
bitmap.compress( Bitmap.CompressFormat.JPEG,100,bufferedOutputStream );
//对图片进行压缩 设置输出流为内容
}catch (FileNotFoundException e){
e.printStackTrace();
}finally {
//最后对所用到的东西进行关闭 以免内存泄漏
//使用完毕假如缓冲输出流不是空值 关闭缓存输入流
//防止使用后用户打开其他软件会出现bug
if (bufferedOutputStream!=null){
//判断输出流是否为空
try {
//对输出流进行关闭
bufferedOutputStream.close();
} catch (IOException e) {
//抛出异常
e.printStackTrace();
}
}
}
}
} );
}
} );
//缓存持有者 添加回传
surfaceHolder.addCallback( new SurfaceHolder.Callback(){
//重写下面的方法
@Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
//缓存创建的时候
//得到相机 初始化
//得到相机对象
camera=getCamera();
//如果相机不为空
if (camera!=null){
try {
//preview预览 display显示 缓存持有者
//设置缓存路径
camera.setPreviewDisplay( surfaceHolder );
//缓存开始预览
//开始缓存
camera.startPreview();
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Override
public void surfaceChanged(SurfaceHolder surfaceHolder,int i,int i1,int i2) {
}
@Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
//缓存销毁时 release释放
//释放相机 以免再次使用相机出现被占用的现象
releaseCamera();
}
} );
//内容持有者设置种类
surfaceHolder.setType( SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
//得到相机
camera=getCamera();
//如果相机不为空
if (camera!=null){
try {
//相机 设置preview预览 display显示
camera.setPreviewDisplay( surfaceHolder );
//相机开始预览 这是相机里的方法 调用
//preview预览
camera.startPreview();
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Override
protected void onDestroy() {
//release释放
releaseCamera();
super.onDestroy();
}
//得到相机
private Camera getCamera() {
//如果相机 为空
if (camera == null) {
try {
//打开相机
camera = Camera.open();
//返回相机
return camera;
} catch (Exception ex) {
return null;
}
}
//不管怎么样 返回相机
return camera;
}
//release释放
private void releaseCamera(){
//如果相机不为空 释放相机 初始化
if (camera!=null){
camera.release();
camera=null;
}
}
}
/***
最后别忘了在功能清单注册权限
注:自定义相机权限处理方法不同 读取内存卡 使用相机 为危险权限 请注意加以处理 这里不写 用户必须去打开相应的权限 才可以继续使用
假如进行拍摄录制 需要加入一个音频录制的权限 后续会写6.0动态权限的处理
欢迎大家来参考我的博客 如有错误 请评论指出