转载请指明出处@https://blog.csdn.net/qq_41912447
版权归博主所有
/**
**本文由自己搜集资料编程 如有错误请指明
**李紫洋著
*/
导引
相机成为了一个app必不可少的一个重要组成 它可以实现拍照,扫描二维码,视频聊天等 今天博主就来讲一下安卓开发相机 今天就带大家入门 更多资料请参考谷歌安卓开发官网android developers
-
简介
相机是属于系统资源 本身硬件就自带相机 你可以直接调用他来大大减少你的开发时间 像美颜相机这种app就需要自定义相机 现在大部分都有第三方平台 提供api供你使用 这同时大大减少你的代码量和团队创建的资金 第三方框架的使用大大减少你的工作量 同时他们的体系更加的完善 会减少bug的出现 -
设备兼容的问题需要大家考虑 像小米 oppo vivo 华为等设备都对硬件系统做了一定的处理 用来优化他们的设备性能性 所以请参考他们这些设备的官网开发资料进行一定的处理
一般的处理方法是在功能清单注册这些设备的权限 在代码中对这些设备进行一定的处理
- 同时6.0动态权限也需要大家考虑 让用户有一个高的体验 这的确是初学者的一个重大考验 你需要考虑所有会发生的情况
1软件安装提示权限
2
<1.用户同意继续使用app
<2.用户拒绝》》》1如果有必不可少的权限则退出 像相机app如果不给予相机权限只能强制退出软件了 这时就要提示用户如果拒绝则不再可以使用此软件
》》》2如果此权限并不正常影响你的app正常启动 则可以选择在使用你的功能时再次询问是否打开权限 这时可以使用intent信使让用户跳转到系统权限打开的类手动打开权限
》》为了避免多次对话框出现对用户体验造成影响 甚至会卸载app
用户可能随时关闭权限 对你的app进行管理 有可能你的app是一个相机软件 用户不能理解你使用电话权限的理由 你可以在用户拒绝的时候提示用户此权限的意义 不要在权限申请的时候进行处理 过多的提示会降低用户的体验
3对手机系统版本做出判断 低版本安卓设备权限处理与6.0版本以上处理方法不同 主要是6.0动态权限加入危险权限和安全权限 安全权限则只需要在功能清单注册即可 一般一组权限只需要注册一个即可 用户同意则会给予你这组权限的所有
**需要对设备本地数据库具有一定的理解 用来存储一些重要数据永久保存 将在软件卸载的时候消失 同时也要对网络传输数据库有一定的理解 了解json xml传输数据 json有一个好的工具是谷歌的gson 后续会编写相关的博客
-
6.0动态权限有一张步骤图 这里就不公告了 我用自己的理解把他用通俗的语言写出来了 请不要觉得文字太多 每个点都是需要考虑的
-
注 所有资源请以谷歌开发者文档官网为标准
好了废话不多说 现在来看我的一个简单示例代码
这里示例调用系统相机 和自定义相机两个部分扫描二维码关注公众号,回复: 5562157 查看本文章
调用系统相机
先来看布局文件来了解一下步骤
下面来示例这个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 有需要可以参考网络资料 对布局优化
来看一下布局代码
<?xml version="1.0" encoding="utf-8"?>
<!--relativeLayout相对布局
这个布局可以让你更好的精确控制控件位置 最好使用谷歌新推出的布局 他避免了嵌套布局 加快了加载速度-->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".Main2Activity">
<!---
使用surfaceView来当作相机的画面 注意要全屏设置 用来美化布局-->
<SurfaceView
android:id="@+id/surfaceView2"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentBottom="true" />
<!--这里嵌套了一个布局
android:gravity="center" 把布局里的控件放到布局中心 如 --*-- -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="80dp"
android:gravity="center"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true">
<!--图片按钮 用来设置拍摄图片-->
<ImageButton
android:id="@+id/imageButton2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@android:drawable/btn_radio" />
<!--图片控件进行预览-->
<ImageView
android:id="@+id/imageView3"
android:layout_width="80dp"
android:layout_height="80dp"
android:src="@color/colorAccent" />
</LinearLayout>
</RelativeLayout>
来看自定义相机的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;
}
}
}
/***
- 自定义相机的实现
- 首先使用surface布局视图来显示自定义的画面
- 得到画面缓存器 用来实现预览
- 路径图片名字 压缩的处理
- 使用surface回传
- 软件进程关闭的处理 释放相机和画面持有者
*/
最后别忘了在功能清单注册权限
- 注:6.0动态权限危险权限 需要特殊的处理
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.liziyang.dall">
<!-- 如果不必须使用自定义相机改为false required必须 -->
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.CAMERA2" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-feature android:name="android.hardware.camera.any" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".Main2Activity"
android:screenOrientation="landscape"
android:label="@string/title_activity_main2">
</activity>
</application>
</manifest>
注:自定义相机权限处理方法不同 读取内存卡 使用相机 为危险权限 请注意加以处理 这里不写 用户必须去打开相应的权限 才可以继续使用
假如进行拍摄录制 需要加入一个音频录制的权限 后续会写6.0动态权限的处理
<!-- 如果不必须使用自定义相机改为false required必须 -->
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.CAMERA2" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-feature android:name="android.hardware.camera.any" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
欢迎大家来参考我的博客 如有错误 请评论指出
同时我简书和博客有些不同步 关注我的技术主题,每天都有优质技术文章推送 简书扫一扫下方二维码即可关注: