Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Android

วิธีใช้งานกล้อง (Camera) ในแอป Android ด้วย Kotlin

ภาพรวม

ตัวอย่างนี้สาธิตการใช้งานกล้องใน Android 2 วิธี: 1) ใช้ Camera Intent (ง่าย ไม่ต้องขอ Permission Camera โดยตรง) 2) ใช้ CameraX (สมัยใหม่ แนะนำ)

ขั้นตอนที่ 1: สร้างโปรเจกต์ใหม่

เปิด Android Studio เลือก File → New Project

ขั้นตอนที่ 2: Layout (activity_main.xml)

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:tools="https://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:layout_marginBottom="32dp"
        android:text="Take a Photo" />

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_above="@+id/button"
        android:layout_alignParentTop="true"
        android:layout_marginBottom="16dp"
        android:scaleType="centerCrop"
        android:src="@drawable/ic_baseline_image_24" />

</RelativeLayout>

วิธีที่ 1: Camera Intent (ง่าย ไม่ต้องขอ Permission Camera)

import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.net.Uri
import android.os.Bundle
import android.provider.MediaStore
import android.widget.Button
import android.widget.ImageView
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.FileProvider
import java.io.File

class MainActivity : AppCompatActivity() {
    private lateinit var imageView: ImageView
    private var photoUri: Uri? = null

    // Activity Result API สำหรับถ่ายรูป
    private val takePictureLauncher = registerForActivityResult(ActivityResultContracts.TakePicture()) { success ->
        if (success) {
            photoUri?.let { imageView.setImageURI(it) }
        } else {
            Toast.makeText(this, "Photo capture failed or cancelled", Toast.LENGTH_SHORT).show()
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        title = "KotlinApp"

        imageView = findViewById(R.id.imageView)
        val photoButton = findViewById<Button>(R.id.button)

        photoButton.setOnClickListener {
            dispatchTakePictureIntent()
        }
    }

    private fun dispatchTakePictureIntent() {
        val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
        
        // สร้างไฟล์สำหรับเก็บรูป (ใช้ FileProvider)
        val photoFile = createImageFile() ?: return
        photoUri = FileProvider.getUriForFile(
            this, 
            "${packageName}.fileprovider", 
            photoFile
        )
        
        intent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri)
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)

        if (intent.resolveActivity(packageManager) != null) {
            takePictureLauncher.launch(intent)
        } else {
            Toast.makeText(this, "No camera app available", Toast.LENGTH_SHORT).show()
        }
    }

    private fun createImageFile(): File? {
        val timeStamp = java.text.SimpleDateFormat("yyyyMMdd_HHmmss", java.util.Locale.getDefault()).format(java.util.Date())
        val storageDir = getExternalFilesDir(android.os.Environment.DIRECTORY_PICTURES)
        return try {
            File.createTempFile("IMG_${timeStamp}_", ".jpg", storageDir)
        } catch (e: Exception) {
            e.printStackTrace()
            null
        }
    }
}

วิธีที่ 2: CameraX (แนะนำสำหรับ Production)

build.gradle

dependencies {
    def camerax_version = "1.3.3"
    implementation "androidx.camera:camera-core:${camerax_version}"
    implementation "androidx.camera:camera-camera2:${camerax_version}"
    implementation "androidx.camera:camera-lifecycle:${camerax_version}"
    implementation "androidx.camera:camera-view:${camerax_version}"
    implementation "androidx.camera:camera-video:${camerax_version}"
}

Layout (เพิ่ม PreviewView)

<androidx.camera.view.PreviewView
    android:id="@+id/previewView"
    android:layout_width="match_parent"
    android:layout_height="0dp"
    android:layout_weight="1" />
    
<Button
    android:id="@+id/captureButton"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Capture" />

MainActivity.kt (CameraX)

import androidx.camera.core.ImageCapture
import androidx.camera.core.ImageCaptureException
import androidx.camera.core.Preview
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.camera.view.PreviewView
import androidx.core.content.ContextCompat
import com.google.common.util.concurrent.ListenableFuture
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors

