Camera API

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

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

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

الاعتبارات

قبل تفعيل تطبيقك لاستخدام الكاميرات على أجهزة Android، عليك التفكير في بعض الأسئلة حول كيفية استخدام تطبيقك لميزة الأجهزة هذه.

  • متطلبات الكاميرا: هل استخدام الكاميرا مهم جدًا لتطبيقك لدرجة أنك لا تريد تثبيت تطبيقك على جهاز لا يحتوي على كاميرا؟ إذا كان الأمر كذلك، يجب الإعلان عن متطلبات الكاميرا في ملف البيان.
  • التقاط صورة سريعة أو استخدام كاميرا مخصّصة: كيف سيستخدم تطبيقك الكاميرا؟ هل أنت مهتم فقط بالتقاط صورة سريعة أو مقطع فيديو، أم أن تطبيقك سيوفر طريقة جديدة لاستخدام الكاميرات؟ لالتقاط صورة أو مقطع فيديو سريع، ننصحك باستخدام تطبيقات الكاميرا الحالية. لتطوير ميزة كاميرا مخصّصة، يُرجى الاطّلاع على قسم إنشاء تطبيق كاميرا.
  • متطلبات الخدمات التي تعمل في المقدّمة: متى يتفاعل تطبيقك مع الكاميرا؟ على الإصدار 9 من نظام التشغيل Android (المستوى 28 لواجهة برمجة التطبيقات) والإصدارات الأحدث، لا يمكن للتطبيقات التي تعمل في الخلفية الوصول إلى الكاميرا. لذلك، يجب استخدام الكاميرا إما عندما يكون تطبيقك في المقدّمة أو كجزء من خدمة تعمل في المقدّمة.
  • مساحة التخزين: هل الصور أو الفيديوهات التي ينشئها تطبيقك مخصّصة لتكون مرئية لتطبيقك فقط أو لتتم مشاركتها حتى تتمكّن التطبيقات الأخرى، مثل "معرض الصور" أو تطبيقات الوسائط والتطبيقات الاجتماعية الأخرى، من استخدامها؟ هل تريد أن تكون الصور والفيديوهات متاحة حتى في حال إلغاء تثبيت تطبيقك؟ يمكنك الاطّلاع على قسم حفظ ملفات الوسائط لمعرفة كيفية تنفيذ هذه الخيارات.

الأساسيات

يتيح إطار عمل Android التقاط الصور والفيديو من خلال واجهة برمجة التطبيقات android.hardware.camera2 أو Intent الكاميرا. في ما يلي الفئات ذات الصلة:

android.hardware.camera2
هذه الحزمة هي واجهة برمجة التطبيقات الأساسية للتحكّم في كاميرات الأجهزة. ويمكن استخدامها لالتقاط الصور أو الفيديوهات عند إنشاء تطبيق كاميرا.
Camera
هذه الفئة هي واجهة برمجة التطبيقات القديمة التي تم إيقافها نهائيًا للتحكّم في كاميرات الأجهزة.
SurfaceView
تُستخدم هذه الفئة لعرض معاينة مباشرة للكاميرا للمستخدم.
MediaRecorder
تُستخدَم هذه الفئة لتسجيل فيديو من الكاميرا.
Intent
يمكن استخدام نوع إجراء intent من MediaStore.ACTION_IMAGE_CAPTURE أو MediaStore.ACTION_VIDEO_CAPTURE لالتقاط الصور أو الفيديوهات بدون استخدام عنصر Camera مباشرةً.

تعريفات ملف البيان

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

  • الإذن باستخدام الكاميرا: يجب أن يطلب تطبيقك الإذن باستخدام كاميرا الجهاز.
    <uses-permission android:name="android.permission.CAMERA" />

    ملاحظة: إذا كنت تستخدم الكاميرا من خلال استدعاء تطبيق كاميرا حالي، لن يحتاج تطبيقك إلى طلب هذا الإذن.

  • ميزات الكاميرا: يجب أن يعرّف تطبيقك أيضًا استخدام ميزات الكاميرا، على سبيل المثال:
    <uses-feature android:name="android.hardware.camera" />

    للاطّلاع على قائمة بميزات الكاميرا، يُرجى مراجعة مرجع الميزات في ملف البيان.

    تؤدي إضافة ميزات الكاميرا إلى ملف البيان إلى منع Google Play لتطبيقك من التثبيت على الأجهزة التي لا تتضمن كاميرا أو لا تدعم ميزات الكاميرا التي تحددها. لمزيد من المعلومات حول استخدام الفلترة المستندة إلى الميزات مع Google Play، يُرجى الاطّلاع على Google Play والفلترة المستندة إلى الميزات.

    إذا كان تطبيقك يمكنه استخدام كاميرا أو ميزة كاميرا للتشغيل بشكل صحيح، ولكنّه لا يتطلّب ذلك، يجب تحديد ذلك في ملف البيان من خلال تضمين السمة android:required وضبطها على false:

    <uses-feature android:name="android.hardware.camera" android:required="false" />
  • إذن الوصول إلى مساحة التخزين: يمكن لتطبيقك حفظ الصور أو الفيديوهات في وحدة تخزين خارجية للجهاز (بطاقة SD) إذا كان يستهدف الإصدار 10 من نظام التشغيل Android (مستوى واجهة برمجة التطبيقات 29) أو إصدارًا أقدم ويحدّد ما يلي في البيان.
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
  • إذن تسجيل الصوت: لتسجيل الصوت مع التقاط الفيديو، يجب أن يطلب تطبيقك إذن التقاط الصوت.
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
  • إذن تحديد الموقع الجغرافي: إذا كان تطبيقك يضع علامات على الصور باستخدام معلومات عن الموقع الجغرافي للجهاز حسب نظام تحديد المواقع العالمي (GPS)، عليك طلب إذن ACCESS_FINE_LOCATION. يُرجى العِلم أنّه إذا كان تطبيقك يستهدف الإصدار 5.0 من نظام التشغيل Android (مستوى واجهة برمجة التطبيقات 21) أو إصدارًا أحدث، عليك أيضًا الإفصاح عن أنّ تطبيقك يستخدم نظام تحديد المواقع العالمي (GPS) في الجهاز:

    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    ...
    <!-- Needed only if your app targets Android 5.0 (API level 21) or higher. -->
    <uses-feature android:name="android.hardware.location.gps" />

    لمزيد من المعلومات حول الحصول على الموقع الجغرافي للمستخدم، يُرجى الاطّلاع على استراتيجيات الموقع الجغرافي.

استخدام تطبيقات الكاميرا الحالية

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

إنشاء تطبيق كاميرا

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

ملاحظة: الدليل التالي مخصّص لواجهة برمجة التطبيقات Camera القديمة التي تم إيقافها نهائيًا. بالنسبة إلى تطبيقات الكاميرا الجديدة أو المتقدّمة، يُنصح باستخدام واجهة برمجة التطبيقات android.hardware.camera2 الأحدث.

