Kamerayı kontrol etme

Bu derste, kamera donanımını doğrudan çerçeve API'lerini kullanarak nasıl kontrol edeceğinizi ele alacağız.

Not: Bu sayfada, desteği sonlandırılan Camera sınıfı ele alınmaktadır. CameraX veya belirli kullanım alanları için Camera2 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ı doğrudan kontrol etmek, mevcut kamera uygulamalarından fotoğraf veya video istemekten çok daha fazla kod gerektirir. Ancak özel bir kamera uygulaması veya uygulama kullanıcı arayüzünüze tamamen entegre edilmiş bir şey oluşturmak istiyorsanız bu derste nasıl yapılacağı gösterilmektedir.

Aşağıdaki ilgili kaynaklara göz atın:

Kamera nesnesini açma

Kamerayı doğrudan kontrol etme işleminin ilk adımı, Camera nesnesinin bir örneğini elde etmektir. Android'in kendi kamera uygulamasında olduğu gibi, kameraya erişmenin önerilen yolu onCreate()'ten başlatılan ayrı bir iş parçacığında Camera'ü açmaktır. Bu yaklaşım, biraz zaman alabileceği ve kullanıcı arayüzü iş parçacığının tıkanmasına neden olabileceği için iyi bir fikirdir. Daha basit bir uygulamada, kameranın açılması onResume() yöntemine ertelenebilir. Böylece kod yeniden kullanılabilir ve kontrol akışı basit tutulabilir.

Kamera başka bir uygulama tarafından kullanılıyorsa Camera.open() çağrısı bir istisna oluşturur. Bu nedenle, çağrıyı bir try bloğuna sarmalıyoruz.

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;
    }
}

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

Kamera önizlemesini oluşturma

Fotoğraf çekmek için kullanıcıların genellikle deklanşörü tıklamadan önce fotoğrafını çekecekleri nesnenin önizlemesini görmeleri gerekir. Bunu yapmak için kamera sensörünün algıladığı görüntülerin önizlemelerini çizmek üzere SurfaceView simgesini kullanabilirsiniz.

Önizleme Sınıfı

Önizleme göstermeye başlamak için önizleme sınıfına ihtiyacınız vardır. Önizleme için, kamera donanımından uygulamaya resim verilerini 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);
    }
...
}

Canlı resim önizlemesi başlatılmadan önce, önizleme sınıfı sonraki bölümde gösterildiği gibi Camera nesnesine aktarılmalıdır.

Önizlemeyi ayarlama ve başlatma

Kamera örneği ve ilgili önizlemesi, kamera nesnesi önce 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 işlem yaptığında Camera.startPreview() yöntemi tarafından setCamera() çağrılacak şekilde kapsüllenir. Önizleme, önizleme sınıfı surfaceChanged() geri çağırma yönteminde de yeniden başlatılmalıdır.

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ştirme

Kamera ayarları, yakınlaştırma seviyesinden pozlama telafisine kadar kameranın fotoğraf çekme şeklini değiştirir. Bu örnekte yalnızca önizleme boyutu değiştirilmektedir. Daha fazla ö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ü ayarlama

Çoğu kamera uygulaması, kamera sensörünün doğal yönü yatay olduğu için ekranı yatay moda kilitler. Cihazın yönü EXIF başlığına kaydedildiği için bu ayar, dikey modda fotoğraf çekmenizi engellemez. setCameraDisplayOrientation() yöntemi, resmin nasıl kaydedildiğini etkilemeden önizlemenin nasıl gösterileceğini değiştirmenize olanak tanır. Ancak API düzeyi 14'ten önceki Android sürümlerinde, yönü değiştirmeden önce önizlemenizi durdurup yeniden başlatmanız gerekir.

Fotoğraf çekme

Önizleme başladıktan sonra fotoğraf çekmek için Camera.takePicture() yöntemini kullanın. Camera.PictureCallback ve Camera.ShutterCallback nesneleri oluşturup bunları Camera.takePicture()'ye iletebilirsiniz.

Resimleri sürekli olarak almak istiyorsanız onPreviewFrame() uygulayan bir Camera.PreviewCallback oluşturabilirsiniz. Bu iki seçenek arasında bir şey için yalnızca seçili önizleme karelerini yakalayabilir veya takePicture() işlevini çağıracak gecikmeli bir işlem ayarlayabilirsiniz.

Önizlemeyi yeniden başlatma

Bir fotoğraf çekildikten sonra kullanıcının başka bir fotoğraf çekmesi için önizlemeyi yeniden başlatmanız gerekir. Bu örnekte, yeniden başlatma işlemi deklanşör düğmesine aşırı yük uygulanarak 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 durdurma ve kamerayı bırakma

Uygulamanız kamerayı kullanmayı tamamladığında temizleme işlemini yapmanız gerekir. Özellikle Camera nesnesini serbest bırakmanız gerekir. Aksi takdirde, kendi uygulamanızın yeni örnekleri de dahil olmak üzere diğer uygulamaların kilitlenmesi riskine girersiniz.

Önizlemeyi ne zaman durdurmalı ve kamerayı ne zaman bırakmalısınız? Önizleme yüzeyinizin yok edilmesi, önizlemeyi durdurup kamerayı serbest bırakma zamanının geldiğinin oldukça iyi bir göstergesidir. Preview sınıfındaki bu yöntemlerde gösterildiği gibi.

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 önceki bölümlerinde bu prosedür setCamera() yönteminin bir parçasıydı. Bu nedenle, kamerayı başlatmak her zaman önizlemeyi durdurmakla başlar.