Android 从零开发一个简易的相机App

这篇具有很好参考价值的文章主要介绍了Android 从零开发一个简易的相机App。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

本文介绍了实现一个简易Android相机App过程中,遇到的一些问题,对Camera API的选型、通知相册更新、跳转相册、左右滑动界面切换拍照/录像,相机切换时候的高斯模糊虚化效果、相机切换的3D效果做了说明。

1. 技术选型

Android调用相机可以使用Camera1Camera2CameraX

1.1 Camera1

Camera1API相对复杂,且GoogleAndroid 5.0的时候,就已经停止维护了。
但由于种种原因,有时候不得不使用Camera1的API。
如果必须要使用,建议参照 Camera1Java 这个Github库,写的还挺详细的。
同时,还有Camera1的官方文档 : Camera1 API

1.2 Camera2

Android5.0以上支持Camera2的API,如果使用Camera2,可以看我的博客 :
十分钟实现 Android Camera2 相机预览
十分钟实现 Android Camera2 相机拍照
十分钟实现 Android Camera2 视频录制
还有官方的文档 : Android Developers | Camera2 overview

也可以直接使用Github上的一个封装库 CameraView,使用起来比较简单,
它支持使用Camera1Camera2作为引擎,进行图片的拍摄和视频的捕捉。

<com.otaliastudios.cameraview.CameraView
    android:id="@+id/camera"
    android:keepScreenOn="true"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    CameraView camera = findViewById(R.id.camera);
    camera.setLifecycleOwner(this);
}

具体详见 官方文档 CameraView官方文档

Android5.0以上支持Camera2,但 Android 5.0 及更高版本的设备可能并不支持所有相机 API2 功能。
不是所有Android设备都支持完整的Camera2功能, 现在都2022了, Camera2出来都有8年左右了, Android车机上还有在使用低版本HAL的, 就会导致Camera2一些高级功能都没法使用。详见 Android Camera2 综述

1.3 Camera X

CameraX 是 Jetpack 的新增库。基于Camera2开发,向上提供更简洁的API接口,向下处理了各种厂商机型的兼容性问题,有助于在众多设备上打造一致的开发者体验。

Camera X 用起来也很简单

<androidx.camera.view.PreviewView
    android:id="@+id/previewView"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />
val preview = Preview.Builder().build()
val viewFinder: PreviewView = findViewById(R.id.previewView)

// The use case is bound to an Android Lifecycle with the following code
val camera = cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview)

// PreviewView creates a surface provider and is the recommended provider
preview.setSurfaceProvider(viewFinder.getSurfaceProvider())

具体详见我的另一篇博客 Android 使用CameraX实现预览/拍照/录制视频/图片分析/对焦/切换摄像头等操作

这里,我选用了CameraX来进行相机的开发。

1.4 扩展知识 : Android Camera HAL

HAL(Hardware Abstraction Layer),即Android 的Camera硬件抽象层。
HAL 位于相机驱动程序和更高级别的 Android 框架之间,它定义了必须实现的接口,以便应用可以正确地操作相机硬件。
HAL 可定义一个标准接口以供硬件供应商实现,可让Android忽略较低级别的驱动程序实现。HAL实现通常会内置在共享库模块(.so)中。

Android Developers | HAL介绍

接下来来介绍下开发简易相机App的时候,遇到的问题

2. 通知相册更新

当我们拍摄了一张图片之后,如果不通知系统更新相册,那么在相册中是找不到这张图片的。
所以当我们拍摄了图片后,必须要通知系统,让相册更新这张图片。

首先,新建一个FileUtils类,将需要保存的图片存储在该路径下

object FileUtils {
    val FILENAME = "yyyy-MM-dd-HH-mm-ss-SSS"
    val PHOTO_EXTENSION = ".jpg"

    /** Helper function used to create a timestamped file */
    fun createFile(baseFolder: File, format: String, extension: String) =
        File(
            baseFolder, SimpleDateFormat(format, Locale.US)
                .format(System.currentTimeMillis()) + extension
        )