في ما يلي الخطوات العامة لإنشاء واجهة كاميرا مخصّصة لتطبيقك:

  • رصد الكاميرا والوصول إليها: يمكنك إنشاء رمز للتحقّق من توفّر الكاميرات وطلب الوصول إليها.
  • إنشاء فئة معاينة: أنشئ فئة معاينة للكاميرا تمدّد SurfaceView وتنفّذ واجهة SurfaceHolder. وتعرض هذه الفئة معاينة للصور المباشرة من الكاميرا.
  • إنشاء تخطيط معاينة - بمجرد حصولك على فئة معاينة الكاميرا، قم بإنشاء تخطيط عرض يتضمن المعاينة وعناصر التحكم في واجهة المستخدم التي تريدها.
  • إعداد المستمعين لالتقاط الصور: يمكنك ربط المستمعين بعناصر التحكّم في الواجهة لبدء التقاط الصور أو الفيديوهات استجابةً لإجراءات المستخدم، مثل الضغط على زر.
  • التقاط الملفات وحفظها: يمكنك إعداد الرمز لالتقاط الصور أو الفيديوهات وحفظ الناتج.
  • تحرير الكاميرا: بعد استخدام الكاميرا، يجب أن يحررها تطبيقك بشكل صحيح لتستخدمها التطبيقات الأخرى.

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

تنبيه: تذكَّر تحرير الكائن Camera من خلال استدعاء Camera.release() عند انتهاء تطبيقك من استخدامه. إذا لم يحرّر تطبيقك الكاميرا بشكل صحيح، ستفشل جميع المحاولات اللاحقة للوصول إلى الكاميرا، بما في ذلك المحاولات التي يجريها تطبيقك، وقد يؤدي ذلك إلى إغلاق تطبيقك أو التطبيقات الأخرى.

جارٍ رصد جهاز الكاميرا

إذا كان تطبيقك لا يتطلب استخدام الكاميرا على وجه التحديد من خلال بيان الإفصاح، عليك التحقّق من توفّر الكاميرا أثناء التشغيل. لإجراء عملية التحقّق هذه، استخدِم طريقة PackageManager.hasSystemFeature()، كما هو موضّح في الرمز البرمجي النموذجي أدناه:

Kotlin

/** Check if this device has a camera */
private fun checkCameraHardware(context: Context): Boolean {
    if (context.packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA)) {
        // this device has a camera
        return true
    } else {
        // no camera on this device
        return false
    }
}

Java

/** Check if this device has a camera */
private boolean checkCameraHardware(Context context) {
    if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)){
        // this device has a camera
        return true;
    } else {
        // no camera on this device
        return false;
    }
}

يمكن أن تحتوي أجهزة Android على كاميرات متعددة، على سبيل المثال كاميرا خلفية للتصوير الفوتوغرافي وكاميرا أمامية لمكالمات الفيديو. يسمح لك Android 2.3 (مستوى واجهة برمجة التطبيقات 9) والإصدارات الأحدث بالتحقّق من عدد الكاميرات المتاحة على الجهاز باستخدام طريقة Camera.getNumberOfCameras().

الوصول إلى الكاميرات

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

للوصول إلى الكاميرا الأساسية، استخدِم الإجراء Camera.open() وتأكَّد من رصد أي استثناءات، كما هو موضّح في الرمز أدناه:

Kotlin

/** A safe way to get an instance of the Camera object. */
fun getCameraInstance(): Camera? {
    return try {
        Camera.open() // attempt to get a Camera instance
    } catch (e: Exception) {
        // Camera is not available (in use or does not exist)
        null // returns null if camera is unavailable
    }
}

Java

/** A safe way to get an instance of the Camera object. */
public static Camera getCameraInstance(){
    Camera c = null;
    try {
        c = Camera.open(); // attempt to get a Camera instance
    }
    catch (Exception e){
        // Camera is not available (in use or does not exist)
    }
    return c; // returns null if camera is unavailable
}

تنبيه: يجب دائمًا التحقّق من الاستثناءات عند استخدام Camera.open(). سيؤدي عدم التحقّق من الاستثناءات في حال كانت الكاميرا قيد الاستخدام أو غير متوفّرة إلى إيقاف تطبيقك من قِبل النظام.

على الأجهزة التي تعمل بنظام التشغيل Android 2.3 (مستوى واجهة برمجة التطبيقات 9) أو الإصدارات الأحدث، يمكنك الوصول إلى كاميرات معيّنة باستخدام Camera.open(int). سيصل نموذج الرمز أعلاه إلى الكاميرا الخلفية الأولى على جهاز يحتوي على أكثر من كاميرا.

التحقّق من ميزات الكاميرا

بعد الحصول على إذن الوصول إلى الكاميرا، يمكنك الحصول على مزيد من المعلومات حول إمكاناتها باستخدام الطريقة Camera.getParameters() والتحقّق من عنصر Camera.Parameters الذي تم عرضه لمعرفة الإمكانات المتوافقة. عند استخدام المستوى 9 أو أعلى من واجهة برمجة التطبيقات، استخدِم Camera.getCameraInfo() لتحديد ما إذا كانت الكاميرا في الجهة الأمامية أو الخلفية من الجهاز، وتحديد اتجاه الصورة.

إنشاء صف معاينة

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

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

Kotlin

/** A basic Camera preview class */
class CameraPreview(
        context: Context,
        private val mCamera: Camera
) : SurfaceView(context), SurfaceHolder.Callback {

    private val mHolder: SurfaceHolder = holder.apply {
        // Install a SurfaceHolder.Callback so we get notified when the
        // underlying surface is created and destroyed.
        addCallback(this@CameraPreview)
        // deprecated setting, but required on Android versions prior to 3.0
        setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS)
    }

    override fun surfaceCreated(holder: SurfaceHolder) {
        // The Surface has been created, now tell the camera where to draw the preview.
        mCamera.apply {
            try {
                setPreviewDisplay(holder)
                startPreview()
            } catch (e: IOException) {
                Log.d(TAG, "Error setting camera preview: ${e.message}")
            }
        }
    }

    override fun surfaceDestroyed(holder: SurfaceHolder) {
        // empty. Take care of releasing the Camera preview in your activity.
    }

    override fun surfaceChanged(holder: SurfaceHolder, format: Int, w: Int, h: Int) {
        // If your preview can change or rotate, take care of those events here.
        // Make sure to stop the preview before resizing or reformatting it.
        if (mHolder.surface == null) {
            // preview surface does not exist
            return
        }

        // stop preview before making changes
        try {
            mCamera.stopPreview()
        } catch (e: Exception) {
            // ignore: tried to stop a non-existent preview
        }

        // set preview size and make any resize, rotate or
        // reformatting changes here

        // start preview with new settings
        mCamera.apply {
            try {
                setPreviewDisplay(mHolder)
                startPreview()
            } catch (e: Exception) {
                Log.d(TAG, "Error starting camera preview: ${e.message}")
            }
        }
    }
}

Java

/** A basic Camera preview class */
public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
    private SurfaceHolder mHolder;
    private Camera mCamera;

    public CameraPreview(Context context, Camera camera) {
        super(context);
        mCamera = camera;

        // Install a SurfaceHolder.Callback so we get notified when the
        // underlying surface is created and destroyed.
        mHolder = getHolder();
        mHolder.addCallback(this);
        // deprecated setting, but required on Android versions prior to 3.0
        mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    }

    public void surfaceCreated(SurfaceHolder holder) {
        // The Surface has been created, now tell the camera where to draw the preview.
        try {
            mCamera.setPreviewDisplay(holder);
            mCamera.startPreview();
        } catch (IOException e) {
            Log.d(TAG, "Error setting camera preview: " + e.getMessage());
        }
    }

    public void surfaceDestroyed(SurfaceHolder holder) {
        // empty. Take care of releasing the Camera preview in your activity.
    }

    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
        // If your preview can change or rotate, take care of those events here.
        // Make sure to stop the preview before resizing or reformatting it.

        if (mHolder.getSurface() == null){
          // preview surface does not exist
          return;
        }

        // stop preview before making changes
        try {
            mCamera.stopPreview();
        } catch (Exception e){
          // ignore: tried to stop a non-existent preview
        }

        // set preview size and make any resize, rotate or
        // reformatting changes here

        // start preview with new settings
        try {
            mCamera.setPreviewDisplay(mHolder);
            mCamera.startPreview();

        } catch (Exception e){
            Log.d(TAG, "Error starting camera preview: " + e.getMessage());
        }
    }
}

