Android开发(Jetpack) 学习CameraX 自定义相机实现

追求适度,才能走向成功;人在顶峰,迈步就是下坡;身在低谷,抬足既是登高;弦,绷得太紧会断;人,思虑过度会疯;水至清无鱼,人至真无友,山至高无树;适度,不是中庸,而是一种明智的生活态度。

导读:本篇文章讲解 Android开发(Jetpack) 学习CameraX 自定义相机实现,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文

目录

一、CameraX和Camera2

二、CameraX的引入

三、自定义拍照

四、自定义视频录制

五、XML 配置


一、CameraX和Camera2

CameraX 是一个 Jetpack 库,旨在帮助您更轻松地开发相机应用。 对于新应用,Android官方建议从 CameraX 开始。它提供一致且易于使用的 API,适用于绝大多数 Android 设备,并向后兼容 Android 5.0(API 级别 21)。

Camera2是一个底层相机的实现,CameraX实则也是基于Camera2,所以Camera2非常底层,如果需要对纹理等操作,需要使用Camera2来实现。

Camera2出自jetpack库,它比自己粗糙封装的Camera2更加健壮,而且它的兼容性由官方维护更省心。

二、CameraX的引入

废话不多说,直接上依赖;
依赖引入:

// //使用camera2实现的CameraX核心库
def camerax_version = "1.1.0"
// 以下行是可选的,因为camera-camera2间接包含了核心库
implementation "androidx.camera:camera-core:${camerax_version}"
implementation "androidx.camera:camera-camera2:${camerax_version}"
// 如果您想另外使用CameraX生命周期库
implementation "androidx.camera:camera-lifecycle:${camerax_version}"
// 如果您想额外使用CameraX VideoCapture库
implementation "androidx.camera:camera-video:${camerax_version}"
// 如果您想另外使用CameraX View类
implementation "androidx.camera:camera-view:${camerax_version}"
// 如果您想额外添加CameraX ML Kit Vision Integration
implementation "androidx.camera:camera-mlkit-vision:${camerax_version}"
// 如果您想另外使用CameraX Extensions库
implementation "androidx.camera:camera-extensions:${camerax_version}"

三、自定义拍照

在实际开发中经常可能碰到需要拍照的需求,但调用系统的照相机无法实现自己的自定义功能,自己封装Camera2有些麻烦,且没有处理好容易造成内存泄漏和闪退。使用CameraX虽然它属于再封装不够底层,但只是自定义相机的界面完全足够了。

以下是使用kotlin实现的实例代码,代码还有待优化。

/**
 * @author Ym
 * @version 1.0
 * @createTime 
 * @describe  自定义相机 拍照
 */
class CamareXActivity : AppCompatActivity(), View.OnClickListener {
    companion object {

        fun createIntent(mContext: Context): Intent {
            return Intent(mContext, CamareXActivity::class.java)
        }
    }

    private lateinit var imageCapture: ImageCapture
    private lateinit var cameraProviderFuture: ListenableFuture<ProcessCameraProvider>
    private var cameraProvider: ProcessCameraProvider? = null
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_camerax)
        initCameraX()
        tv_btn.setOnClickListener(this)
    }

    private fun initCameraX() {
        imageCapture = ImageCapture.Builder()
            .build()
        cameraProviderFuture = ProcessCameraProvider.getInstance(this)
        cameraProviderFuture.addListener(Runnable {
            cameraProvider = cameraProviderFuture.get()
            cameraProvider?.let { bindPreview(it) }
        }, ContextCompat.getMainExecutor(this))
    }

    fun bindPreview(cameraProvider: ProcessCameraProvider) {
        var preview: Preview = Preview.Builder()
            .build()

        var cameraSelector: CameraSelector = CameraSelector.Builder()
            .requireLensFacing(CameraSelector.LENS_FACING_BACK)
            .build()

        preview.setSurfaceProvider(pvCameraX.getSurfaceProvider())
        var camera = cameraProvider.bindToLifecycle(this, cameraSelector, preview, imageCapture)
    }

    private var filepath: String? = null

    private fun takePicture() {
        if (filepath == null) {
            filepath = this.getExternalFilesDir(null)?.absolutePath + File.separator +
                    "camerax_image"
        }
        var imagePath = filepath + File.separator+ "img_${Calendar.getInstance().timeInMillis}.jpeg"
        val imageFile = File(imagePath)
        if (!imageFile.parentFile.exists()){
            imageFile.parentFile.mkdirs()
        }
        imageFile.createNewFile()
        Log.e("拍照地址", imagePath)
        val outputFileOptions = ImageCapture.OutputFileOptions.Builder(File(imagePath)).build()
        imageCapture.takePicture(outputFileOptions, ThreadUtil.getThread(),
            object : ImageCapture.OnImageSavedCallback {
                override fun onError(error: ImageCaptureException) {
                    Log.e("拍照失败", error.toString())
                }

                override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {
                    val imagePath = outputFileResults.savedUri
                    runOnUiThread {
                        Toast.makeText(
                            this@CamareXActivity, imagePath.toString(), Toast.LENGTH_SHORT
                        ).show()
                        Log.i("拍照结果", imagePath.toString())
                        Glide.with(this@CamareXActivity).load(imagePath).into(iv_image)
                    }
                }
            })
    }

    override fun onClick(p0: View?) {
        takePicture()
    }
}

