Android 拍照并获取图片缩略图的实现

文章目录

    逻辑上是点击自己 Anroid APP 的拍照按钮,然后调用系统内置的相机应用,拍照后,将照片返回给我的 APP。

    实际上就是启动一个 activity,并接收其返回的结果数据,然后再处理。

    Android 官方推荐使用 AndroidX Activity 和 Fragment 中引入的 Activity Result API,以替代 startActivityForResult() 和 onActivityResult() API。

    记录一下实现。

    注意,这个方式获取到是图片的缩略图,并不是原始图片的尺寸,而且小非常多,几乎看不清的尺寸,如果要获取原始尺寸,需要参考Android 通过 ACTION_IMAGE_CAPTURE 拍照获取原尺寸的 bitmap 图片

    第一步:XML 模板

    <LinearLayout
        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:padding="10dp"
        android:orientation="vertical"
        >
       
       <ScrollView
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"
            >
          <ImageView
              android:id="@+id/imageViewResult"
              android:layout_width="match_parent"
              android:layout_height="wrap_content" />
       </ScrollView>
    
       <Button
            android:id="@+id/btnTakePicture"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@string/take_picture" />
    </LinearLayout>
    

    主要是两部分:

    • 按钮:点击触发拍照
    • ImageView: 展示拍摄的照片

    第二步:Kotlin 实现

    class SeedActivity : AppCompatActivity() {
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_seed)
    
            val launcher =
                registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
                    if (result.resultCode == Activity.RESULT_OK) {
                        handleCameraImage(result.data)
                    }
                }
    
            val btnTakePicture = findViewById<Button>(R.id.btnTakePicture)
            btnTakePicture.setOnClickListener {
                // intent to open camera app
                val cameraIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
                launcher.launch(cameraIntent)
            }
        }
    
        private fun handleCameraImage(intent: Intent?) {
            val bitmap = intent?.extras?.get("data") as Bitmap
            val imageView = findViewById<ImageView>(R.id.imageViewResult)
            imageView.setImageBitmap(bitmap)
        }
    }
    

    核心是:

    • registerForActivityResult: 注册回调逻辑,处理返回的图片
    • launch intent: 打开手机摄像头

    需要申请的权限

    我模拟器里测试,manifest 文件是否配置权限好像都不影响。需要在真机上测试一下。确实需要,可能之前测试有问题。

    <uses-feature android:name="android.hardware.camera"
    	android:required="true" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    
    • required 可以设置为 false,代表即便没有摄像头也可以用这个 app,但是需要在运行时检测是否有摄像头。
    • 要保存完整尺寸的照片,Android 相机应用会保存一张完整尺寸的照片,前提是您为该照片指定了一个文件来保存它。这就需要 WRITE_EXTERNAL_STORAGE 权限。

    废弃的方式:startActivityForResult

    网上很多实现都是用的 startActivityForResult,甚至《第一行代码 - Android》第三版里也是

    private fun dispatchTakePictureIntent() {
        val requestImageCapture = 1
        val takePictureIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
        try {
            startActivityForResult(takePictureIntent, requestImageCapture)
        } catch (e: ActivityNotFoundException) {
            // display error state to the user
        }
    }
    

    但是,在 Android Studio 里,会提示警告:

    ‘startActivityForResult(Intent!, Int): Unit’ is deprecated. Deprecated in Java

    即,startActivityForResult 已废弃。

    要了解 registerForActivityResult 的使用,详细可以参考我整理的两个例子

    是否需要用到 CameraX, Camera2, Camera

    从 Android 官方文档看,有三个摄像头相关类库 CameraX, Camera2, Camera。

    但是否一定要用那三个库,我看直接用 intent 也行。

    https://developer.android.com/training/camerax

    CameraX is a Jetpack support library, built to help you make camera app development easier. It provides a consistent and easy-to-use API surface that works across most Android devices, with backward-compatibility to Android 5.0 (API level 21).

    While CameraX leverages the capabilities of camera2, it uses a simpler approach that is lifecycle-aware and is based on use cases.

    而 Camera 已废弃,所以目前首选是 CameraX。

    但是文档看起来非常复杂,我猜测可能是要在自己 APP 内独立实现拍照功能才会用到吧。

    如果是未来要实现用摄像头实时分析图片,大概会用到吧。

    参考

    • 一个完整的流程介绍 https://intensecoder.com/android-open-camera-and-take-picture-in-kotlin
    • 官方的文档,用的不是拍照的案例 https://developer.android.com/training/basics/intents/result
    • 相对水的一个介绍 https://medium.com/swlh/intro-to-androidx-activity-result-apis-taking-a-picture-6013c3852c0b

    关于作者 🌱

    我是来自山东烟台的一名开发者,有感兴趣的话题,或者软件开发需求,欢迎加微信 zhongwei 聊聊,或者关注我的个人公众号“大象工具”, 查看更多联系方式