إذا أردت ضبط حجم معيّن لمعاينة الكاميرا، يمكنك ضبطه في طريقة surfaceChanged() كما هو موضّح في التعليقات أعلاه. عند ضبط حجم المعاينة، يجب استخدام القيم من getSupportedPreviewSizes(). لا تضبط قيمًا عشوائية في طريقة setPreviewSize().

ملاحظة: بعد طرح ميزة النوافذ المتعددة في الإصدار Android 7.0 (مستوى واجهة برمجة التطبيقات 24) والإصدارات الأحدث، لم يعُد بإمكانك افتراض أنّ نسبة العرض إلى الارتفاع للمعاينة هي نفسها نسبة العرض إلى الارتفاع للنشاط حتى بعد استدعاء setDisplayOrientation(). استنادًا إلى حجم النافذة ونسبة العرض إلى الارتفاع، قد تحتاج إلى ملاءمة معاينة الكاميرا العريضة في تنسيق عمودي، أو العكس، باستخدام تنسيق شاشة عريضة.

وضع المعاينة في تخطيط

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

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

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
  <FrameLayout
    android:id="@+id/camera_preview"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:layout_weight="1"
    />

  <Button
    android:id="@+id/button_capture"
    android:text="Capture"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    />
</LinearLayout>

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

<activity android:name=".CameraActivity"
          android:label="@string/app_name"

          android:screenOrientation="landscape">
          <!-- configure this activity to use landscape orientation -->

          <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

ملاحظة: ليس من الضروري أن تكون معاينة الكاميرا في الوضع الأفقي. بدءًا من الإصدار Android 2.2 (مستوى واجهة برمجة التطبيقات 8)، يمكنك استخدام طريقة setDisplayOrientation() لضبط تدوير صورة المعاينة. لتغيير اتجاه المعاينة عندما يغيّر المستخدم اتجاه الهاتف، عليك أولاً إيقاف المعاينة باستخدام Camera.stopPreview() ضمن طريقة surfaceChanged() في فئة المعاينة، ثم تغيير الاتجاه وبدء المعاينة مرة أخرى باستخدام Camera.startPreview().

في النشاط الخاص بعرض الكاميرا، أضِف فئة المعاينة إلى العنصر FrameLayout الموضَّح في المثال أعلاه. يجب أن يضمن نشاط الكاميرا أيضًا تحرير الكاميرا عند إيقافها مؤقتًا أو إيقاف تشغيلها. يوضّح المثال التالي كيفية تعديل نشاط الكاميرا لإرفاق فئة المعاينة الموضّحة في إنشاء فئة معاينة.

Kotlin

class CameraActivity : Activity() {

    private var mCamera: Camera? = null
    private var mPreview: CameraPreview? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // Create an instance of Camera
        mCamera = getCameraInstance()

        mPreview = mCamera?.let {
            // Create our Preview view
            CameraPreview(this, it)
        }

        // Set the Preview view as the content of our activity.
        mPreview?.also {
            val preview: FrameLayout = findViewById(R.id.camera_preview)
            preview.addView(it)
        }
    }
}

Java

public class CameraActivity extends Activity {

    private Camera mCamera;
    private CameraPreview mPreview;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        // Create an instance of Camera
        mCamera = getCameraInstance();

        // Create our Preview view and set it as the content of our activity.
        mPreview = new CameraPreview(this, mCamera);
        FrameLayout preview = (FrameLayout) findViewById(R.id.camera_preview);
        preview.addView(mPreview);
    }
}

ملاحظة: تشير الطريقة getCameraInstance() في المثال أعلاه إلى الطريقة النموذجية الموضّحة في الوصول إلى الكاميرات.

التقاط الصور

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

لاسترداد صورة، استخدِم طريقة Camera.takePicture(). تتطلّب هذه الطريقة ثلاث مَعلمات تتلقّى البيانات من الكاميرا. لتلقّي البيانات بتنسيق JPEG، يجب تنفيذ واجهة Camera.PictureCallback لتلقّي بيانات الصورة وكتابتها في ملف. يوضّح الرمز التالي تنفيذًا أساسيًا لواجهة Camera.PictureCallback لحفظ صورة تم تلقّيها من الكاميرا.

Kotlin

private val mPicture = Camera.PictureCallback { data, _ ->
    val pictureFile: File = getOutputMediaFile(MEDIA_TYPE_IMAGE) ?: run {
        Log.d(TAG, ("Error creating media file, check storage permissions"))
        return@PictureCallback
    }

    try {
        val fos = FileOutputStream(pictureFile)
        fos.write(data)
        fos.close()
    } catch (e: FileNotFoundException) {
        Log.d(TAG, "File not found: ${e.message}")
    } catch (e: IOException) {
        Log.d(TAG, "Error accessing file: ${e.message}")
    }
}

Java

private PictureCallback mPicture = new PictureCallback() {

    @Override
    public void onPictureTaken(byte[] data, Camera camera) {

        File pictureFile = getOutputMediaFile(MEDIA_TYPE_IMAGE);
        if (pictureFile == null){
            Log.d(TAG, "Error creating media file, check storage permissions");
            return;
        }

        try {
            FileOutputStream fos = new FileOutputStream(pictureFile);
            fos.write(data);
            fos.close();
        } catch (FileNotFoundException e) {
            Log.d(TAG, "File not found: " + e.getMessage());
        } catch (IOException e) {
            Log.d(TAG, "Error accessing file: " + e.getMessage());
        }
    }
};

يمكنك تفعيل التقاط صورة من خلال استدعاء طريقة Camera.takePicture(). يوضّح الرمز في المثال التالي كيفية استدعاء هذه الطريقة من زر View.OnClickListener.

Kotlin

val captureButton: Button = findViewById(R.id.button_capture)
captureButton.setOnClickListener {
    // get an image from the camera
    mCamera?.takePicture(null, null, picture)
}

Java

// Add a listener to the Capture button
Button captureButton = (Button) findViewById(R.id.button_capture);
captureButton.setOnClickListener(
    new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            // get an image from the camera
            mCamera.takePicture(null, null, picture);
        }
    }
);

ملاحظة: يشير العضو mPicture في المثال التالي إلى نموذج الرمز البرمجي أعلاه.

تنبيه: تذكَّر تحرير الكائن Camera من خلال استدعاء Camera.release() عند انتهاء تطبيقك من استخدامه. للحصول على معلومات حول كيفية تحرير الكاميرا، يُرجى الاطّلاع على تحرير الكاميرا.

تسجيل الفيديوهات

يتطلّب التقاط الفيديو باستخدام إطار عمل Android إدارة دقيقة لعنصر Camera والتنسيق مع فئة MediaRecorder. عند تسجيل فيديو باستخدام Camera، عليك إدارة طلبات Camera.lock() وCamera.unlock() للسماح لـ MediaRecorder بالوصول إلى أجهزة الكاميرا، بالإضافة إلى طلبات Camera.open() وCamera.release().

