ภาพรวม
ตัวอย่างนี้สาธิตการใช้งานกล้องใน 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 Intent | CameraX |
|---|---|---|
| ความซับซ้อน | ต่ำมาก | ปานกลาง |
| 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 ตามวิธีที่เลือก