التحكم في الكاميرا

في هذا الدرس، سنناقش كيفية التحكم في أجهزة الكاميرا مباشرةً باستخدام واجهات برمجة التطبيقات لإطار العمل.

ملاحظة: تشير هذه الصفحة إلى فئة الكاميرا التي تم إيقافها نهائيًا. ننصحك باستخدام الكاميراX أو الكاميرا2 في حالات استخدام معيَّنة. يتوافق كل من CameraX و Camera2 مع نظام التشغيل Android 5.0 (المستوى 21 لواجهة برمجة التطبيقات) والإصدارات الأحدث.

يتطلّب التحكّم المباشر في كاميرا الجهاز رمزًا أكبر بكثير من رمز طلب الصور أو الفيديوهات. من تطبيقات الكاميرا الحالية. أما إذا أردت إنشاء تطبيق كاميرا متخصص أو عنصرًا مدمجًا بالكامل في واجهة مستخدم التطبيق، فإن هذا الدرس يوضح كيفية إجراء ذلك.

يمكنك الاطّلاع على المراجع التالية ذات الصلة:

فتح كائن الكاميرا

إنّ الحصول على مثيل للكائن Camera هو الخطوة الأولى في التحكم المباشر في الكاميرا. وكما يفعل تطبيق الكاميرا في Android، الطريقة المُقترَحة للوصول إلى الكاميرا هي فتح Camera في سلسلة محادثات منفصلة. التي تم إطلاقها من onCreate(). يُعد هذا النهج فكرة جيدة حيث قد يستغرق الأمر بعض الوقت وقد يعيق مؤشر ترابط واجهة المستخدم. في عملية تنفيذ أساسية، يمكن تأجيل فتح الكاميرا إلى طريقة onResume() لتسهيل إعادة استخدام الرمز والحفاظ على سير عملية التحكم ببساطة.

يؤدي الاتصال بـ Camera.open() إلى طرح باستثناء إذا كانت الكاميرا قيد الاستخدام في تطبيق آخر، نربطها في جزء try.

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

بدءًا من المستوى 9 من واجهة برمجة التطبيقات، يتوافق إطار عمل الكاميرا مع عدة كاميرات. إذا كنت تستخدم واجهة برمجة التطبيقات القديمة وطلب open() بدون فستحصل على أول كاميرا خلفية.

إنشاء معاينة الكاميرا

يتطلب التقاط صورة عادةً أن يرى المستخدمون معاينة لموضوعهم قبل أن ينقروا عليه الغالق. ولإجراء ذلك، يمكنك استخدام SurfaceView لرسم معاينات لما شركة استشعار الكاميرا تقوم باستقبال المشكلة.

معاينة الصف

لبدء عرض معاينة، تحتاج إلى صف المعاينة. تشير رسالة الأشكال البيانية تتطلب المعاينة تنفيذ واجهة android.view.SurfaceHolder.Callback، وهي تُستخدم لتمرير الصورة البيانات من جهاز الكاميرا إلى التطبيق.

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

يجب تمرير فئة المعاينة إلى العنصر Camera قبل البث المباشر لبدء معاينة الصورة، كما هو موضّح في القسم التالي.

إعداد المعاينة وبدؤها

يجب إنشاء مثيل الكاميرا والمعاينة المرتبطة به في وحدة تخزين بالترتيب، مع جعل كائن الكاميرا أولًا. في المقتطف أدناه، عملية إعداد الكاميرا متضمنة بحيث يتم استدعاء Camera.startPreview() بواسطة setCamera()، عندما يفعل المستخدم شيئًا لتغيير والكاميرا. يجب أيضًا إعادة تشغيل المعاينة في طريقة استدعاء فئة المعاينة surfaceChanged().

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

تعديل إعدادات الكاميرا

تغير إعدادات الكاميرا طريقة التقاط الكاميرا للصور من ميزة التكبير/التصغير مستوى التعويض عن التعرض للضوء. يغير هذا المثال حجم المعاينة فقط؛ ويمكنك الاطلاع على رمز المصدر لتطبيق "الكاميرا" لعدة ميزات أخرى.

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

ضبط اتجاه المعاينة

وتعمل معظم تطبيقات الكاميرا على قفل الشاشة في الوضع الأفقي لأن هذا الوضع اتجاه أداة استشعار الكاميرا. لا يمنعك هذا الإعداد من استخدام الوضع الرأسي الصور، لأنه يتم تسجيل اتجاه الجهاز في عنوان EXIF. تتيح لك الطريقة setCameraDisplayOrientation() تغيير طريقة عرض المعاينة بدون التأثير في طريقة تسجيل الصورة. ومع ذلك، في Android السابق إلى المستوى 14 من واجهة برمجة التطبيقات، يجب إيقاف المعاينة قبل تغيير الاتجاه ثم إعادة تشغيله.

التقاط صورة

استخدام Camera.takePicture() لالتقاط صورة بمجرد بدء المعاينة. يمكنك إنشاء كائنين Camera.PictureCallback وCamera.ShutterCallback وتمريرهما إلى Camera.takePicture().

إذا أردت التقاط الصور بشكل مستمر، يمكنك إنشاء Camera.PreviewCallback تنفذ onPreviewFrame(). بالنسبة شيئًا ما بينهما، يمكنك التقاط إطارات معاينة محددة فقط، أو إعداد إجراء متأخر للاتصال بـ takePicture().

إعادة بدء المعاينة

بعد التقاط صورة، يجب إعادة تشغيل المعاينة قبل أن يتمكّن المستخدم التقاط صورة أخرى. في هذا المثال، تتم إعادة التشغيل من خلال التحميل الزائد. زر الغالق.

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

إيقاف المعاينة وإطلاق الكاميرا

بعد الانتهاء من معالجة طلبك باستخدام الكاميرا، يحين وقت تنظيف الكاميرا. ضِمن على وجه التحديد، عليك تحرير كائن Camera، وإلا فقد تتعرض لأعطال التطبيقات، بما في ذلك النُسخ الجديدة من تطبيقك.

متى يجب إيقاف المعاينة وإفلات الكاميرا؟ حسنًا، سيساعدك وجود تعرض معاينة البيانات تعد تلميحًا جيدًا إلى أن الوقت قد حان لإيقاف معاينة الكاميرا وتحريرها، كما هو موضَّح في هذه الطرق من فئة Preview.

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

في وقت سابق من هذا الدرس، كان هذا الإجراء أيضًا جزءًا من طريقة setCamera()، لذا فإن إعداد الكاميرا يبدأ دائمًا بإيقاف معاينة.