واجهة برمجة تطبيقات الكاميرا

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

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

يُرجى الرجوع إلى المراجع ذات الصلة التالية:

الاعتبارات

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

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

الأساسيات

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

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

بيانات البيان

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

  • إذن الكاميرا - يجب أن يطلب تطبيقك إذنًا لاستخدام كاميرا الجهاز.
    <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. تقوم هذه الفصل بمعاينة الصور المباشرة من الكاميرا.
  • إنشاء تنسيق معاينة: بعد الحصول على فئة معاينة الكاميرا، أنشئ تنسيق عرض يضمّ المعاينة وعناصر التحكّم في واجهة المستخدم التي تريدها.
  • إعداد مستمعي عمليات الالتقاط: يمكنك ربط مستمعي عناصر التحكّم في الواجهة لبدء التقاط الصور أو الفيديوهات استجابةً لإجراءات المستخدم، مثل الضغط على أحد buttons.
  • التقاط الملفات وحفظها: يمكنك إعداد الرمز البرمجي لالتقاط الصور أو الفيديوهات وحفظ النتيجة.
  • إخلاء الكاميرا: بعد استخدام الكاميرا، يجب أن يُخلي تطبيقك سبيلها بشكل صحيح لاستخدامها من قِبل التطبيقات الأخرى.

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

تحذير: تذكَّر تحرير 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 كاميرات متعددة، مثل كاميرا خلفية للتصوير وكاميرا أمامية لمكالمات الفيديو. يتيح لك الإصدار 2.3 من نظام التشغيل Android (المستوى 9 من واجهة برمجة التطبيقات) والإصدارات الأحدث التحقّق من عدد الكاميرات المتاحة على الجهاز باستخدام الطريقة Camera.getNumberOfCameras().

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

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

للوصول إلى الكاميرا الأساسية، استخدِم الطريقة 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(). في حال عدم التحقّق من الاستثناءات إذا كانت الكاميرا في استخدامٍ أو غير متوفّرة، سيؤدي ذلك إلى إغلاق النظام لتطبيقك.

على الأجهزة التي تعمل بالإصدار 2.3 من نظام التشغيل Android (المستوى 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().

ملاحظة: مع طرح ميزة "وضع المتعدّد النوافذ" في الإصدار 7.0 من نظام التشغيل Android (المستوى 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>

ملاحظة: لا يلزم أن تكون معاينة الكاميرا في الوضع الأفقي. بدءًا من الإصدار 2.2 من Android (المستوى 8 لواجهة برمجة التطبيقات)، يمكنك استخدام الطريقة setDisplayOrientation() لضبط درجة دوران صورة المعاينة. لتغيير اتجاه المعاينة عندما يعيد المستخدم توجيه الهاتف، ضمن الطريقة surfaceChanged() لفئة المعاينة، أوقِف أولاً المعاينة باستخدام Camera.stopPreview() في تغيير الاتجاه، ثم ابدأ المعاينة مرة أخرى باستخدام 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. معاينة الاتصال: يمكنك إعداد معاينة لصورة الكاميرا المباشرة من خلال توصيل SurfaceView بالكاميرا باستخدام Camera.setPreviewDisplay().
  3. بدء المعاينة: اتصل برقم 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 تنسيق المعاينة لتطبيقك. استخدِم العنصر نفسه الذي حدّدته في معاينة "الاتصال".

      ملاحظة: يجب استدعاء طرق ضبط 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(). بدءًا من الإصدار Android 4.0 (المستوى 14 من واجهة برمجة التطبيقات)، لن تكون هذه المكالمة مطلوبة إلا إذا تعذّر طلب MediaRecorder.prepare().
  6. إيقاف المعاينة: عند الانتهاء من استخدام الكاميرا، يمكنك إيقاف المعاينة باستخدام Camera.stopPreview().
  7. إلغاء حجز الكاميرا: يمكنك إلغاء حجز الكاميرا حتى تتمكّن التطبيقات الأخرى من استخدامها من خلال الاتصال بالرقم 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() إلى نموذج الرمز المعروض في Configure 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): تعرض هذه الطريقة الموقع الجغرافي العادي والمشترَك والمُقترَح لحفظ الصور والفيديوهات. هذا الدليل مشترك (عام)، لذا يمكن للتطبيقات الأخرى اكتشاف الملفات المحفوظة في هذا الموقع وقراءتها وتغييرها وحذفها بسهولة. إذا ألغى المستخدم تثبيت تطبيقك، لن تتم إزالة ملفات الوسائط المحفوظة في هذا الموقع. لتجنُّب التدخل في الصور والفيديوهات الحالية للمستخدمين، عليك إنشاء دليل فرعي لملفّات وسائط تطبيقك ضمن هذا الدليل، كما هو موضّح في نموذج الرمز البرمجي أدناه. تتوفّر هذه الطريقة في Android 2.2 (المستوى 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() في الإصدار 2.2 من نظام التشغيل Android (المستوى 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. تظهر التغييرات التي تطرأ علىparametres عادةً للمستخدم على الفور في معاينة الكاميرا للتطبيق. من ناحية البرامج، قد تستغرق تغييرات المَعلمات عدة لقطات لتسريدها فعليًا لأنّه يتم معالجة التعليمات الجديدة في برمجيات الكاميرا ثم إرسال بيانات الصورة المعدَّلة.

ملاحظة مهمة: لا يمكن تغيير بعض ميزات الكاميرا حسب الطلب. وعلى وجه الخصوص، يتطلب تغيير حجم معاينة الكاميرا أو اتجاهها إيقاف المعاينة أولاً، وتغيير حجم المعاينة، ثم إعادة تشغيلها. بدءًا من 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 x ‏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 الرسمي.