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

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

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

الاعتبارات

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

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

الأساسيات

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

android.hardware.camera2
هذه الحزمة هي واجهة برمجة التطبيقات الأساسية للتحكّم في كاميرات الأجهزة. يمكن استخدامها لالتقاط صور أو مقاطع فيديو عند إنشاء تطبيق كاميرا.
Camera
هذه الفئة هي واجهة برمجة التطبيقات القديمة والمتوقفة نهائيًا للتحكّم في كاميرات الأجهزة.
SurfaceView
يُستخدم هذا الصف لتقديم معاينة كاميرا مباشرة للمستخدم.
MediaRecorder
يستخدم هذا الصف لتسجيل فيديو من الكاميرا.
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) إذا كان يستهدف Android 10 (المستوى 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 (إلا إذا كنت تستخدم نية الوصول إلى الكاميرا).

للوصول إلى الكاميرا الأساسية، استخدِم طريقة 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().

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

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

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

  1. فتح الكاميرا: استخدِم Camera.open() للحصول على مثيل لكائن الكاميرا.
  2. ربط المعاينة: يمكنك إعداد معاينة مباشرة لصورة الكاميرا من خلال توصيل SurfaceView بالكاميرا باستخدام Camera.setPreviewDisplay().
  3. بدء المعاينة: يمكنك طلب إجراء مكالمة مع Camera.startPreview() لبدء عرض صور الكاميرا المباشرة.
  4. بدء تسجيل الفيديو: يجب إكمال الخطوات التالية من أجل تسجيل الفيديو بنجاح:
    1. فتح قفل الكاميرا - افتح قفل الكاميرا لاستخدامها بحلول MediaRecorder من خلال الاتصال على Camera.unlock().
    2. Configure MediaRecorder (إعداد MediaRecorder): يمكنك الاتصال بطُرق MediaRecorder التالية بهذا الترتيب. لمزيد من المعلومات، اطّلِع على المستندات المرجعية الخاصة بـ MediaRecorder.
      1. setCamera() - اضبط الكاميرا على استخدامها لالتقاط الفيديو، واستخدِم النسخة الحالية لتطبيقك من Camera.
      2. setAudioSource() - اضبط مصدر الصوت واستخدِم "MediaRecorder.AudioSource.CAMCORDER"
      3. setVideoSource() - اضبط مصدر الفيديو واستخدِم MediaRecorder.VideoSource.CAMERA
      4. ضبط تنسيق إخراج الفيديو وترميزه بالنسبة إلى نظام التشغيل Android 2.2 (المستوى 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. Start MediaRecorder: يمكنك بدء تسجيل الفيديو عن طريق الاتصال بـ MediaRecorder.start().
  5. إيقاف تسجيل الفيديو: عليك استدعاء الطرق التالية بالترتيب لإكمال تسجيل الفيديو بنجاح:
    1. Stop MediaRecorder: لإيقاف تسجيل الفيديو عن طريق الاتصال بـ MediaRecorder.stop().
    2. إعادة ضبط MediaRecorder - يمكنك اختياريًا إزالة إعدادات الضبط من المسجّلة عن طريق استدعاء MediaRecorder.reset().
    3. Release MediaRecorder - حرر MediaRecorder من خلال الاتصال بـ MediaRecorder.release().
    4. قفل الكاميرا - يمكنك قفل الكاميرا حتى تتمكن جلسات MediaRecorder المستقبلية من استخدامها عن طريق الاتصال بـ Camera.lock(). بدءًا من الإصدار 4.0 من نظام التشغيل Android (المستوى 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() إلى نموذج الرمز الموضّح في إعداد 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() في Android 2.2 (المستوى 8 لواجهة برمجة التطبيقات) أو الإصدارات الأحدث. في حال استهداف أجهزة تعمل بإصدارات سابقة من نظام التشغيل Android، استخدِم Environment.getExternalStorageDirectory() بدلاً من ذلك. لمزيد من المعلومات، راجع حفظ الملفات المشتركة.

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

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

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

  • قياس حصة القراءة المجانية ومناطق التركيز
  • التعرّف على الوجوه
  • فيديو في وضع "التسريع الزمني"

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

قياس حصة القراءة المجانية ومناطق التركيز

في بعض السيناريوهات المتعلقة بالتصوير الفوتوغرافي، قد لا يؤدي التركيز التلقائي وقياس الضوء إلى النتائج المطلوبة. بدءًا من نظام التشغيل 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 شكلاً مستطيلاً تمّ ربطه على شبكة وحدة قياس 2,000 × 2,000. تمثل الإحداثيات -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 وتشغيله، يُرجى الاطّلاع على التقاط الفيديوهات.

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

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

يجب أن تحصل التطبيقات التي تعمل بالإصدار 10 من نظام التشغيل 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

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

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