ملاحظة: بدءًا من الإصدار 4.0 من Android (المستوى 14 من واجهة برمجة التطبيقات)، تتم إدارة طلبات Camera.lock() وCamera.unlock() تلقائيًا.

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

  1. فتح الكاميرا: استخدِم Camera.open() للحصول على نسخة من عنصر الكاميرا.
  2. Connect Preview: يمكنك إعداد معاينة مباشرة لصورة الكاميرا من خلال توصيل جهاز SurfaceView بالكاميرا باستخدام Camera.setPreviewDisplay().
  3. Start Preview: يمكنك استدعاء Camera.startPreview() لبدء عرض صور الكاميرا المباشرة.
  4. بدء تسجيل الفيديو: يجب إكمال الخطوات التالية بالترتيب لتسجيل الفيديو بنجاح:
    1. فتح قفل الكاميرا: يمكنك فتح قفل الكاميرا ليستخدمها "MediaRecorder" من خلال الاتصال بالرقم ‎Camera.unlock().
    2. ضبط MediaRecorder: استدعِ الطرق الـ MediaRecorder التالية بهذا الترتيب. لمزيد من المعلومات، يُرجى الاطّلاع على مستندات مرجع MediaRecorder.
      1. ‫setCamera(): لضبط الكاميرا التي سيتم استخدامها لتسجيل الفيديو، استخدِم النسخة الحالية من Camera في تطبيقك.
      2. setAudioSource(): لضبط مصدر الصوت، استخدِم MediaRecorder.AudioSource.CAMCORDER.
      3. setVideoSource() - لضبط مصدر الفيديو، استخدِم MediaRecorder.VideoSource.CAMERA.
      4. اضبط تنسيق إخراج الفيديو وترميزه. بالنسبة إلى الإصدار 2.2 من نظام التشغيل Android (المستوى 8 من واجهة برمجة التطبيقات) والإصدارات الأحدث، استخدِم طريقة MediaRecorder.setProfile واحصل على مثيل للملف الشخصي باستخدام CamcorderProfile.get(). بالنسبة إلى إصدارات Android الأقدم من 2.2، يجب ضبط تنسيق إخراج الفيديو ومَعلمات الترميز:
        1. setOutputFormat() - لضبط تنسيق الناتج، حدِّد الإعداد التلقائي أو MediaRecorder.OutputFormat.MPEG_4.
        2. setAudioEncoder() - لضبط نوع ترميز الصوت، حدِّد الإعداد التلقائي أو MediaRecorder.AudioEncoder.AMR_NB.
        3. ‫setVideoEncoder(): لضبط نوع ترميز الفيديو، حدِّد الإعداد التلقائي أو MediaRecorder.VideoEncoder.MPEG_4_SP.
      5. ‫setOutputFile() - اضبط ملف الإخراج، واستخدِم getOutputMediaFile(MEDIA_TYPE_VIDEO).toString() من طريقة المثال في قسم حفظ ملفات الوسائط.
      6. ‫setPreviewDisplay(): يمكنك تحديد عنصر تنسيق معاينة SurfaceView لتطبيقك. استخدِم العنصر نفسه الذي حدّدته في Connect Preview.

      تنبيه: يجب استدعاء طرق الضبط MediaRecorder هذه بهذا الترتيب، وإلا سيواجه تطبيقك أخطاءً وسيتعذّر التسجيل.

    3. إعداد MediaRecorder: يمكنك إعداد MediaRecorder باستخدام إعدادات الضبط المقدَّمة من خلال استدعاء MediaRecorder.prepare().
    4. بدء MediaRecorder: ابدأ تسجيل الفيديو من خلال استدعاء MediaRecorder.start().
  5. إيقاف تسجيل الفيديو: يمكنك استدعاء الطرق التالية بالترتيب لإكمال تسجيل الفيديو بنجاح:
    1. إيقاف MediaRecorder: يمكنك إيقاف تسجيل الفيديو من خلال استدعاء MediaRecorder.stop().
    2. إعادة ضبط MediaRecorder: يمكنك اختياريًا إزالة إعدادات الضبط من المسجّل من خلال استدعاء MediaRecorder.reset().
    3. تحرير MediaRecorder: يمكنك تحرير MediaRecorder من خلال استدعاء MediaRecorder.release().
    4. قفل الكاميرا: يمكنك قفل الكاميرا حتى تتمكّن جلسات MediaRecorder المستقبلية من استخدامها من خلال طلب Camera.lock(). اعتبارًا من الإصدار 4.0 من Android (المستوى 14 لواجهة برمجة التطبيقات)، لا يكون هذا الطلب مطلوبًا إلا في حال تعذّر تنفيذ طلب MediaRecorder.prepare().
  6. إيقاف المعاينة: عندما ينتهي نشاطك من استخدام الكاميرا، أوقِف المعاينة باستخدام Camera.stopPreview().
  7. Release Camera: لإيقاف الكاميرا حتى تتمكّن التطبيقات الأخرى من استخدامها من خلال استدعاء Camera.release().

ملاحظة: يمكن استخدام MediaRecorder بدون إنشاء معاينة للكاميرا أولاً وتخطّي الخطوات القليلة الأولى من هذه العملية. ومع ذلك، بما أن المستخدمين يفضلون عادةً رؤية معاينة قبل بدء التسجيل، فإن هذه العملية ليست تمت مناقشته هنا.

ملاحظة: إذا كان تطبيقك يُستخدم عادةً لتسجيل الفيديو، اضبط setRecordingHint(boolean) على true قبل بدء المعاينة. يمكن أن يساعد هذا الإعداد في تقليل الوقت المستغرَق لبدء التسجيل.

ضبط MediaRecorder

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

Kotlin

private fun prepareVideoRecorder(): Boolean {
    mediaRecorder = MediaRecorder()

    mCamera?.let { camera ->
        // Step 1: Unlock and set camera to MediaRecorder
        camera?.unlock()

        mediaRecorder?.run {
            setCamera(camera)

            // Step 2: Set sources
            setAudioSource(MediaRecorder.AudioSource.CAMCORDER)
            setVideoSource(MediaRecorder.VideoSource.CAMERA)

            // Step 3: Set a CamcorderProfile (requires API Level 8 or higher)
            setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH))

            // Step 4: Set output file
            setOutputFile(getOutputMediaFile(MEDIA_TYPE_VIDEO).toString())

            // Step 5: Set the preview output
            setPreviewDisplay(mPreview?.holder?.surface)

            setOutputFormat(MediaRecorder.OutputFormat.MPEG_4)
            setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT)
            setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT)


            // Step 6: Prepare configured MediaRecorder
            return try {
                prepare()
                true
            } catch (e: IllegalStateException) {
                Log.d(TAG, "IllegalStateException preparing MediaRecorder: ${e.message}")
                releaseMediaRecorder()
                false
            } catch (e: IOException) {
                Log.d(TAG, "IOException preparing MediaRecorder: ${e.message}")
                releaseMediaRecorder()
                false
            }
        }

    }
    return false
}

Java

private boolean prepareVideoRecorder(){

    mCamera = getCameraInstance();
    mediaRecorder = new MediaRecorder();

    // Step 1: Unlock and set camera to MediaRecorder
    mCamera.unlock();
    mediaRecorder.setCamera(mCamera);

    // Step 2: Set sources
    mediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
    mediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);

    // Step 3: Set a CamcorderProfile (requires API Level 8 or higher)
    mediaRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH));

    // Step 4: Set output file
    mediaRecorder.setOutputFile(getOutputMediaFile(MEDIA_TYPE_VIDEO).toString());

    // Step 5: Set the preview output
    mediaRecorder.setPreviewDisplay(mPreview.getHolder().getSurface());

    // Step 6: Prepare configured MediaRecorder
    try {
        mediaRecorder.prepare();
    } catch (IllegalStateException e) {
        Log.d(TAG, "IllegalStateException preparing MediaRecorder: " + e.getMessage());
        releaseMediaRecorder();
        return false;
    } catch (IOException e) {
        Log.d(TAG, "IOException preparing MediaRecorder: " + e.getMessage());
        releaseMediaRecorder();
        return false;
    }
    return true;
}

