Kamerayı kontrol etme

Bu derste, çerçeve API'lerini kullanarak kamera donanımının doğrudan nasıl kontrol edileceğini tartışacağız.

Not: Bu sayfa, kullanımdan kaldırılan Kamera sınıfıyla ilgilidir. KameraX veya belirli kullanım alanlarında Kamera2 kullanmanızı öneririz. Hem CameraX hem de Camera2, Android 5.0 (API düzeyi 21) ve sonraki sürümleri destekler.

Bir cihaz kamerasının doğrudan kontrol edilmesi, mevcut kamera uygulamalarından resim veya video istemekten çok daha fazla kod gerektirir. Ancak özel bir kamera uygulaması veya uygulamanızın kullanıcı arayüzüne tamamen entegre bir şey geliştirmek istiyorsanız bu derste bunu nasıl yapacağınızı öğrenebilirsiniz.

Aşağıdaki ilgili kaynaklara bakın:

Kamera Nesnesini açma

Kamerayı doğrudan kontrol etmenin ilk adımı Camera nesnesinin bir örneğini almaktır. Android'in kendi Kamera uygulamasının da yaptığı gibi, kameraya erişmek için önerilen yol Camera API'sini onCreate() ürününden başlatılan ayrı bir iş parçacığında açmaktır. Bu, biraz zaman alabileceği ve kullanıcı arayüzü iş parçacığını boğabileceği için iyi bir yaklaşımdır. Daha temel bir uygulamada, kodun yeniden kullanımını kolaylaştırmak ve kontrol akışını basit tutmak için kameranın açılması onResume() yöntemine ertelenebilir.

Camera.open() çağrısı, kamera zaten başka bir uygulama tarafından kullanılıyorsa istisna oluşturur. Bu nedenle, kamerayı try bloğuna sarmalarız.

Kotlin

private fun safeCameraOpen(id: Int): Boolean {
    return try {
        releaseCameraAndPreview()
        mCamera = Camera.open(id)
        true
    } catch (e: Exception) {
        Log.e(getString(R.string.app_name), "failed to open Camera")
        e.printStackTrace()
        false
    }
}

private fun releaseCameraAndPreview() {
    preview?.setCamera(null)
    mCamera?.also { camera ->
        camera.release()
        mCamera = null
    }
}

Java

private boolean safeCameraOpen(int id) {
    boolean qOpened = false;

    try {
        releaseCameraAndPreview();
        camera = Camera.open(id);
        qOpened = (camera != null);
    } catch (Exception e) {
        Log.e(getString(R.string.app_name), "failed to open Camera");
        e.printStackTrace();
    }

    return qOpened;
}

private void releaseCameraAndPreview() {
    preview.setCamera(null);
    if (camera != null) {
        camera.release();
        camera = null;
    }
}

API düzeyi 9'dan itibaren kamera çerçevesi birden fazla kamerayı destekler. Eski API'yi kullanır ve bağımsız değişken olmadan open() yöntemini çağırırsanız ilk arka kamerayı alırsınız.

Kamera Önizlemesi Oluştur

Fotoğraf çekmek için genellikle kullanıcılarınızın deklanşörü tıklamadan önce öznenin önizlemesini görmeleri gerekir. Bunun için, kamera sensörünün alacağı öğenin önizlemelerini çizmek için bir SurfaceView kullanabilirsiniz.

Önizleme Sınıfı

Önizleme görüntülemeye başlamak için önizleme sınıfına ihtiyacınız vardır. Önizlemede, görüntü verilerini kamera donanımından uygulamaya aktarmak için kullanılan android.view.SurfaceHolder.Callback arayüzünün uygulanması gerekir.

Kotlin

class Preview(
        context: Context,
        val surfaceView: SurfaceView = SurfaceView(context)
) : ViewGroup(context), SurfaceHolder.Callback {

    var mHolder: SurfaceHolder = surfaceView.holder.apply {
        addCallback(this@Preview)
        setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS)
    }
    ...
}

Java

