دوربین را کنترل کنید

در این درس، نحوه کنترل مستقیم سخت افزار دوربین با استفاده از APIهای فریمورک را مورد بحث قرار می دهیم.

توجه: این صفحه به کلاس Camera اشاره دارد که منسوخ شده است. توصیه می کنیم از CameraX یا برای موارد استفاده خاص از Camera2 استفاده کنید. هر دو CameraX و Camera2 از اندروید 5.0 (سطح API 21) و بالاتر پشتیبانی می کنند.

کنترل مستقیم دوربین دستگاه به کد بسیار بیشتری نسبت به درخواست عکس یا فیلم از برنامه های دوربین موجود نیاز دارد. با این حال، اگر می خواهید یک برنامه دوربین تخصصی یا چیزی کاملاً یکپارچه در رابط کاربری برنامه خود بسازید، این درس به شما نشان می دهد که چگونه.

به منابع مرتبط زیر مراجعه کنید:

شئ دوربین را باز کنید

دریافت یک نمونه از شی Camera اولین مرحله در فرآیند کنترل مستقیم دوربین است. همانطور که برنامه دوربین خود اندروید انجام می دهد، راه پیشنهادی برای دسترسی به دوربین این است که Camera در یک رشته جداگانه که از onCreate() راه اندازی شده است باز کنید. این رویکرد ایده خوبی است زیرا ممکن است مدتی طول بکشد و ممکن است رشته UI را مختل کند. در یک پیاده سازی اساسی تر، باز کردن دوربین را می توان به روش onResume() موکول کرد تا استفاده مجدد از کد را تسهیل کند و جریان کنترل را ساده نگه دارد.

فراخوانی Camera.open() یک استثنا ایجاد می کند اگر دوربین قبلاً توسط برنامه دیگری استفاده می شود، بنابراین آن را در یک بلوک try قرار می دهیم.

کاتلین

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

جاوا

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 9، چارچوب دوربین از چندین دوربین پشتیبانی می کند. اگر از API قدیمی استفاده کنید و open() بدون آرگومان فراخوانی کنید، اولین دوربین رو به عقب را دریافت خواهید کرد.

پیش نمایش دوربین را ایجاد کنید

معمولاً برای گرفتن عکس نیاز است که کاربران شما پیش نمایشی از سوژه خود را قبل از کلیک کردن روی شاتر ببینند. برای انجام این کار، می‌توانید از SurfaceView برای ترسیم پیش‌نمایش چیزی که سنسور دوربین می‌گیرد استفاده کنید.

کلاس پیش نمایش

برای شروع کار با نمایش پیش نمایش، به کلاس پیش نمایش نیاز دارید. پیش‌نمایش به پیاده‌سازی رابط android.view.SurfaceHolder.Callback نیاز دارد که برای انتقال داده‌های تصویر از سخت‌افزار دوربین به برنامه استفاده می‌شود.

کاتلین

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)
    }
    ...
}

جاوا

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

همانطور که در بخش بعدی نشان داده شده است، قبل از شروع پیش نمایش تصویر زنده، کلاس پیش نمایش باید به شی Camera ارسال شود.

پیش نمایش را تنظیم و شروع کنید

یک نمونه دوربین و پیش‌نمایش مربوط به آن باید با ترتیب خاصی ایجاد شود و شی دوربین اول باشد. در قطعه زیر، فرآیند مقداردهی اولیه دوربین کپسوله شده است به طوری که هر زمان که کاربر برای تغییر دوربین اقدامی انجام دهد، Camera.startPreview() با روش setCamera() فراخوانی می شود. پیش‌نمایش نیز باید در متد callback کلاس preview surfaceChanged() راه‌اندازی مجدد شود.

کاتلین

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()
    }
}

جاوا

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

تنظیمات دوربین را تغییر دهید

تنظیمات دوربین روشی را که دوربین عکس می گیرد، از سطح زوم تا جبران نوردهی تغییر می دهد. این مثال فقط اندازه پیش نمایش را تغییر می دهد. برای اطلاعات بیشتر به کد منبع برنامه دوربین مراجعه کنید.

کاتلین

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()
    }
}

جاوا

@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();
}

جهت پیش نمایش را تنظیم کنید

اکثر برنامه های دوربین نمایشگر را در حالت افقی قفل می کنند زیرا جهت گیری طبیعی سنسور دوربین این است. این تنظیم مانع از عکس گرفتن در حالت پرتره نمی شود، زیرا جهت دستگاه در هدر EXIF ​​ثبت می شود. متد setCameraDisplayOrientation() به شما امکان می‌دهد نحوه نمایش پیش‌نمایش را بدون تأثیر بر نحوه ضبط تصویر تغییر دهید. با این حال، در اندروید قبل از سطح API 14، باید پیش نمایش خود را قبل از تغییر جهت متوقف کنید و سپس آن را مجددا راه اندازی کنید.

عکس بگیرید

پس از شروع پیش نمایش، از روش Camera.takePicture() برای گرفتن عکس استفاده کنید. می توانید اشیاء Camera.PictureCallback و Camera.ShutterCallback را ایجاد کرده و آنها را به Camera.takePicture() منتقل کنید.

اگر می‌خواهید به طور مداوم تصاویر را بگیرید، می‌توانید یک Camera.PreviewCallback ایجاد کنید که onPreviewFrame() را پیاده‌سازی می‌کند. برای چیزی در این بین، می‌توانید فقط فریم‌های پیش‌نمایش انتخاب‌شده را ضبط کنید، یا یک اقدام تاخیری برای فراخوانی takePicture() تنظیم کنید.

پیش نمایش را مجدداً راه اندازی کنید

پس از گرفتن عکس، قبل از اینکه کاربر بتواند عکس دیگری بگیرد، باید پیش نمایش را مجدداً راه اندازی کنید. در این مثال، راه اندازی مجدد با بارگذاری بیش از حد دکمه شاتر انجام می شود.

کاتلین

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()
}

جاوا

@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();
}

پیش نمایش را متوقف کنید و دوربین را رها کنید

هنگامی که برنامه شما با استفاده از دوربین تمام شد، نوبت به پاکسازی می رسد. به ویژه، باید شی Camera را رها کنید، در غیر این صورت در معرض خطر از کار افتادن سایر برنامه‌ها، از جمله نمونه‌های جدید برنامه خود هستید.

چه زمانی باید پیش نمایش را متوقف کنید و دوربین را رها کنید؟ خوب، تخریب سطح پیش‌نمایش یک اشاره بسیار خوب است که وقت آن رسیده است که پیش‌نمایش را متوقف کنید و دوربین را رها کنید، همانطور که در این روش‌ها از کلاس Preview نشان داده شده است.

کاتلین

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

جاوا

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

در اوایل درس، این رویه نیز بخشی از متد setCamera() بود، بنابراین مقداردهی اولیه دوربین همیشه با توقف پیش‌نمایش آغاز می‌شود.