    /** Use external media if it is available, our app's file directory otherwise */
    fun getOutputDirectory(context: Context): File {
        val appContext = context.applicationContext
        val mediaDir = context.externalMediaDirs.firstOrNull()?.let {
            File(it, appContext.resources.getString(R.string.app_name)).apply { mkdirs() }
        }
        return if (mediaDir != null && mediaDir.exists())
            mediaDir else appContext.filesDir
    }

    fun getMoviesDirectory(context: Context): File {
        var externalDirectory = Environment.getExternalStorageDirectory()
        return File(externalDirectory, "Movies")
    }
}

然后保存图片后

//这里是异步线程
File outputDirectory = FileUtils.INSTANCE.getOutputDirectory(context);
File myCaptureFile = FileUtils.INSTANCE.createFile(outputDirectory, FileUtils.INSTANCE.getFILENAME(), FileUtils.INSTANCE.getPHOTO_EXTENSION());
//写入文件
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(myCaptureFile));
bm.compress(Bitmap.CompressFormat.JPEG, 100, bos);
bos.flush();
bos.close();

最后,通知系统更新相册

//把图片保存后声明这个广播事件通知系统相册有新图片到来
Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
Uri uri = Uri.fromFile(myCaptureFile);
Log.d(TAG, "Photo capture succeeded:" + myCaptureFile.getPath());
intent.setData(uri);
context.sendBroadcast(intent);

需要注意的是,这里没有启用分区存储,如果要适配分区存储的话,请看这几篇文章
Android 10 分区存储完全解析
Android Developer : 访问共享存储空间中的媒体文件
支持 Android 12,全版本保存图片到相册方案

3. 跳到相册

相机里还有一个跳转到全部相册的功能
首先,是去网上找到了一个跳转到相册的方法

3.1 使用Intent.ACTION_PICK

val intent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
if (isVideo){
    intent.type = "video/*"
}else{
    intent.type = "image/*"
}
startActivity(intent)

详见 android如何调系统用相册并处理返回

但是这其实是一个选择图片的Intent,并不是跳转到真的相册。
后来想到,可以通过隐式Intent跳转到系统相册的App,这里以华为的相册为例

3.2 反编译获得隐式intent

3.2.1 查找包名并导出

首先,我们需要先查找到华为相册的包名,导出华为相册app

// 第一步 : 查看包名
adb shell am monitor

//第二步 : 查看该包名的存放路径
adb shell pm path com.huawei.photos

//第三步 : 导出到电脑上
adb pull 路径地址

具体详见 用adb导出某个app

3.2.2 使用dex2jar进行反编译

我们先解压apk,获取到dex文件,然后使用dex2jar进行反编译
需要注意的是,需要把dex版本修改为036,高版本已不支持反编译了。

d2j-dex2jar classes.dex

反编译成功后,我们使用jd-gui进行打开
同时,通过apktool我们可以反编译得到AndroidManifest.xml
查看Manifest可以得知,相册app的入口是GalleryMain

<activity android:configChanges="keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize" android:label="@string/app_name" android:launchMode="singleTop" android:name="com.huawei.gallery.app.GalleryMain" android:theme="@style/SplashTheme" android:windowSoftInputMode="adjustPan">
    <intent-filter>
        <action android:name="com.huawei.gallery.action.ACCESS_HWGALLERY"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <data android:host="photosapp" android:path="/oneKeyDirect" android:scheme="huaweischeme"/>
    </intent-filter>
    <intent-filter>
        <action android:name="com.huawei.gallery.action.ACCESS_HWGALLERY"/>
        <category android:name="android.intent.category.DEFAULT"/>
    </intent-filter>
    <intent-filter>
        <action android:name="hwgallery.intent.action.GET_PHOTOSHARE_CONTENT"/>
        <category android:name="android.intent.category.DEFAULT"/>
    </intent-filter>
    <intent-filter>
        <action android:name="android.intent.action.MAIN"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <category android:name="android.intent.category.LAUNCHER"/>
        <category android:name="android.intent.category.APP_GALLERY"/>
    </intent-filter>
    <meta-data android:name="android.app.shortcuts" android:resource="@xml/shortcuts"/>
