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

يتيح إطار عمل 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 (المستوى 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().

ملاحظة: بدءًا من الإصدار 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. ضبط تنسيق إخراج الفيديو وترميزه في 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() من الطريقة الموضحة في المثال في القسم Saving Media Files.
      6. setPreviewDisplay() - حدِّد عنصر تنسيق معاينة SurfaceView الخاص بتطبيقك. استخدِم العنصر نفسه الذي حدّدته في Connect Preview (معاينة الربط).

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

    3. إعداد MediaRecorder: عليك إعداد MediaRecorder مع إعدادات الضبط المتوفّرة من خلال طلب الإذن MediaRecorder.prepare().
    4. بدء MediaRecorder: لبدء تسجيل الفيديو من خلال الاتصال بالرقم MediaRecorder.start()
  5. إيقاف تسجيل الفيديو - اطلب الطرق التالية بالترتيب، لإكمال تسجيل الفيديو بنجاح:
    1. إيقاف MediaRecorder: لإيقاف تسجيل الفيديو من خلال الاتصال بـ MediaRecorder.stop()
    2. إعادة ضبط MediaRecorder: يمكنك إزالة إعدادات الضبط من المسجّلة الذكية بشكل اختياري من خلال طلب الإجراء MediaRecorder.reset().
    3. إصدار MediaRecorder - حرر MediaRecorder من خلال طلب MediaRecorder.release().
    4. قفل الكاميرا: عليك قفل الكاميرا بحيث يمكن لجلسات MediaRecorder المستقبلية استخدامها عن طريق الاتصال بالرقم Camera.lock(). بدءًا من الإصدار 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() إلى نموذج الرمز المعروض في تكوين 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. عادةً ما تظهر التغييرات التي يتم إجراؤها على المعلَمات للمستخدم مباشرةً في معاينة الكاميرا في التطبيق. من جانب البرامج، قد تستغرق تغييرات المعلمات عدة إطارات لتصبح سارية المفعول عندما يعالج جهاز الكاميرا التعليمات الجديدة ثم يرسل بيانات صور محدّثة.

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

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

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

يتوفر مخطط سريع حول كيفية تنفيذ هذه الميزات في الأقسام التالية.

قياس الأداء والمجالات التي يتم التركيز عليها

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

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

Kotlin

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

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

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

Java

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

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

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

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

camera.setParameters(params);

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

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

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

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

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

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

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

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

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

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

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

Kotlin

internal class MyFaceDetectionListener : Camera.FaceDetectionListener {

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

Java

class MyFaceDetectionListener implements Camera.FaceDetectionListener {

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

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

Kotlin

camera?.setFaceDetectionListener(MyFaceDetectionListener())

Java

camera.setFaceDetectionListener(new MyFaceDetectionListener());

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

Kotlin

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

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

Java

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

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

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

Kotlin

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

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

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

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

Java

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

        startFaceDetection(); // start face detection feature

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

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

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

    try {
        mCamera.stopPreview();

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

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

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

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

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

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

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

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

Kotlin

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

Java

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

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

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

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

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

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

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

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