57. 【Android教程】相机:Camera

相机现在已经不仅仅是手机必备神器了,甚至相机的拍照质量已经是很多人买手机的首选条件了。而对于相机而言主要有两大功能:拍照片和拍视频。Android 为此两种方式:

  • 相机 intent
  • 相机 API
    本节我们就一起来看看相机的具体用法。

1. 打开 Camera 的两大方式

目前市面上绝大多数的 Android 手机是有前后两个摄像头,当然有部分特殊机型会存在其他的情况,本节主要针对双摄像头设备做解析。
在前面有提到过,通常使用相机有两大方式:“Intent”和“API”。最大的差别就是“Intent”是跳转到系统提供的相机页面,而使用“API”是封闭在自己的 App 中使用相机。我们可以通过“Intent”直接打开系统提供的相机 Activity,在用户拍摄完成之后系统 Activity 会通知我们拍摄结果,然后拿到拍摄的图片或者视频。而直接通过 API 就需要我们去开发一整套相机的控制页面,自行完成照片 / 视频的拍摄进而直接拿到用户拍摄的结果。

2. Camera 的基本用法

我们分别看看这两种打开方式的使用方法:

2.1 使用 Intent 打开

通过使用 MediaStore 类提供的两个 Intnet 常量,可以直接将相机操作托管给 Android 系统而无需创建 Camera 实例:

  • ACTION_IMAGE_CAPTURE:
    拍摄照片
  • ACTION_VIDEO_CAPTURE:
    拍摄视频

2.2 使用 API 打开

采用 API 打开会让整个相机程序都封闭在自己的 APP 中完成,这里需要明确几个概念:

  1. Camera 类
    使用 API 之前需要创建 Camera 实例,然后通过 API 来初始化 Camera 进而拿到实时拍摄的画面

  2. SurfaceView
    用来渲染视频实时画面的 View

采用 Intent 打开相机的方法非常简单,接下来我们全程使用 Camera API 来实现一下相机功能。

3. Camera 使用示例

使用 API 来拍照会相对比较麻烦一点,首先需要获取权限,那么对于 Android 6.0 版本以上的系统除了要在 Manifest 里面加入 Camera 权限之外,还需要动态获取权限,这个可能大家在用一些比较老的教程示例时会踩坑。
获取到权限就可以打开 Camera 了,然后拿到 Camera 实例设置一个 SurfaceView 来渲染预览页面,在用户需要拍照的时候调用 Camera 的takePicture()方法获取当前帧,最后保存输出到文件中或者在新的 Activity 中展示,这样就算完成了一次拍照闭环。

3.1 Camera 拍照逻辑

首先看看整个拍照的代码如下:


package com.emercy.myapplication;

import android.Manifest;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.hardware.Camera;
import android.os.Build;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

public class MainActivity extends Activity {

    public static final String CAMERA_PATH = "path";
    public static final String CAMERA_IMG = "img";

    private SurfaceView mSurfaceView;
    private Button mTakePhoto;
    private Camera mCamera = null;
    private SurfaceHolder.Callback mCallback = new SurfaceHolder.Callback() {
        @Override
        public void surfaceCreated(SurfaceHolder holder) {
            startPreview();
        }

        @Override
        public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

        }

        @Override
        public void surfaceDestroyed(SurfaceHolder holder) {
            stopPreview();
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        getPermission();

        bindViews();
    }