</activity>

android开发简易app,Android日常经验,android,camera,相机,开发,跳转相册
可以看到,当tab为1的时候,会切换到albums这个tab,这个就是我们想要的。

最终的itnent为

if (RomUtils.isHuawei()) {
	 val intent = Intent()
	 intent.setClassName("com.huawei.photos", "com.huawei.gallery.app.GalleryMain")
	 intent.putExtra("tab", 1)
	 startActivity(intent)
 }

4. 左右滑动界面切换拍照/录像

一般相机左右滑动之后可以切换拍照/录像功能,我们对根view进行touchEvent监听即可

private var mPosX = 0F
private var mPosY = 0F
private var mCurPosX = 0F
private var mCurPosY = 0F

binding.rootView.setOnTouchListener { v, event ->
	when (event.action) {
		MotionEvent.ACTION_DOWN -> {
			mPosX = event.x
			mPosY = event.y
		}
		MotionEvent.ACTION_MOVE -> {
			mCurPosX = event.x
			mCurPosY = event.y
		}
		MotionEvent.ACTION_UP ->
			if (mCurPosX - mPosX > 0
				&& Math.abs(mCurPosX - mPosX) > 120
			) {
				//往左滑
			} else if (mCurPosX - mPosX < 0
				&& Math.abs(mCurPosX - mPosX) > 120
			) {
				//往右滑
			}
	}
	true
}

5. 相机切换时候虚化,实现高斯模糊效果

我这里使用到了AndroidUtilCode里的工具类ImageUtils,使用ImageUtils.fastBlur来进行高斯模糊效果的处理

 val originBitmap = binding.previewView.bitmap
 val blurBitmap = ImageUtils.fastBlur(originBitmap, 0.25F, 25F)
 binding.imgBlur.setImageBitmap(blurBitmap)

这里需要注意的有两点

  1. 用户点击切换后,需要同步先处理好高斯模糊的效果,再进行摄像头的切换,这个处理时间大概在200ms,对于用户几乎是无感知的
  2. CameraX 默认使用的implementationModeperformance,我是改为compatible取到的图像角度才是正常的,关于implementationMode可以看 CameraX 实现预览文档

效果如下
android开发简易app,Android日常经验,android,camera,相机,开发,跳转相册

6. 相机切换 3D翻转效果

市面上主流的相机,切换前后摄像头的时候,会有3D翻转的效果。
网上找到了这篇文章 手把手教你实现Android开发中的3D卡片翻转效果!,自己按照文中步骤实现了一下

Rotate3dAnimation.kt

/**
 * An animation that rotates the view on the Y axis between two specified angles.
 * This animation also adds a translation on the Z axis (depth) to improve the effect.
 */
public class Rotate3dAnimation extends Animation {
    private final float mFromDegrees;
    private final float mToDegrees;
    private final float mCenterX;
    private final float mCenterY;
    private final float mDepthZ;
    private final boolean mReverse;
    private Camera mCamera;

    /**
     * Creates a new 3D rotation on the Y axis. The rotation is defined by its
     * start angle and its end angle. Both angles are in degrees. The rotation
     * is performed around a center point on the 2D space, definied by a pair
     * of X and Y coordinates, called centerX and centerY. When the animation
     * starts, a translation on the Z axis (depth) is performed. The length
     * of the translation can be specified, as well as whether the translation
     * should be reversed in time.
     *
     * @param fromDegrees the start angle of the 3D rotation //起始角度
     * @param toDegrees the end angle of the 3D rotation //结束角度
     * @param centerX the X center of the 3D rotation //x中轴线
     * @param centerY the Y center of the 3D rotation //y中轴线
     * @param reverse true if the translation should be reversed, false otherwise//是否反转
     */
    public Rotate3dAnimation(float fromDegrees, float toDegrees,
            float centerX, float centerY, float depthZ, boolean reverse) {
        mFromDegrees = fromDegrees;
        mToDegrees = toDegrees;
        mCenterX = centerX;
        mCenterY = centerY;
        mDepthZ = depthZ;//Z轴移动的距离,这个来影响视觉效果,可以解决flip animation那个给人看似放大的效果
        mReverse = reverse;
    }