قبل الإصدار 2.2 من Android (المستوى 8 من واجهة برمجة التطبيقات)، يجب ضبط مَعلمات تنسيق الإخراج وتنسيقات الترميز مباشرةً، بدلاً من استخدام CamcorderProfile. يتم توضيح هذا النهج في التعليمة البرمجية التالية:

Kotlin

    // Step 3: Set output format and encoding (for versions prior to API Level 8)
    mediaRecorder?.apply {
        setOutputFormat(MediaRecorder.OutputFormat.MPEG_4)
        setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT)
        setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT)
    }

Java

    // Step 3: Set output format and encoding (for versions prior to API Level 8)
    mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
    mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);
    mediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT);

يتم ضبط معلمات تسجيل الفيديو التالية لـ MediaRecorder على الإعدادات التلقائية، ولكن قد تحتاج إلى ضبط هذه الإعدادات لتطبيقك:

بدء MediaRecorder وإيقافه

عند بدء تسجيل الفيديو وإيقافه باستخدام الفئة MediaRecorder، يجب اتّباع ترتيب محدّد، كما هو موضّح أدناه.

  1. فتح الكاميرا باستخدام "Camera.unlock()"
  2. اضبط MediaRecorder كما هو موضّح في مثال الرمز أعلاه
  3. بدء التسجيل باستخدام "MediaRecorder.start()"
  4. تسجيل الفيديو
  5. إيقاف التسجيل باستخدام "MediaRecorder.stop()"
  6. تحرير مسجّل الوسائط باستخدام MediaRecorder.release()
  7. قفل الكاميرا باستخدام Camera.lock()

يوضّح المثال التالي كيفية ربط زر لبدء تسجيل الفيديو وإيقافه بشكل صحيح باستخدام الكاميرا وفئة MediaRecorder.

ملاحظة: عند إكمال تسجيل فيديو، لا تترك الكاميرا وإلا سيتم إيقاف المعاينة.

Kotlin

var isRecording = false
val captureButton: Button = findViewById(R.id.button_capture)
captureButton.setOnClickListener {
    if (isRecording) {
        // stop recording and release camera
        mediaRecorder?.stop() // stop the recording
        releaseMediaRecorder() // release the MediaRecorder object
        mCamera?.lock() // take camera access back from MediaRecorder

        // inform the user that recording has stopped
        setCaptureButtonText("Capture")
        isRecording = false
    } else {
        // initialize video camera
        if (prepareVideoRecorder()) {
            // Camera is available and unlocked, MediaRecorder is prepared,
            // now you can start recording
            mediaRecorder?.start()

            // inform the user that recording has started
            setCaptureButtonText("Stop")
            isRecording = true
        } else {
            // prepare didn't work, release the camera
            releaseMediaRecorder()
            // inform user
        }
    }
}

Java

private boolean isRecording = false;

// Add a listener to the Capture button
Button captureButton = (Button) findViewById(id.button_capture);
captureButton.setOnClickListener(
    new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            if (isRecording) {
                // stop recording and release camera
                mediaRecorder.stop();  // stop the recording
                releaseMediaRecorder(); // release the MediaRecorder object
                mCamera.lock();         // take camera access back from MediaRecorder

                // inform the user that recording has stopped
                setCaptureButtonText("Capture");
                isRecording = false;
            } else {
                // initialize video camera
                if (prepareVideoRecorder()) {
                    // Camera is available and unlocked, MediaRecorder is prepared,
                    // now you can start recording
                    mediaRecorder.start();

                    // inform the user that recording has started
                    setCaptureButtonText("Stop");
                    isRecording = true;
                } else {
                    // prepare didn't work, release the camera
                    releaseMediaRecorder();
                    // inform user
                }
            }
        }
    }
);

ملاحظة: في المثال أعلاه، تشير الطريقة prepareVideoRecorder() إلى الرمز النموذجي المعروض في ضبط MediaRecorder. تتولّى هذه الطريقة قفل الكاميرا وضبط مثيل MediaRecorder وإعداده.

جارٍ إلغاء الإذن بوصولك إلى الكاميرا

تُعدّ الكاميرات موردًا تشاركه التطبيقات على الجهاز. يمكن لتطبيقك استخدام الكاميرا بعد الحصول على نسخة من Camera، ويجب أن تكون حذرًا بشكل خاص عند تحرير عنصر الكاميرا عندما يتوقف تطبيقك عن استخدامه، وبمجرد إيقاف تطبيقك مؤقتًا (Activity.onPause()). إذا لم يحرر تطبيقك الكاميرا بشكل صحيح، فستفشل جميع المحاولات اللاحقة للوصول إلى الكاميرا، بما في ذلك تلك التي يقوم بها تطبيقك، وقد تتسبب في إيقاف تشغيل تطبيقك أو التطبيقات الأخرى.

لإصدار نسخة من عنصر Camera، استخدِم الإجراء Camera.release()، كما هو موضّح في مثال الرمز البرمجي أدناه.

Kotlin

class CameraActivity : Activity() {
    private var mCamera: Camera?
    private var preview: SurfaceView?
    private var mediaRecorder: MediaRecorder?

    override fun onPause() {
        super.onPause()
        releaseMediaRecorder() // if you are using MediaRecorder, release it first
        releaseCamera() // release the camera immediately on pause event
    }

    private fun releaseMediaRecorder() {
        mediaRecorder?.reset() // clear recorder configuration
        mediaRecorder?.release() // release the recorder object
        mediaRecorder = null
        mCamera?.lock() // lock camera for later use
    }

    private fun releaseCamera() {
        mCamera?.release() // release the camera for other applications
        mCamera = null
    }
}

Java

public class CameraActivity extends Activity {
    private Camera mCamera;
    private SurfaceView preview;
    private MediaRecorder mediaRecorder;

    ...

    @Override
    protected void onPause() {
        super.onPause();
        releaseMediaRecorder();       // if you are using MediaRecorder, release it first
        releaseCamera();              // release the camera immediately on pause event
    }

    private void releaseMediaRecorder(){
        if (mediaRecorder != null) {
            mediaRecorder.reset();   // clear recorder configuration
            mediaRecorder.release(); // release the recorder object
            mediaRecorder = null;
            mCamera.lock();           // lock camera for later use
        }
    }

    private void releaseCamera(){
        if (mCamera != null){
            mCamera.release();        // release the camera for other applications
            mCamera = null;
        }
    }
}

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