    /**
     * 获取权限
     */
    private void getPermission() {
        if (Build.VERSION.SDK_INT > 22) {
            if (checkSelfPermission(android.Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
                //先判断有没有权限 ,没有就在这里进行权限的申请
                requestPermissions(new String[]{Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE}, 100);
            } else {
                //说明已经获取到摄像头权限了
                Log.i("Camera", "已经获取了权限");
            }
        } else {
            //这个说明系统版本在6.0之下,不需要动态获取权限。
            Log.i("Camera", "这个说明系统版本在6.0之下,不需要动态获取权限。");
        }
    }

    private void bindViews() {
        mSurfaceView = (SurfaceView) findViewById(R.id.sfv_preview);
        mTakePhoto = (Button) findViewById(R.id.btn_take);
        mSurfaceView.getHolder().addCallback(mCallback);

        mTakePhoto.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mCamera.takePicture(null, null, new Camera.PictureCallback() {
                    @Override
                    public void onPictureTaken(byte[] data, Camera camera) {
                        String path;
                        if (TextUtils.isEmpty(path = savePhoto(data))) {
                            Intent it = new Intent(MainActivity.this, PreviewActivity.class);
                            it.putExtra(CAMERA_PATH, path);
                            startActivity(it);
                        } else {
                            Toast.makeText(MainActivity.this, "拍照失败", Toast.LENGTH_SHORT).show();
                        }
                    }
                });
            }
        });
    }

    private String savePhoto(byte[] bytes) {
        try {
            File file = File.createTempFile(CAMERA_IMG, "");
            FileOutputStream fos = new FileOutputStream(file);
            fos.write(bytes);
            fos.flush();
            fos.close();
            return file.getAbsolutePath();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return "";
    }

    private void startPreview() {
        mCamera = Camera.open();
        try {
            mCamera.setPreviewDisplay(mSurfaceView.getHolder());
            mCamera.setDisplayOrientation(90);   // 让相机旋转90度
            mCamera.startPreview();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void stopPreview() {
        mCamera.stopPreview();
        mCamera.release();
        mCamera = null;
    }
}

里面涉及到的几个新概念大家需要注意,然后记得在SurfaceHolder.Callback()surfaceCreated()中启动预览,在surfaceDestroyed()方法中要停止预览,其他的基本上按照步骤来不会有什么问题。

3.2 相机布局

目前市面上有很多相机 App,各种布局千变万化,大家完全可以按照自己的喜好来进行设计。这里只做一个拍照预览和拍照按钮。

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <SurfaceView
        android:id="@+id/sfv_preview"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <Button
        android:id="@+id/btn_take"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|center"
        android:text="拍照" />

</FrameLayout>

3.3 照片回看页面

点击“拍照” Button 获取到当前画面之后,我们可以就可以按照自己的逻辑对图片进行操作了。比如可以通过 Http 上传、通过 Socket 发送给其他设备、保存到本地、或者传递给其他 App 等。本例子中将图片传递给另一个 Activity 专门用于查看图片,下面是 PhotoActivity 的代码:

package com.emercy.myapplication;

import android.app.Activity;
import android.net.Uri;
import android.os.Bundle;
import android.widget.ImageView;

import java.io.File;

import static com.emercy.myapplication.MainActivity.CAMERA_PATH;

public class PhotoActivity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ImageView img = new ImageView(this);
        String path = getIntent().getStringExtra(CAMERA_PATH);
        if (path != null) {
            img.setImageURI(Uri.fromFile(new File(path)));
        }
        setContentView(img);
    }
}

通过 Intent 接收图片路径,然后展示在 ImageView 上,需要注意的是PhotoActivity 中是直接将 ImageView 作为参数直接设置给了 SetContentView(),这样相当于布局文件中只有一个 ImageView 的写法。

最后别忘了在 Manifest 当中添加权限:

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

编译运行效果如下:

点击拍照进入 PhotoActivity,展示的就是我们拍照的画面。

4. 小结

本节学习了一个手机上必不可少的设备,通过相机我们可以拍摄照片或者视频,Android 系统提供了两大打开方式:Intent 调用系统相机 Activitiy或者用 Camera API 自行实现拍照功能。第一种非常简单,将所有的相机操作都托管给系统,我们只关心最终用户拍摄的结果;而第二种就需要我们自己初始化 Camera 对象从而实现拍摄,在实际开发中如果你的功能是和拍摄强相关,需要一些定制化的拍摄体验,那么一定要使用第二种方式来自己实现 Camera,但是如果你只是一个简单的拍照获取图片,那么第一种会让你事半功倍。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/617411.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Windows快速部署DCNv4(成功版)