class Preview extends ViewGroup implements SurfaceHolder.Callback {

    SurfaceView surfaceView;
    SurfaceHolder holder;

    Preview(Context context) {
        super(context);

        surfaceView = new SurfaceView(context);
        addView(surfaceView);

        // Install a SurfaceHolder.Callback so we get notified when the
        // underlying surface is created and destroyed.
        holder = surfaceView.getHolder();
        holder.addCallback(this);
        holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    }
...
}

Sonraki bölümde gösterildiği gibi, canlı resim önizlemesinin başlatılabilmesi için önizleme sınıfının Camera nesnesine aktarılması gerekir.

Önizlemeyi Ayarlama ve Başlatma

Bir kamera örneği ve ilgili önizlemesi, kamera nesnesi başta olacak şekilde belirli bir sırada oluşturulmalıdır. Aşağıdaki snippet'te kamerayı başlatma işlemi, kullanıcı kamerayı değiştirmek için bir şey her yaptığında setCamera() yöntemiyle Camera.startPreview() çağrılacak şekilde kapsüllenir. Önizlemenin, önizleme sınıfı surfaceChanged() geri çağırma yönteminde de yeniden başlatılması gerekir.

Kotlin

fun setCamera(camera: Camera?) {
    if (mCamera == camera) {
        return
    }

    stopPreviewAndFreeCamera()

    mCamera = camera

    mCamera?.apply {
        mSupportedPreviewSizes = parameters.supportedPreviewSizes
        requestLayout()

        try {
            setPreviewDisplay(holder)
        } catch (e: IOException) {
            e.printStackTrace()
        }

        // Important: Call startPreview() to start updating the preview
        // surface. Preview must be started before you can take a picture.
        startPreview()
    }
}

Java

public void setCamera(Camera camera) {
    if (mCamera == camera) { return; }

    stopPreviewAndFreeCamera();

    mCamera = camera;

    if (mCamera != null) {
        List<Size> localSizes = mCamera.getParameters().getSupportedPreviewSizes();
        supportedPreviewSizes = localSizes;
        requestLayout();

        try {
            mCamera.setPreviewDisplay(holder);
        } catch (IOException e) {
            e.printStackTrace();
        }

        // Important: Call startPreview() to start updating the preview
        // surface. Preview must be started before you can take a picture.
        mCamera.startPreview();
    }
}

Kamera Ayarlarını Değiştir

Kamera ayarları, kameranın resim çekme şeklini, yakınlaştırma düzeyinden pozlama telafisine değiştirir. Bu örnekte yalnızca önizleme boyutu değiştirilir. Daha birçok örnek için Kamera uygulamasının kaynak koduna bakın.

Kotlin

override fun surfaceChanged(holder: SurfaceHolder, format: Int, w: Int, h: Int) {
    mCamera?.apply {
        // Now that the size is known, set up the camera parameters and begin
        // the preview.
        parameters?.also { params ->
            params.setPreviewSize(previewSize.width, previewSize.height)
            requestLayout()
            parameters = params
        }

        // Important: Call startPreview() to start updating the preview surface.
        // Preview must be started before you can take a picture.
        startPreview()
    }
}

Java

@Override
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
    // Now that the size is known, set up the camera parameters and begin
    // the preview.
    Camera.Parameters parameters = mCamera.getParameters();
    parameters.setPreviewSize(previewSize.width, previewSize.height);
    requestLayout();
    mCamera.setParameters(parameters);

    // Important: Call startPreview() to start updating the preview surface.
    // Preview must be started before you can take a picture.
    mCamera.startPreview();
}

Önizleme Yönünü Ayarla

Kamera sensörünün doğal yönü bu olduğundan, çoğu kamera uygulaması ekranı yatay moda kilitler. Cihazın yönü EXIF üstbilgisine kaydedildiğinden, bu ayar dikey modda fotoğraf çekmenizi engellemez. setCameraDisplayOrientation() yöntemi, resmin kaydedilme şeklini etkilemeden önizlemenin görüntülenme biçimini değiştirmenize olanak tanır. Ancak API düzeyi 14'ten önceki Android cihazlarda yönü değiştirmeden önce önizlemenizi durdurmanız ve ardından yeniden başlatmanız gerekir.