حفظ ملفات الوسائط

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

  • ‫Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES): تعرض هذه الطريقة الموقع الجغرافي العادي والمشترَك والمقترَح لحفظ الصور والفيديوهات. هذا الدليل مشترك (عام)، لذا يمكن للتطبيقات الأخرى العثور على الملفات المحفوظة في هذا الموقع وقراءتها وتغييرها وحذفها بسهولة. إذا ألغى المستخدم تثبيت تطبيقك، لن تتم إزالة ملفات الوسائط المحفوظة في هذا الموقع. لتجنُّب التداخل مع صور المستخدمين وفيديوهاتهم الحالية، عليك إنشاء دليل فرعي لملفات الوسائط الخاصة بتطبيقك داخل هذا الدليل، كما هو موضّح في عينة التعليمات البرمجية أدناه. تتوفّر هذه الطريقة في الإصدار 2.2 من نظام التشغيل Android (المستوى 8 لواجهة برمجة التطبيقات)، وللاطّلاع على الطلبات المكافئة في الإصدارات السابقة من واجهة برمجة التطبيقات، يُرجى الاطّلاع على حفظ الملفات المشتركة.
  • Context.getExternalFilesDir(Environment.DIRECTORY_PICTURES): تعرض هذه الطريقة موقعًا جغرافيًا عاديًا لحفظ الصور والفيديوهات المرتبطة بتطبيقك. في حال إلغاء تثبيت تطبيقك، تتم إزالة أي ملفات محفوظة في هذا الموقع. لا يتم فرض الأمان على الملفات في هذا الموقع، وقد تتمكن التطبيقات الأخرى من قراءتها وتغييرها وحذفها.

يوضّح المثال التالي كيفية إنشاء موقع جغرافي لملف وسائط File أو Uri يمكن استخدامه عند استدعاء كاميرا الجهاز باستخدام Intent أو كجزء من إنشاء تطبيق كاميرا.

Kotlin

val MEDIA_TYPE_IMAGE = 1
val MEDIA_TYPE_VIDEO = 2

/** Create a file Uri for saving an image or video */
private fun getOutputMediaFileUri(type: Int): Uri {
    return Uri.fromFile(getOutputMediaFile(type))
}

/** Create a File for saving an image or video */
private fun getOutputMediaFile(type: Int): File? {
    // To be safe, you should check that the SDCard is mounted
    // using Environment.getExternalStorageState() before doing this.

    val mediaStorageDir = File(
            Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),
            "MyCameraApp"
    )
    // This location works best if you want the created images to be shared
    // between applications and persist after your app has been uninstalled.

    // Create the storage directory if it does not exist
    mediaStorageDir.apply {
        if (!exists()) {
            if (!mkdirs()) {
                Log.d("MyCameraApp", "failed to create directory")
                return null
            }
        }
    }

    // Create a media file name
    val timeStamp = SimpleDateFormat("yyyyMMdd_HHmmss").format(Date())
    return when (type) {
        MEDIA_TYPE_IMAGE -> {
            File("${mediaStorageDir.path}${File.separator}IMG_$timeStamp.jpg")
        }
        MEDIA_TYPE_VIDEO -> {
            File("${mediaStorageDir.path}${File.separator}VID_$timeStamp.mp4")
        }
        else -> null
    }
}

Java

public static final int MEDIA_TYPE_IMAGE = 1;
public static final int MEDIA_TYPE_VIDEO = 2;

/** Create a file Uri for saving an image or video */
private static Uri getOutputMediaFileUri(int type){
      return Uri.fromFile(getOutputMediaFile(type));
}

/** Create a File for saving an image or video */
private static File getOutputMediaFile(int type){
    // To be safe, you should check that the SDCard is mounted
    // using Environment.getExternalStorageState() before doing this.

    File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(
              Environment.DIRECTORY_PICTURES), "MyCameraApp");
    // This location works best if you want the created images to be shared
    // between applications and persist after your app has been uninstalled.

    // Create the storage directory if it does not exist
    if (! mediaStorageDir.exists()){
        if (! mediaStorageDir.mkdirs()){
            Log.d("MyCameraApp", "failed to create directory");
            return null;
        }
    }

    // Create a media file name
    String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
    File mediaFile;
    if (type == MEDIA_TYPE_IMAGE){
        mediaFile = new File(mediaStorageDir.getPath() + File.separator +
        "IMG_"+ timeStamp + ".jpg");
    } else if(type == MEDIA_TYPE_VIDEO) {
        mediaFile = new File(mediaStorageDir.getPath() + File.separator +
        "VID_"+ timeStamp + ".mp4");
    } else {
        return null;
    }

    return mediaFile;
}

ملاحظة: يتوفّر Environment.getExternalStoragePublicDirectory() في الإصدار Android 2.2 (المستوى 8 من واجهة برمجة التطبيقات) أو الإصدارات الأحدث. إذا كنت تستهدف أجهزة تعمل بإصدارات أقدم من Android، استخدِم Environment.getExternalStorageDirectory() بدلاً من ذلك. لمزيد من المعلومات، يُرجى الاطّلاع على حفظ الملفات المشتركة.

لجعل معرّف الموارد المنتظم متوافقًا مع ملفات العمل، عليك أولاً تحويل معرّف الموارد المنتظم للملف إلى معرّف موارد منتظم للمحتوى. بعد ذلك، أضِف معرّف الموارد المنتظم للمحتوى إلى EXTRA_OUTPUT في Intent.

لمزيد من المعلومات حول حفظ الملفات على جهاز Android، يُرجى الاطّلاع على تخزين البيانات.

ميزات الكاميرا

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

للحصول على معلومات عامة حول كيفية استخدام الميزات التي يتم التحكّم فيها من خلال "Camera.Parameters"، يُرجى مراجعة قسم استخدام ميزات الكاميرا. للحصول على معلومات أكثر تفصيلاً حول كيفية استخدام الميزات التي يتم التحكّم فيها من خلال عنصر مَعلمات الكاميرا، اتّبِع الروابط في قائمة الميزات أدناه للانتقال إلى مستندات مرجع واجهة برمجة التطبيقات.

الجدول 1: ميزات الكاميرا الشائعة مرتّبة حسب مستوى واجهة برمجة تطبيقات Android الذي تم تقديمها فيه

الميزة مستوى واجهة برمجة التطبيقات الوصف
التعرّف على الوجوه 14 تحديد الوجوه البشرية في الصورة واستخدامها للتركيز والقياس وتوازن اللون الأبيض
مناطق القياس 14 تحديد منطقة واحدة أو أكثر ضمن صورة لاحتساب توازن اللون الأبيض
مجالات التركيز 14 يمكنك تحديد منطقة واحدة أو أكثر ضمن الصورة لاستخدامها في التركيز
White Balance Lock 14 إيقاف أو بدء التعديلات التلقائية لموازنة اللون الأبيض
Exposure Lock 14 إيقاف أو بدء تعديلات التعرّض التلقائية
Video Snapshot 14 التقاط صورة أثناء تصوير فيديو (التقاط إطار)
فيديو في وضع "التسريع الزمني" 11 تسجيل اللقطات مع ضبط فترات تأخير لتسجيل فيديو في وضع التسريع الزمني
Multiple Cameras 9 دعم أكثر من كاميرا واحدة على الجهاز، بما في ذلك الكاميرات الأمامية والخلفية
Focus Distance 9 يُبلغ عن المسافات بين الكاميرا والأجسام التي تظهر في نطاق التركيز
Zoom 8 ضبط تكبير الصورة
Exposure Compensation 8 زيادة مستوى التعرّض للضوء أو خفضه
GPS Data 5 تضمين بيانات الموقع الجغرافي أو حذفها من الصورة
White Balance 5 ضبط وضع توازن اللون الأبيض الذي يؤثر في قيم الألوان في الصورة التي يتم التقاطها
Focus Mode 5 ضبط كيفية تركيز الكاميرا على هدف معيّن، مثل التركيز التلقائي أو الثابت أو الفائق أو اللانهائي
Scene Mode 5 تطبيق وضع مُعدّ مسبقًا لأنواع معيّنة من حالات التصوير الفوتوغرافي، مثل المشاهد الليلية أو الشاطئ أو الثلج أو ضوء الشموع
JPEG Quality 5 يمكنك ضبط مستوى ضغط صورة JPEG، ما يؤدي إلى زيادة أو تقليل جودة وحجم ملف الصورة الناتج
Flash Mode 5 تفعيل الفلاش أو إيقافه أو استخدام الإعداد التلقائي
Color Effects 5 تطبيق تأثير لوني على الصورة التي تم التقاطها، مثل الأبيض والأسود أو البني الداكن أو السلبي
Anti-Banding 5 يقلّل من تأثير التدرّج في تدرّجات الألوان بسبب ضغط JPEG
Picture Format 1 تحديد تنسيق ملف الصورة
Picture Size 1 تحديد أبعاد الصورة المحفوظة بالبكسل