文章目录 一、介绍二、编译DCNv42.1 下载源码2.2 编译DCNv4 三、报错提示3.1 Cuda is not available3.2 需要Microsoft Visual C 14.0 一、介绍 论文链接&#xff1a;[https://arxiv.org/pdf/2401.06197.pdf] (https://arxiv.org/pdf/2401.06197.pdf)   在这篇文章中介绍了一…

8种区块链开发者必须知道的顶级编程语言!

我来问你一个问题&#xff1a;请说出一种技术&#xff0c;它以去中心化、不可篡改和透明性等核心特征席卷了全球。 这个问题的答案是&#xff0c;当然是区块链&#xff0c;它在近些年进入大家的视野并颠覆了工商业&#xff0c;没有任何其他技术能够做到这一点。 预计从2020年…

通过python实现Google的精准搜索

问题背景&#xff1a; 我想通过Google或者其他网站通过精准搜索确认该产品是否存在&#xff0c;但是即使该产品不存在Google也会返回一些相关的url链接&#xff0c;现在想通过python实现搜索结果的精准匹配以确认该产品是否为正确的名称【可以通过google搜索到&#xff0c;如果…

Git的安装和配置

一、Git的介绍 代码的一套托管工具&#xff0c;它分为两个仓库&#xff0c;首先将你写的代码提交到本地仓库&#xff0c;这个时候只有你可以看&#xff0c;和你一起开发的同事看不到。将本地仓库的代码推到远程仓库&#xff08;githab、gitee、gitlab等之一&#xff09;&#…

斩获 32k 星!号称下一代知识库工具开源了!

AFFiNE&#xff08;发音为 [ə‘fain]&#xff09;是一个下一代知识库平台&#xff0c;它将规划、整理和创造整合在一个空间。AFFiNE致力于提供一个隐私优先、开源、可定制且即用性强的解决方案&#xff0c;作为 Notion 、Airtable、Miro 的整合“替代品”。 Notion 、Airtable…

数据结构-二叉树-红黑树

一、红黑树的概念 红黑树是一种二叉搜索树&#xff0c;但在每个节点上增加一个存储位表示节点的颜色&#xff0c;可以是Red或者BLACK&#xff0c;通过对任何一条从根到叶子的路径上各个节点着色方式的限制&#xff0c;红黑树确保没有一条路径会比其他路径长出两倍&#xff0c;…

Java | Spring框架 | AOP代理机制

大家好&#xff0c;我是程序员影子&#xff0c;一名AI编程深耕者&#xff0c;点击左上角头像了解我的详细信息。 今天来聊一聊关于Java中的Spring AOP代理机制中的JDK动态代理与CGLIB。 一、JDK动态代理 JDK动态代理是Spring AOP默认使用的代理机制。它基于Java反射机制&…

力扣 5-11

704. 二分查找 给定一个 n 个元素有序的&#xff08;升序&#xff09;整型数组 nums 和一个目标值 target &#xff0c;写一个函数搜索 nums 中的 target&#xff0c;如果目标值存在返回下标&#xff0c;否则返回 -1。 这道题目的前提是数组为有序数组&#xff0c;同时题目还强…

边缘计算:数据处理的新范式

在不断发展的科技领域中&#xff0c;我们对数据的处理和管理方式正经历着一场范式转变。边缘计算的兴起正在改变传统的数据处理方法。本文将深入探讨边缘计算的涌现&#xff0c;探讨其对数据处理的变革性影响、带来的优势以及对各个行业的影响。 探索边缘计算 边缘计算的核心理…

Docker搭建ctfd平台