    @Override
    public void initialize(int width, int height, int parentWidth, int parentHeight) {
        super.initialize(width, height, parentWidth, parentHeight);
        mCamera = new Camera();
    }

    @Override
    protected void applyTransformation(float interpolatedTime, Transformation t) {
        final float fromDegrees = mFromDegrees;
        float degrees = fromDegrees + ((mToDegrees - fromDegrees) * interpolatedTime);

        final float centerX = mCenterX;
        final float centerY = mCenterY;
        final Camera camera = mCamera;

        final Matrix matrix = t.getMatrix();

        Log.i("interpolatedTime", interpolatedTime+"");
        camera.save();
        if (mReverse) {
            camera.translate(0.0f, 0.0f, mDepthZ * interpolatedTime);
        } else {
            camera.translate(0.0f, 0.0f, mDepthZ * (1.0f - interpolatedTime));
        }
        camera.rotateX(degrees);
        camera.getMatrix(matrix);
        camera.restore();

        matrix.preTranslate(-centerX, -centerY);
        matrix.postTranslate(centerX, centerY);
    }
}

Rotate3dManager.kt

class Rotate3dManager(val photo1: View) {
    private var centerX = 0
    private var centerY = 0
    private val depthZ = 400
    private val duration = 300
    private var closeAnimation: Rotate3dAnimation? = null

    /**
     * 卡牌文本介绍关闭效果:旋转角度与打开时逆行即可
     */
    private fun initCloseAnim() {
        closeAnimation = Rotate3dAnimation(
            360F, 270F, centerX.toFloat(), centerY.toFloat(),
            depthZ.toFloat(), true
        )
        closeAnimation!!.setDuration(duration.toLong())
        closeAnimation!!.setFillAfter(true)
        closeAnimation!!.setInterpolator(AccelerateInterpolator())
        closeAnimation!!.setAnimationListener(object : Animation.AnimationListener {
            override fun onAnimationStart(animation: Animation) {

            }

            override fun onAnimationRepeat(animation: Animation) {}
            override fun onAnimationEnd(animation: Animation) {
                val rotateAnimation =
                    Rotate3dAnimation(
                        90F, 0F,
                        centerX.toFloat(), centerY.toFloat(), depthZ.toFloat(), false
                    )
                rotateAnimation.duration = duration.toLong()
                rotateAnimation.fillAfter = true
                rotateAnimation.interpolator = DecelerateInterpolator()
                photo1!!.startAnimation(rotateAnimation)
            }
        })
    }



    fun operate() {
		if (photo1.width <= 0) {
            photo1.post {
                operate()
            }
            return
        }

        //以旋转对象的中心点为旋转中心点,这里主要不要再onCreate方法中获取,因为视图初始绘制时,获取的宽高为0
        centerX = photo1.width / 2
        centerY = photo1.height / 2

        if (closeAnimation == null) {
            initCloseAnim()
        }

        //用作判断当前点击事件发生时动画是否正在执行
        if (closeAnimation!!.hasStarted() && !closeAnimation!!.hasEnded()) {
            return
        }

        photo1!!.startAnimation(closeAnimation)
    }
}

然后,对想要翻转的View进行使用即可

val rotate3dManager = Rotate3dManager(targetView)
rotate3dManager.operate()

效果如下
android开发简易app,Android日常经验,android,camera,相机,开发,跳转相册

github地址 : DialogFlipTest文章来源地址https://www.toymoban.com/news/detail-524402.html