四、自定义视频录制

自定义视频录像虽然实现了视频录像但它的缺陷如同它的出生是一个Jetpack 库,如果需要实现比较底层的图像处理,比较困难,如果只是实现录像和自定义界面CameraX绰绰有余。

以下是使用kotlin实现的实例代码,代码还有待优化。

/**
 * @author Ym
 * @version 1.0
 * @createTime 
 * @describe 自定义录制视频
 */
class VideoCameraXActivity : BaseActivity(), View.OnClickListener {
    companion object {

        fun createIntent(mContext: Context): Intent {
            return Intent(mContext, VideoCameraXActivity::class.java)
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_camerax_video)
        initView()
    }

    private lateinit var cameraProviderFuture:ListenableFuture<ProcessCameraProvider>
    private fun initView() {
        tv_start.setOnClickListener(this)
        tv_stop.setOnClickListener(this)
       initCameraX()
    }
    private var cameraProvider: ProcessCameraProvider? = null
    private lateinit var qualitySelector: QualitySelector

    private fun initCameraX() {
        qualitySelector = QualitySelector.from(Quality.HD)
        cameraProviderFuture = ProcessCameraProvider.getInstance(this)
        cameraProviderFuture.addListener(Runnable {
            cameraProvider = cameraProviderFuture.get()
            cameraProvider?.let { bindPreview(it) }
        }, ContextCompat.getMainExecutor(this))

    }

    private lateinit var videoCapture: VideoCapture<Recorder>
    fun bindPreview(cameraProvider: ProcessCameraProvider) {
        var preview: Preview = Preview.Builder()
            .build()
        preview.setSurfaceProvider(pvCameraX.getSurfaceProvider())

        val recorder = Recorder.Builder()
            .setExecutor(ThreadUtil.getThread())
            .setQualitySelector(qualitySelector)
            .build()

        videoCapture = VideoCapture.withOutput(recorder)

        var cameraSelector: CameraSelector = CameraSelector.Builder()
            .requireLensFacing(CameraSelector.LENS_FACING_BACK)
            .build()
        try {
            cameraProvider.bindToLifecycle(
              this, cameraSelector,videoCapture,preview)
        } catch (exc: Exception) {
            Log.e("video", "Use case binding failed", exc)
        }
    }

   private var currentRecording:Recording?= null
    private var filepath: String? = null
    fun start() {
        if (filepath == null) {
            filepath = this.getExternalFilesDir(null)?.absolutePath + File.separator +
                    "camerax_video"
        }
        var videoPath = filepath + File.separator+ "video_${Calendar.getInstance().timeInMillis}.mp4"
        val videoFile = File(videoPath)
        if (!videoFile.parentFile.exists()){
            videoFile.parentFile.mkdirs()
        }
        videoFile.createNewFile()
        Log.e("视频地址", videoPath)

        val mediaStoreOutput = FileOutputOptions.Builder(videoFile)
            .setFileSizeLimit(100*1024*1024)
            .build()

        var currentRecording = videoCapture.output
            .prepareRecording(this, mediaStoreOutput)
            .apply {
                if (ActivityCompat.checkSelfPermission(
                        this@VideoCameraXActivity,
                        Manifest.permission.RECORD_AUDIO
                    ) == PackageManager.PERMISSION_GRANTED
                ) {
                    withAudioEnabled()
                }
            }
            .start(ThreadUtil.getThread(), captureListener)

    }
    /**
     * CaptureEvent监听器。
     */
    private val captureListener = Consumer<VideoRecordEvent> { event ->

        if (event is VideoRecordEvent.Finalize){
            val videoUrl = event.outputResults.outputUri
            Log.e("视频地址 Uri",videoUrl.toString())
        }
    }

    override fun onClick(v: View?) {
        when (v?.id) {
            R.id.tv_start -> {
                currentRecording?.stop()
                start()
            }
            R.id.tv_stop -> {
                currentRecording?.stop()
            }
        }
    }


}

五、XML 配置

xml代码省略了 布局配置,xxxlayout布局可以自行配置。

<...................>

<androidx.camera.view.PreviewView
        android:id="@+id/pvCameraX"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

................................./>

如果对您有一些意义,希望您给博主一些鼓励(点赞、关注、收藏),如果有错误欢迎大家评论。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/128348.html

(0)
飞熊的头像飞熊bm

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!