class MainActivity : AppCompatActivity() {
    private lateinit var previewView: PreviewView
    private lateinit var captureButton: Button
    private var imageCapture: ImageCapture? = null
    private val cameraExecutor = Executors.newSingleThreadExecutor()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        
        previewView = findViewById(R.id.previewView)
        captureButton = findViewById(R.id.captureButton)
        
        startCamera()
        
        captureButton.setOnClickListener { takePhoto() }
    }

    private fun startCamera() {
        val cameraProviderFuture: ListenableFuture<ProcessCameraProvider> = 
            ProcessCameraProvider.getInstance(this)
        
        cameraProviderFuture.addListener({
            val cameraProvider = cameraProviderFuture.get()
            bindCameraUseCases(cameraProvider)
        }, ContextCompat.getMainExecutor(this))
    }

    private fun bindCameraUseCases(cameraProvider: ProcessCameraProvider) {
        val preview = Preview.Builder().build().also {
            it.setSurfaceProvider(previewView.surfaceProvider)
        }

        imageCapture = ImageCapture.Builder()
            .setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
            .build()

        val cameraSelector = androidx.camera.core.CameraSelector.DEFAULT_BACK_CAMERA

        try {
            cameraProvider.unbindAll()
            cameraProvider.bindToLifecycle(this, cameraSelector, preview, imageCapture)
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

    private fun takePhoto() {
        val outputFile = createImageFile() ?: return
        val outputOptions = ImageCapture.OutputFileOptions.Builder(outputFile).build()
        
        imageCapture?.takePicture(
            outputOptions, 
            cameraExecutor, 
            object : ImageCapture.OnImageSavedCallback {
                override fun onError(exc: ImageCaptureException) {
                    runOnUiThread { Toast.makeText(this@MainActivity, "Capture failed: ${exc.message}", Toast.LENGTH_SHORT).show() }
                }
                override fun onImageSaved(output: ImageCapture.OutputFileResults) {
                    runOnUiThread { 
                        Toast.makeText(this@MainActivity, "Photo saved: ${outputFile.absolutePath}", Toast.LENGTH_SHORT).show()
                    }
                }
            }
        )
    }

    override fun onDestroy() {
        super.onDestroy()
        cameraExecutor.shutdown()
    }
}

ขั้นตอนที่ 3: Manifest

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="https://schemas.android.com/apk/res/android"
    package="com.example.cameraapp">

    <uses-permission android:name="android.permission.CAMERA" />
    <uses-feature android:name="android.hardware.camera" android:required="true" />
    <uses-feature android:name="android.hardware.camera.autofocus" android:required="false" />

    <application ... >
        <activity android:name=".MainActivity"> ... </activity>
        
        <provider
            android:name="androidx.core.content.FileProvider"
            android:authorities="${applicationId}.fileprovider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_paths" />
        </provider>
    </application>
</manifest>

file_paths.xml (res/xml/file_paths.xml)

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="https://schemas.android.com/apk/res/android">
    <external-files-path name="camera_photos" path="Pictures/" />
</paths>

เปรียบเทียบวิธีการ

ด้านCamera IntentCameraX
ความซับซ้อนต่ำมากปานกลาง
UI Controlใช้ Camera App ของระบบCustom UI ได้เต็มที่
Permissionไม่ต้องขอ (System App จัดการ)ต้องขอ CAMERA Permission
Featuresจำกัด (ถ่ายรูป/วิดีโอพื้นฐาน)ครบครัน (Zoom, Flash, Focus, Analysis, Video)
LifecycleจัดการเองLifecycle-aware อัตโนมัติ
แนะนำสำหรับApp เรียกใช้กล้องบ้างครั้งCamera App, AR, ML, Custom UI

การทดสอบ

รันบนอุปกรณ์จริง (Emulator จำลองกล้องได้แต่จำกัด) กดปุ่มถ่ายรูป จะเปิด Camera App หรือแสดง Preview ตามวิธีที่เลือก