ملاحظة: لا تتوفّر هذه الميزات على بعض الأجهزة بسبب اختلافات في الأجهزة وتنفيذ البرامج. للحصول على معلومات حول التحقّق من مدى توفّر الميزات على الجهاز الذي يعمل عليه تطبيقك، يُرجى الاطّلاع على التحقّق من مدى توفّر الميزات.

التحقّق من توفُّر الميزة

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

يمكنك التحقّق من توفّر ميزات الكاميرا من خلال الحصول على مثيل لكائن مَعلمات الكاميرا والتحقّق من الطرق ذات الصلة. يوضّح لك عينة التعليمات البرمجية التالية كيفية الحصول على عنصر Camera.Parameters والتحقّق مما إذا كانت الكاميرا تتيح ميزة التركيز التلقائي:

Kotlin

val params: Camera.Parameters? = camera?.parameters
val focusModes: List<String>? = params?.supportedFocusModes
if (focusModes?.contains(Camera.Parameters.FOCUS_MODE_AUTO) == true) {
    // Autofocus mode is supported
}

Java

// get Camera parameters
Camera.Parameters params = camera.getParameters();

List<String> focusModes = params.getSupportedFocusModes();
if (focusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO)) {
  // Autofocus mode is supported
}

يمكنك استخدام الطريقة الموضّحة أعلاه لمعظم ميزات الكاميرا. يوفّر العنصر Camera.Parameters طريقة getSupported...() أو is...Supported() أو getMax...() لتحديد ما إذا كانت الميزة متاحة (وإلى أي مدى).

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

استخدام ميزات الكاميرا

يتم تفعيل معظم ميزات الكاميرا والتحكّم فيها باستخدام عنصر Camera.Parameters. يمكنك الحصول على هذا العنصر من خلال الحصول أولاً على مثيل لعنصر Camera، واستدعاء طريقة getParameters()، وتغيير عنصر المَعلمة الذي تم عرضه، ثم ضبطه مرة أخرى في عنصر الكاميرا، كما هو موضّح في مثال الرمز البرمجي التالي:

Kotlin

val params: Camera.Parameters? = camera?.parameters
params?.focusMode = Camera.Parameters.FOCUS_MODE_AUTO
camera?.parameters = params

Java

// get Camera parameters
Camera.Parameters params = camera.getParameters();
// set the focus mode
params.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
// set Camera parameters
camera.setParameters(params);

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

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

تتطلّب ميزات الكاميرا الأخرى المزيد من الرموز البرمجية لتنفيذها، بما في ذلك:

  • مجالات القياس والتركيز
  • التعرّف على الوجوه
  • فيديو بالتسريع الزمني

في الأقسام التالية، نقدّم لك نبذة سريعة عن كيفية تنفيذ هذه الميزات.

مجالات القياس والتركيز

في بعض سيناريوهات التصوير الفوتوغرافي، قد لا يؤدي التركيز التلقائي وقياس الضوء إلى الحصول على النتائج المرجوة. بدءًا من الإصدار Android 4.0 (مستوى واجهة برمجة التطبيقات 14)، يمكن أن يوفّر تطبيق الكاميرا عناصر تحكّم إضافية للسماح لتطبيقك أو للمستخدمين بتحديد مناطق في الصورة لاستخدامها في تحديد إعدادات مستوى التركيز أو الإضاءة وتمرير هذه القيم إلى أجهزة الكاميرا لاستخدامها في التقاط الصور أو الفيديو.

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

Kotlin

// Create an instance of Camera
camera = getCameraInstance()

// set Camera parameters
val params: Camera.Parameters? = camera?.parameters

params?.apply {
    if (maxNumMeteringAreas > 0) { // check that metering areas are supported
        meteringAreas = ArrayList<Camera.Area>().apply {
            val areaRect1 = Rect(-100, -100, 100, 100) // specify an area in center of image
            add(Camera.Area(areaRect1, 600)) // set weight to 60%
            val areaRect2 = Rect(800, -1000, 1000, -800) // specify an area in upper right of image
            add(Camera.Area(areaRect2, 400)) // set weight to 40%
        }
    }
    camera?.parameters = this
}

Java

// Create an instance of Camera
camera = getCameraInstance();

// set Camera parameters
Camera.Parameters params = camera.getParameters();

if (params.getMaxNumMeteringAreas() > 0){ // check that metering areas are supported
    List<Camera.Area> meteringAreas = new ArrayList<Camera.Area>();

    Rect areaRect1 = new Rect(-100, -100, 100, 100);    // specify an area in center of image
    meteringAreas.add(new Camera.Area(areaRect1, 600)); // set weight to 60%
    Rect areaRect2 = new Rect(800, -1000, 1000, -800);  // specify an area in upper right of image
    meteringAreas.add(new Camera.Area(areaRect2, 400)); // set weight to 40%
    params.setMeteringAreas(meteringAreas);
}

camera.setParameters(params);

يحتوي العنصر Camera.Area على مَعلمتَي بيانات: عنصر Rect لتحديد منطقة ضمن مجال رؤية الكاميرا وقيمة الوزن التي تخبر الكاميرا بمستوى الأهمية الذي يجب أن تحصل عليه هذه المنطقة في حسابات قياس الضوء أو التركيز.

يصف الحقل Rect في عنصر Camera.Area شكلاً مستطيلاً تم تعيينه على شبكة وحدات تبلغ مساحتها 2000 × 2000. تمثّل الإحداثيات ‎-1000, -1000 الزاوية العلوية اليمنى من صورة الكاميرا، وتمثّل الإحداثيات 1000, 1000 الزاوية السفلية اليسرى من صورة الكاميرا، كما هو موضّح في الرسم التوضيحي أدناه.

الشكل 1: توضّح الخطوط الحمراء نظام الإحداثيات لتحديد Camera.Area ضمن معاينة الكاميرا. يعرض المربّع الأزرق الموقع الجغرافي وشكل منطقة الكاميرا مع قيم Rect‏ 333,333,667,667.

تتوافق حدود نظام الإحداثيات هذا دائمًا مع الحافة الخارجية للصورة المرئية في معاينة الكاميرا ولا تتقلص أو تتوسع مع مستوى التكبير. وبالمثل، فإن تدوير معاينة الصورة باستخدام Camera.setDisplayOrientation() لا يعيد تعيين نظام الإحداثيات.

التعرّف على الوجوه

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

ملاحظة: أثناء تشغيل ميزة "التعرّف على الوجوه"، لن يكون لـ setWhiteBalance(String) وsetFocusAreas(List<Camera.Area>) وsetMeteringAreas(List<Camera.Area>) أي تأثير.