安装docker和docker-compose &#xff08;1&#xff09;安装docker&#xff1a; curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun&#xff08;2&#xff09;安装 Docker Compose&#xff1a; yum install docker-compose安装失败参考下面文章 https:/…

消费金融平台公司如何做大做强自营产品

本文来自于2019年的某次内部分享沟通会&#xff0c;部分敏感内容已做删减。

(2024,KAN,MLP,可训练激活函数,样条函数,分层函数)Kolmogorov–Arnold 网络

KAN: Kolmogorov–Arnold Networks 公和众和号&#xff1a;EDPJ&#xff08;进 Q 交流群&#xff1a;922230617 或加 VX&#xff1a;CV_EDPJ 进 V 交流群&#xff09; 目录 0. 摘要 1. 简介 2. KAN 2.1 KA 表示定理 2.2 KAN 架构 2.3 KAN 的逼近能力和缩放定律 2.4 对于…

mysql的存储结构

一个表就是一个ibd文件 .ibd文件大小取决于数据和索引&#xff0c;在5.7之后才会为每个表生成一个独立表空间即一个ibd文件&#xff0c;在此之前&#xff0c;所有表默认下都会存储在“系统表空间”&#xff08;共享表空间&#xff09;&#xff0c;所有表都在一个ibd文件。 inn…

Tomcat7+ 弱口令 后台getshell漏洞

1 漏洞背景 Tomcat 是一个流行的开源Web应用服务器&#xff0c;用于部署和运行Java Web应用程序。Tomcat 7 版本中存在一个安全隐患&#xff0c;即默认的管理员密码可能较弱或者未被修改&#xff0c;攻击者可以利用这一漏洞登录到Tomcat的管理后台&#xff0c;并上传恶意的WAR…

Ps 滤镜:粉笔和炭笔

Ps菜单&#xff1a;滤镜/滤镜库/素描/粉笔和炭笔 Filter Gallery/Sketch/Chalk & Charcoal 粉笔和炭笔 Chalk & Charcoal滤镜可以模拟传统的粉笔和炭笔画风格&#xff0c;通过特定的纹理和线条重绘图像的高光、中间色调和阴影区域。此滤镜非常适合于为数字图像添加手绘…

SAP-CentralFinance - 学习心得2

过账总账中的交易 业务示例 创建大量日记账分录是会计日常工作的一部分。在SAP,会计可以使用不同的输入屏幕。使用所有方法,总账科目过账会自动列在损益表报表中(如果财务报表版本中包含科目)。查询已过账科目时还可显示对应的过账。 贵公司计划通过企业基金增加资本。在…

我的全新官网

科技语者-探索未来的语言和沟通 (chgskj.cn) 另外我还开放了一个网站科技语者-介绍页 (null.fit)

RAG讲解

现有的LLM已经具备了理解、生成、逻辑和记忆能力&#xff0c;RAG(Retrieval Augmented Generation)则是为其套上外挂&#xff0c;使LLM能够访问训练数据来源之外的权威知识库&#xff0c;并生成领域特定的内容&#xff0c;而无须重新训练模型。 RAG的优势 经济高效&#xff1a…

java基础知识点总结2024版(8万字超详细整理)

java基础知识点总结2024版&#xff08;超详细整理&#xff09; 这里写目录标题 java基础知识点总结2024版&#xff08;超详细整理&#xff09;java语言的特点1.简单性2.面向对象3.分布式4.健壮性5.安全性6.体系结构中立7.可移植性8.解释性9.多线程10.动态性 初识java中的main方…

【全开源】Java同城预约月嫂服务上门服务本地服务源码APP+小程序+公众号+H5

特色功能&#xff1a; 预约服务&#xff1a;用户可以通过小程序在线预约月嫂服务&#xff0c;选择服务时间、服务类型、月嫂等信息&#xff0c;实现方便快捷的预约流程。在线咨询&#xff1a;用户可以通过小程序向月嫂或服务机构咨询相关问题&#xff0c;获得专业的解答和建议…