到了这里,关于Android 从零开发一个简易的相机App的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Android Studio开发实战:从零基础到App上线

    第1章  Android开发环境搭建 1     1.1  Android开发简介 1         1.1.1  Android的发展历程 1         1.1.2  Android Studio的发展历程 2     1.2  搭建Android Studio开发环境 2         1.2.1  计算机配置要求 2         1.2.2  安装Android Studio 3         1.2.3  下载Android的S

    2024年02月20日
    浏览(36)
  • 开发一个Android应用:从零到一的实践指南

    在这篇博文中,我们将逐步探讨如何从头开始构建一个Android应用。我们将从最基本的环境搭建开始,然后深入讨论组件、布局和其他核心概念。在完成整个过程后,你将会掌握一个简单但完整的Android应用开发流程。让我们开始吧! 准备开发环境 创建项目 理解项目结构 设计

    2024年02月08日
    浏览(61)
  • 《Android Studio开发实战 从零基础到App上线(第3版)》资源下载和内容勘误

    下面是《Android Studio开发实战 从零基础到App上线(第3版)》一书用到的工具和代码资源: 1、本书使用的Android Studio版本为Android Studio Dolphin(小海豚版本),最新的安装包可前往Android官网页面下载。 2、本书使用的Android NDK版本为r23b,最新的安装包可前往Android官网页面下载。

    2023年04月19日
    浏览(105)
  • Android Studio App开发之使用相机拍摄照片和从相册中选取图片(附源码 超详细必看)

    运行有问题或需要源码请点赞关注收藏后评论区留言~~~ 对于手机拍照的App开发而言,有两种实现方式 1:通过 camera工具联合表面视图SurfaceView自行规划编码细节 2:借助系统相机自动拍照,考虑到多数场景对图片并没有特殊要求,所以使用系统相机更加方便快捷  效果如下 点

    2024年02月11日
    浏览(43)
  • ARabbit:一个快速开发Android App的框架

    5.主要版本修订日志 6.引用的第三方库 7.SDK引用的开源框架 8.使用ARabbit的项目 欢迎加入Android开发交流QQ群: 1.简介 ============================================================== 提供App开发的接口,使开发者只关心App的业务实现,不用再关心具体功能实现,比如网络请求、图片加载、对话

    2024年04月25日
    浏览(24)
  • android开发:用IDEA建立你的第一个APP

            主要是记录一下各种小坑。         IDEA目前是第一流行的java开发工具,同时也支持android开发,可以替代安卓官方的andriod studio,不过仍然要依赖android sdk。         本例指导你完成第一个app,需要一台Windows PC和一部android手机。 目录 一、下载安装IDEA 二、新建and

    2024年02月04日
    浏览(43)
  • android studio简易app实例

    以下是一个使用Android Studio创建的简易App实例。这个App包括一个主页面,一个关于页面和一个设置页面。 步骤1:创建新的Android项目 打开Android Studio,点击\\\"File\\\" - \\\"New\\\" - \\\"New Project\\\"。在新的对话框中,选择\\\"Empty Activity\\\",然后点击\\\"Next\\\"。 在接下来的页面中,填写你的应用名称,应

    2024年04月14日
    浏览(26)
  • 制作一个简易的计算器app

    github项目地址:https://github.com/13008451162/AndroidMoblieCalculator 笔者的Ui制作的制作的比较麻烦仅供参考,在这里使用了多个LinearLayout对屏幕进行了划分。不建议大家这样做最好使用GridLayout会更加快捷简单 笔者大致划分是这样的: 使用了四个大框,在第四个大框里面有多个小框

    2024年02月15日
    浏览(35)
  • 使用Android studio完成简易智能家居APP的制作(含源码工程包)

     (填下坑,把我之前答应大家的完整软件代码翻出来,供大家一起学习) 可以看看实现效果先:   智能家居APP展示视频(含源码)_哔哩哔哩_bilibili 目录 项目说明 功能介绍 系统功能需求 具体实现的软件功能  1、地图定位 2、蓝牙按钮 3、远端控制  核心代码展示说明 蓝牙

    2024年02月06日
    浏览(32)
  • APP自定义身份证相机(Android +iOS)

    基本上同时兼容安卓和苹果的插件都需要付费,这里我找了2个好用的免费插件 1.仅支持安卓: 自定义身份证相机(支持蒙版自定义),内置蒙版,照片预览,身份证裁剪 - DCloud 插件市场、 2.支持iOS(已测),支持Android(未测,应该也可以用):   自定义相机 - DCloud 插件市场 第一

    2024年04月28日
    浏览(28)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包