يتطلّب استخدام ميزة التعرّف على الوجوه في تطبيق الكاميرا بضع خطوات عامة:

  • التأكّد من أنّ الجهاز يتيح ميزة "التعرّف على الوجوه"
  • إنشاء مستمع للتعرّف على الوجوه
  • إضافة مستمع التعرّف على الوجوه إلى عنصر الكاميرا
  • بدء التعرّف على الوجوه بعد المعاينة (وبعد كل إعادة تشغيل للمعاينة)

لا تتوفّر ميزة "التعرّف على الوجوه" على بعض الأجهزة. يمكنك التأكّد من أنّ هذه الميزة متاحة من خلال الاتصال بالرقم getMaxNumDetectedFaces(). يظهر مثال على عملية التحقّق هذه في نموذج الطريقة startFaceDetection() أدناه.

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

Kotlin

internal class MyFaceDetectionListener : Camera.FaceDetectionListener {

    override fun onFaceDetection(faces: Array<Camera.Face>, camera: Camera) {
        if (faces.isNotEmpty()) {
            Log.d("FaceDetection", ("face detected: ${faces.size}" +
                    " Face 1 Location X: ${faces[0].rect.centerX()}" +
                    "Y: ${faces[0].rect.centerY()}"))
        }
    }
}

Java

class MyFaceDetectionListener implements Camera.FaceDetectionListener {

    @Override
    public void onFaceDetection(Face[] faces, Camera camera) {
        if (faces.length > 0){
            Log.d("FaceDetection", "face detected: "+ faces.length +
                    " Face 1 Location X: " + faces[0].rect.centerX() +
                    "Y: " + faces[0].rect.centerY() );
        }
    }
}

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

Kotlin

camera?.setFaceDetectionListener(MyFaceDetectionListener())

Java

camera.setFaceDetectionListener(new MyFaceDetectionListener());

يجب أن يبدأ تطبيقك وظيفة التعرّف على الوجوه في كل مرة تبدأ فيها (أو تعيد تشغيل) معاينة الكاميرا. أنشئ طريقة لبدء التعرّف على الوجوه حتى تتمكّن من استدعائها حسب الحاجة، كما هو موضّح في المثال البرمجي أدناه.

Kotlin

fun startFaceDetection() {
    // Try starting Face Detection
    val params = mCamera?.parameters
    // start face detection only *after* preview has started

    params?.apply {
        if (maxNumDetectedFaces > 0) {
            // camera supports face detection, so can start it:
            mCamera?.startFaceDetection()
        }
    }
}

Java

public void startFaceDetection(){
    // Try starting Face Detection
    Camera.Parameters params = mCamera.getParameters();

    // start face detection only *after* preview has started
    if (params.getMaxNumDetectedFaces() > 0){
        // camera supports face detection, so can start it:
        mCamera.startFaceDetection();
    }
}

يجب بدء ميزة "التعرّف على الوجوه" في كل مرة تبدأ فيها (أو تعيد تشغيل) معاينة الكاميرا. إذا كنت تستخدم فئة المعاينة الموضّحة في إنشاء فئة معاينة، أضِف طريقة startFaceDetection() إلى كل من الطريقتين surfaceCreated() وsurfaceChanged() في فئة المعاينة، كما هو موضّح في الرمز النموذجي أدناه.

Kotlin

override fun surfaceCreated(holder: SurfaceHolder) {
    try {
        mCamera.setPreviewDisplay(holder)
        mCamera.startPreview()

        startFaceDetection() // start face detection feature
    } catch (e: IOException) {
        Log.d(TAG, "Error setting camera preview: ${e.message}")
    }
}

override fun surfaceChanged(holder: SurfaceHolder, format: Int, w: Int, h: Int) {
    if (holder.surface == null) {
        // preview surface does not exist
        Log.d(TAG, "holder.getSurface() == null")
        return
    }
    try {
        mCamera.stopPreview()
    } catch (e: Exception) {
        // ignore: tried to stop a non-existent preview
        Log.d(TAG, "Error stopping camera preview: ${e.message}")
    }
    try {
        mCamera.setPreviewDisplay(holder)
        mCamera.startPreview()

        startFaceDetection() // re-start face detection feature
    } catch (e: Exception) {
        // ignore: tried to stop a non-existent preview
        Log.d(TAG, "Error starting camera preview: ${e.message}")
    }
}

Java

public void surfaceCreated(SurfaceHolder holder) {
    try {
        mCamera.setPreviewDisplay(holder);
        mCamera.startPreview();

        startFaceDetection(); // start face detection feature

    } catch (IOException e) {
        Log.d(TAG, "Error setting camera preview: " + e.getMessage());
    }
}

public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {

    if (holder.getSurface() == null){
        // preview surface does not exist
        Log.d(TAG, "holder.getSurface() == null");
        return;
    }

    try {
        mCamera.stopPreview();

    } catch (Exception e){
        // ignore: tried to stop a non-existent preview
        Log.d(TAG, "Error stopping camera preview: " + e.getMessage());
    }

    try {
        mCamera.setPreviewDisplay(holder);
        mCamera.startPreview();

        startFaceDetection(); // re-start face detection feature

    } catch (Exception e){
        // ignore: tried to stop a non-existent preview
        Log.d(TAG, "Error starting camera preview: " + e.getMessage());
    }
}

ملاحظة: تذكَّر استدعاء هذه الطريقة بعد استدعاء startPreview(). لا تحاول بدء التعرّف على الوجوه في طريقة onCreate() للنشاط الرئيسي لتطبيق الكاميرا، لأن المعاينة غير متاحة في هذه المرحلة من تنفيذ التطبيق.

فيديو بالتسريع الزمني

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

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

Kotlin

mediaRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_TIME_LAPSE_HIGH))
mediaRecorder.setCaptureRate(0.1) // capture a frame every 10 seconds

Java

// Step 3: Set a CamcorderProfile (requires API Level 8 or higher)
mediaRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_TIME_LAPSE_HIGH));
...
// Step 5.5: Set the video capture rate to a low number
mediaRecorder.setCaptureRate(0.1); // capture a frame every 10 seconds

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

يوضّح نموذجا Camera2Video وHdrViewfinder استخدام واجهات برمجة التطبيقات التي تتناولها هذه الصفحة.

حقول الكاميرا التي تتطلّب إذنًا

يجب أن تحصل التطبيقات التي تعمل بنظام التشغيل Android 10 (مستوى واجهة برمجة التطبيقات 29) أو الإصدارات الأحدث على إذن CAMERA للوصول إلى قيم الحقول التالية التي تعرضها طريقة getCameraCharacteristics():

  • LENS_POSE_ROTATION
  • LENS_POSE_TRANSLATION
  • LENS_INTRINSIC_CALIBRATION
  • LENS_RADIAL_DISTORTION
  • LENS_POSE_REFERENCE
  • LENS_DISTORTION
  • LENS_INFO_HYPERFOCAL_DISTANCE
  • LENS_INFO_MINIMUM_FOCUS_DISTANCE
  • SENSOR_REFERENCE_ILLUMINANT1
  • SENSOR_REFERENCE_ILLUMINANT2
  • SENSOR_CALIBRATION_TRANSFORM1
  • SENSOR_CALIBRATION_TRANSFORM2
  • SENSOR_COLOR_TRANSFORM1
  • SENSOR_COLOR_TRANSFORM2
  • SENSOR_FORWARD_MATRIX1
  • SENSOR_FORWARD_MATRIX2

رمز نموذجي إضافي

لتنزيل نماذج التطبيقات، اطّلِع على نموذج Camera2Basic ونموذج تطبيق CameraX الرسمي.