Resim Çek

Önizleme başladıktan sonra resim çekmek için Camera.takePicture() yöntemini kullanın. Camera.PictureCallback ve Camera.ShutterCallback nesneleri oluşturup bunları Camera.takePicture() öğesine aktarabilirsiniz.

Resimleri sürekli çekmek istiyorsanız, onPreviewFrame() uygulayan bir Camera.PreviewCallback oluşturabilirsiniz. Aradaki bir durum için yalnızca seçili önizleme karelerini yakalayabilir veya takePicture() yöntemini çağırmak için gecikmeli bir işlem ayarlayabilirsiniz.

Önizlemeyi Yeniden Başlat

Bir resim çekildikten sonra, kullanıcının başka bir fotoğraf çekebilmesi için önizlemeyi yeniden başlatmanız gerekir. Bu örnekte yeniden başlatma işlemi deklanşör düğmesine fazla yük bindirerek yapılır.

Kotlin

fun onClick(v: View) {
    previewState = if (previewState == K_STATE_FROZEN) {
        camera?.startPreview()
        K_STATE_PREVIEW
    } else {
        camera?.takePicture(null, rawCallback, null)
        K_STATE_BUSY
    }
    shutterBtnConfig()
}

Java

@Override
public void onClick(View v) {
    switch(previewState) {
    case K_STATE_FROZEN:
        camera.startPreview();
        previewState = K_STATE_PREVIEW;
        break;

    default:
        camera.takePicture( null, rawCallback, null);
        previewState = K_STATE_BUSY;
    } // switch
    shutterBtnConfig();
}

Önizlemeyi Durdurup Kamerayı Bırakın

Uygulamanız kamerayı kullanarak tamamlandıktan sonra sıra temizlemeye gelir. Özellikle Camera nesnesini serbest bırakmanız gerekir. Aksi takdirde, kendi uygulamanızın yeni örnekleri de dahil olmak üzere diğer uygulamaların kilitlenme riskiyle karşı karşıya kalırsınız.

Önizlemeyi durdurup kamerayı ne zaman bırakmalısınız? Önizleme yüzeyinin yok edilmesi, Preview sınıfındaki bu yöntemlerde gösterildiği gibi önizlemeyi durdurup kamerayı bırakma zamanının geldiğine dair iyi bir ipucu verir.

Kotlin

override fun surfaceDestroyed(holder: SurfaceHolder) {
    // Surface will be destroyed when we return, so stop the preview.
    // Call stopPreview() to stop updating the preview surface.
    mCamera?.stopPreview()
}

/**
 * When this function returns, mCamera will be null.
 */
private fun stopPreviewAndFreeCamera() {
    mCamera?.apply {
        // Call stopPreview() to stop updating the preview surface.
        stopPreview()

        // Important: Call release() to release the camera for use by other
        // applications. Applications should release the camera immediately
        // during onPause() and re-open() it during onResume()).
        release()

        mCamera = null
    }
}

Java

@Override
public void surfaceDestroyed(SurfaceHolder holder) {
    // Surface will be destroyed when we return, so stop the preview.
    if (mCamera != null) {
        // Call stopPreview() to stop updating the preview surface.
        mCamera.stopPreview();
    }
}

/**
 * When this function returns, mCamera will be null.
 */
private void stopPreviewAndFreeCamera() {

    if (mCamera != null) {
        // Call stopPreview() to stop updating the preview surface.
        mCamera.stopPreview();

        // Important: Call release() to release the camera for use by other
        // applications. Applications should release the camera immediately
        // during onPause() and re-open() it during onResume()).
        mCamera.release();

        mCamera = null;
    }
}

Dersin başlarında bu prosedür de setCamera() yönteminin bir parçasıydı. Bu nedenle, kameranın başlatılması daima önizlemenin durdurulmasıyla başlar.