ממשק API של מצלמה

Android framework כולל תמיכה במגוון מצלמות ותכונות של המצלמה הזמינות כדי לצלם תמונות וסרטונים באפליקציות שלכם. במסמך הזה עוסקת גישה מהירה ופשוטה לצילום תמונות וסרטונים, ומתארת גישה מתקדמת ליצירת ולהתאים אישית את חוויית המצלמה למשתמשים שלכם.

הערה: בדף הזה מתואר Camera שהוצא משימוש. מומלץ להשתמש ספריית Jetpack מצלמהX או, במקרים ספציפיים, camera2, בכיתה. גם CameraX וגם Camera2 פועלים ב-Android 5.0 (רמת API 21) וב- גבוהה יותר.

כדאי לעיין במקורות המידע שקשורים לנושא:

שיקולים

לפני שמפעילים את האפליקציה לשימוש במצלמות במכשירי Android, צריך לשקול כמה דברים שאלות על האופן שבו האפליקציה מתכוונת להשתמש בתכונת החומרה הזו.

  • דרישות המצלמה – האם השימוש במצלמה כל כך חשוב לעסק שלך אפליקציה שאינך רוצה שהאפליקציה שלך תותקן במכשיר שאין בו מצלמה? אם כן, עליך להצהיר על דרישת המצלמה מניפסט.
  • תמונה מהירה או מצלמה מותאמת אישית - איך האפליקציה שלך תשתמש מצלמה? האם אתם רק רוצים לצלם תמונה קצרה או קליפ קצר, או שהבקשה שלכם לספק דרך חדשה להשתמש במצלמות? כדי לצלם קליפ או קליפ מהיר, כדאי לבדוק שימוש באפליקציות מצלמה קיימות. כדי לפתח תכונת מצלמה מותאמת אישית, צריך לבדוק בקטע בנייה של אפליקציית מצלמה.
  • הדרישות לשימוש בשירותים שפועלים בחזית – מתי האפליקציה מקיימת אינטראקציה עם המצלמה? ב-Android 9 (רמת API 28) ואילך, אפליקציות שפועלות לרקע אין גישה למצלמה. לכן, צריך להשתמש במצלמה כשהאפליקציה בחזית או כחלק שירות שפועל בחזית.
  • אחסון – האם התמונות או הסרטונים שהאפליקציה יוצרת נועדו להיות גלויים לאפליקציה שלך או משותפים רק כך שאפליקציות אחרות כמו 'גלריה' או המדיה והאפליקציות החברתיות יכולות להשתמש בהם? האם ברצונך שהתמונות והסרטונים יהיו זמינים גם אם האפליקציה הוסרה? עיין בקטע שמירת קובצי מדיה כדי כדי ללמוד איך ליישם את האפשרויות האלה.

העקרונות הבסיסיים

של Android framework תומך בצילום תמונות וסרטונים באמצעות API של android.hardware.camera2 או מצלמה Intent. אלה הפרטים הרלוונטיים מחלקות:

android.hardware.camera2
החבילה הזו היא ה-API הראשי לשליטה במצלמות של המכשירים. אפשר להשתמש כדי לקחת תמונות או סרטונים כאשר בונים אפליקציית מצלמה.
Camera
המחלקה הזו היא ה-API הישן יותר שהוצא משימוש לשליטה במצלמות של מכשירים.
SurfaceView
הכיתה הזו משמשת להצגת תצוגה מקדימה בזמן אמת של המצלמה למשתמש.
MediaRecorder
הכיתה הזו משמשת להקלטת סרטונים במצלמה.
Intent
ניתן להשתמש בפעולת Intent מסוג MediaStore.ACTION_IMAGE_CAPTURE או MediaStore.ACTION_VIDEO_CAPTURE כדי לצלם תמונות או סרטונים ללא ישירות באמצעות האובייקט Camera.

הצהרות מניפסט

לפני שמתחילים בפיתוח של האפליקציה עם Camera API, צריך לוודא במניפסט יש את ההצהרות המתאימות שיאפשרו שימוש בחומרה של מצלמה שקשורות ל-AI גנרטיבי.

  • הרשאת גישה למצלמה – האפליקציה חייבת לבקש הרשאה כדי להשתמש במכשיר מצלמה.
    <uses-permission android:name="android.permission.CAMERA" />
    

    הערה: אם אתם משתמשים במצלמה עד להפעיל אפליקציית מצלמה קיימת, לאפליקציה שלך אין צורך לבקש את ההרשאה הזו.

  • תכונות המצלמה – האפליקציה צריכה להצהיר גם על שימוש בתכונות המצלמה, לדוגמה:
    <uses-feature android:name="android.hardware.camera" />
    

    רשימה של תכונות המצלמה מופיעה במניפסט פיצ'רים הפניה.

    הוספת תכונות מצלמה למניפסט גורמת ל-Google Play למנוע מהאפליקציה שלך מותקנות במכשירים שלא כוללים מצלמה או שלא תומכים בתכונות המצלמה להגדיר. למידע נוסף על השימוש בסינון מבוסס-תכונות ב-Google Play, ראו Google סינון מבוסס הפעלה ופיצ'רים

    אם האפליקציה יכולה להשתמש בתכונה של מצלמה או מצלמה לפעולה תקינה, אבל לא נדרש, יש לציין זאת במניפסט על ידי הכללת המאפיין android:required והגדרתו ל-false:

    <uses-feature android:name="android.hardware.camera" android:required="false" />
    
  • הרשאת אחסון – האפליקציה יכולה לשמור תמונות או סרטונים האחסון החיצוני של המכשיר (כרטיס SD) אם הוא מיועד ל-Android 10 (רמת API 29) או נמוך יותר ומציין את הפרטים הבאים במניפסט.
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    
  • הרשאה להקלטת אודיו – כדי להקליט אודיו עם צילום וידאו, צריך: האפליקציה חייבת לבקש את ההרשאה להקלטת אודיו.
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
    
  • הרשאת מיקום – אם האפליקציה מתייגת תמונות עם נתוני מיקום GPS, עליך לבקש את ACCESS_FINE_LOCATION הרשאה. לתשומת ליבכם: אם האפליקציה מטרגטת את Android 5.0 (רמת API 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 API. לאפליקציות מצלמה חדשות או מתקדמות, ה-API החדש יותר של 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 (API Level 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(). לא ניתן לבדוק חריגות אם המצלמה נמצאת השימוש או שאינו קיים יגרום להשבתת האפליקציה על ידי המערכת.

במכשירים עם Android מגרסה 2.3 (רמת API 9) ואילך, אפשר לגשת למצלמות ספציפיות באמצעות Camera.open(int) הקוד לדוגמה שלמעלה יקבל גישה במצלמה האחורית הראשונה במכשיר שיש בו יותר ממצלמה אחת.

תכונות המצלמה נבדקות

אחרי שמקבלים גישה למצלמה, אפשר לקבל מידע נוסף על היכולות שלה באמצעות ה-method Camera.getParameters() ובודקים את הוחזר אובייקט Camera.Parameters ליכולות נתמכות. בזמן השימוש ברמה 9 ואילך של API, צריך להשתמש ב-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(). לא להגדיר ערכים שרירותיים ב-method setPreviewSize().

הערה: עם השקת המודל התכונה 'ריבוי חלונות' ב-Android 7.0 (API ברמה 24) ואילך, לא ניתן מניח שיחס הגובה-רוחב של התצוגה המקדימה זהה לפעילות שלך גם אחרי קריאה אל setDisplayOrientation(). בהתאם לגודל החלון וליחס הגובה-רוחב, יכול להיות שתצטרכו להגדיר פורמט רחב את התצוגה המקדימה של המצלמה בפריסה לאורך, או להפך, באמצעות פריסת letterbox.

מיקום תצוגה מקדימה בפריסה

יש למקם בכיתה של תצוגה מקדימה של מצלמה, כמו הדוגמה שהוצגה בקטע הקודם, פריסת פעילות יחד עם פקדים אחרים בממשק המשתמש לצילום תמונה או סרטון. הזה כיצד לבנות פריסה בסיסית ופעילות עבור התצוגה המקדימה.

קוד הפריסה הבא מספק תצוגה בסיסית מאוד שניתן להשתמש בה להצגת מצלמה תצוגה מקדימה. בדוגמה הזו, הרכיב 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 (רמת API 8), אפשר להשתמש ב-method 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);
    }
}

הערה: ה-method 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() כאשר סיימתי להשתמש בו! במאמר שחרור המצלמה מוסבר איך לשחרר את המצלמה.

צילום סרטונים

כדי לצלם סרטון באמצעות framework של Android, נדרש ניהול קפדני של האובייקט Camera ותאם עם MediaRecorder בכיתה. כשמקליטים וידאו עם Camera, צריך לנהל את השיחות Camera.lock() ו-Camera.unlock() כדי לתת ל-MediaRecorder גישה לחומרה של המצלמה. בנוסף לשיחות Camera.open() ו-Camera.release().

הערה: החל מ-Android 4.0 (רמת API 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 (רמת API 8) ול- גבוהה יותר, משתמשים בשיטה MediaRecorder.setProfile ומקבלים מופע של פרופיל באמצעות CamcorderProfile.get(). לגרסאות Android שקודמות ל- 2.2, יש להגדיר את הפרמטרים של הקידוד והפורמט של פלט הווידאו:
        1. setOutputFormat() – קבוצה את פורמט הפלט, מציינים את הגדרת ברירת המחדל או MediaRecorder.OutputFormat.MPEG_4.
        2. setAudioEncoder() – קבוצה סוג קידוד הצליל, מציינים את הגדרת ברירת המחדל או MediaRecorder.AudioEncoder.AMR_NB.
        3. setVideoEncoder() – קבוצה סוג קידוד הווידאו, מציינים את הגדרת ברירת המחדל או MediaRecorder.VideoEncoder.MPEG_4_SP.
      5. setOutputFile() – הגדרת קובץ הפלט, משתמשים ב-getOutputMediaFile(MEDIA_TYPE_VIDEO).toString() מהדוגמה בקטע שמירת קובצי מדיה.
      6. setPreviewDisplay() - ציון הרכיב SurfaceView של פריסת התצוגה המקדימה עבור את האפליקציה שלך. משתמשים באותו אובייקט שציינתם בתכונה Connect Preview (תצוגה מקדימה של קישור).

      זהירות: צריך לקרוא ל-MediaRecorder שיטות ההגדרה האלה בסדר הזה, אחרת, ייתקלו בשגיאות וההקלטה תיכשל.

    3. הכנת MediaRecorder – הכנת MediaRecorder באמצעות הגדרות התצורה שסופקו באמצעות קריאה אל MediaRecorder.prepare().
    4. הפעלת MediaRecorder – התחלת צילום הסרטון על ידי התקשרות אל MediaRecorder.start().
  5. הפסקת ההקלטה של הסרטון – קוראים לשיטות הבאות לפי הסדר כדי להשלים בהצלחה הקלטת סרטון:
    1. Stop MediaRecorder – הפסקת צילום הסרטון על ידי התקשרות אל MediaRecorder.stop().
    2. איפוס MediaRecorder – אופציונלי, הסרה של הגדרות התצורה מ: באמצעות מכשיר ההקלטה, מבצעים קריאה אל MediaRecorder.reset().
    3. פרסום MediaRecorder – פרסום של MediaRecorder באמצעות הטלפון MediaRecorder.release().
    4. נעילת המצלמה – ניתן לנעול את המצלמה כדי שניתן יהיה להשתמש בה בסשנים עתידיים של MediaRecorder באמצעות התקשרות אל Camera.lock(). החל מ-Android 4.0 (רמת API 14), הקריאה הזו לא נדרשת אלא אם הקריאה ל-MediaRecorder.prepare() נכשלה.
  6. להפסיק את התצוגה המקדימה – בסיום השימוש במצלמה, מפסיקים תצוגה מקדימה באמצעות Camera.stopPreview().
  7. שחרור המצלמה – שחרור המצלמה כדי שאפליקציות אחרות יוכלו להשתמש באמצעות קריאה אל Camera.release().

הערה: אפשר להשתמש ב-MediaRecorder בלי ליצור קודם תצוגה מקדימה של המצלמה, ולדלג על השלבים הראשונים בתהליך הזה. אבל, לפעמים מכיוון שמשתמשים בדרך כלל מעדיפים לראות תצוגה מקדימה לפני שהם מתחילים הקלטה, התהליך הזה לא שעליהם אנחנו מדברים כאן.

טיפ: אם האפליקציה שלכם משמשת בדרך כלל להקלטת וידאו, צריך להגדיר setRecordingHint(boolean) עד true לפני התחלת תצוגה מקדימה. ההגדרה הזו יכולה לעזור לכם לקצר את משך הזמן שנדרש כדי להתחיל בצילום.

הגדרת MediaRecorder

כשמשתמשים בכיתה MediaRecorder להקלטת סרטונים, צריך לבצע בסדר ספציפי, ואז קריאה ל-method 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;
}

לפני Android 2.2 (רמת API 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() ה-method מתייחס לקוד לדוגמה שמוצג בהגדרת 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;
        }
    }
}

זהירות: אם האפליקציה לא מפרסמת כמו שצריך את קובץ ה-CSV כל הניסיונות הבאים לגשת למצלמה, כולל אלה של האפליקציה שלך, נכשלים ועלולים לגרום להשבתת האפליקציות שלכם או אחרות.

מתבצעת שמירה של קובצי מדיה

יש לשמור קובצי מדיה שנוצרו על ידי משתמשים, כמו תמונות וסרטונים, באחסון החיצוני של המכשיר ספריית אחסון (כרטיס SD) כדי לחסוך מקום במערכת ולאפשר למשתמשים לגשת לקבצים האלה בלי המכשיר. יש הרבה מיקומים אפשריים של ספריות לשמירת קובצי מדיה במכשיר, עם זאת, יש רק שני מיקומים רגילים שכדאי להכיר כמפתח:

  • Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) – השיטה הזו מחזירה את השיטה הרגילה, המשותפת והמומלצת המיקום לשמירת תמונות וסרטונים. הספרייה הזו משותפת (ציבורית), ולכן אפליקציות אחרות לגלות, לקרוא, לשנות ולמחוק בקלות קבצים שנשמרו במיקום זה. אם הבקשה שלכם ההתקנה הוסרה על ידי המשתמש, קובצי מדיה שנשמרו במיקום הזה לא יוסרו. כדי להימנע כדי להפריע למשתמשים תמונות וסרטונים קיימים, עליך ליצור ספריית משנה של קובצי המדיה של האפליקציה בספרייה הזו, כפי שמוצג בדוגמת הקוד הבאה. השיטה הזאת זמינה ב-Android 2.2 (API ברמה 8), למידע על קריאות מקבילות בגרסאות קודמות של ה-API, אפשר לעיין בשמירת קבצים משותפים.
  • 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 (רמת API 8) או גבוהה יותר. אם הגדרת טירגוט למכשירים עם גרסאות קודמות של Android, צריך להשתמש ב-Environment.getExternalStorageDirectory() במקום זאת. למידע נוסף, ראה שמירת קבצים משותפים.

כדי שה-URI תומך בפרופילי עבודה, תחילה ממירים את ה-URI של הקובץ ל-URI של תוכן. לאחר מכן, מוסיפים את ה-URI של התוכן EXTRA_OUTPUT של Intent.

למידע נוסף על שמירת קבצים במכשיר Android, ראו אחסון נתונים.

תכונות מצלמה

מערכת Android תומכת במגוון רחב של תכונות מצלמה שניתן לשלוט בהן באמצעות אפליקציית המצלמה, כגון פורמט תמונה, מצב Flash, הגדרות מיקוד ועוד. בקטע הזה מופיעה רשימה של תכונות של המצלמה, ונדון בקצרה על אופן השימוש בהן. אפשר לגשת לרוב התכונות של המצלמה ולהגדיר אותן באמצעות האובייקט Camera.Parameters. אבל יש כמה מודלים תכונות חשובות שדורשות יותר מהגדרות פשוטות ב-Camera.Parameters. התכונות האלה מפורטות בקטעים הבאים:

למידע כללי על השימוש בתכונות שנשלטות דרך Camera.Parameters, אפשר לעיין במאמר שימוש במצלמה תכונות. למידע מפורט יותר על השימוש בתכונות שנשלטות באמצעות אובייקט של פרמטרים של מצלמה, לוחצים על הקישורים ברשימת התכונות שלמטה אל הפניית ה-API התיעוד.

טבלה 1. תכונות נפוצות של המצלמה ממוינות לפי רמת ה-API של Android, שבה שהוצגו בה.

תכונה רמת API תיאור
זיהוי פנים 14 זיהוי פנים אנושיות בתמונה ושימוש בהן לצורך מיקוד, מדידה ולבן יתרה
אזורי מדידה 14 צריך לציין אזור אחד או יותר בתמונה לחישוב איזון לבן
תחומי העניין 14 הגדרת אזור אחד או יותר בתמונה שישמשו למיקוד
White Balance Lock 14 איך מפסיקים או מתחילים התאמות אוטומטיות של איזון לבן
Exposure Lock 14 איך מפסיקים או מפעילים התאמות חשיפה אוטומטיות
Video Snapshot 14 צלמו תמונה בזמן צילום הסרטון (שמירת פריימים)
סרטון Time Lapse 11 צילום פריימים עם עיכובים מוגדרים כדי לצלם סרטון Time Lapse
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 מספק method 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 (API רמה 14) ניתן לשנות את הכיוון של התצוגה המקדימה מבלי להפעיל מחדש את התצוגה המקדימה.

כדי להטמיע תכונות אחרות של המצלמה, צריך להוסיף קוד, כולל:

  • אזורי מדידה והתמקדות
  • איתור פנים
  • סרטון בהילוך מהיר

בהמשך מוסבר בהרחבה איך אפשר ליישם את התכונות האלה.

אזורי מדידה והתמקדות

בתרחישי צילום מסוימים, מיקוד אוטומטי ומדידת אור לא יניבו את את התוצאות הרצויות. החל מ-Android 4.0 (רמת API 14), אפליקציית המצלמה יכולה לספק אמצעי בקרה נוספים שמאפשרים לאפליקציה או למשתמשים לציין אזורים בתמונה שישמשו את ההגדרות של המיקוד או רמת האור, ומעבירים את הערכים האלה לחומרת המצלמה לצורך צילום תמונות או סרטונים.

תחומי המדידה והמיקוד פועלים באופן דומה מאוד לתכונות אחרות של המצלמה, בכך שאתם שולטים באמצעות methods באובייקט 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 x 2,000. הקואורדינטות -1000, -1000 שמייצג את הפינה השמאלית העליונה של תמונת המצלמה, והקואורדינטות 1000 ו-1000 מייצגות את בפינה הימנית התחתונה של תמונת המצלמה, כמו באיור שלמטה.

איור 1. הקווים האדומים ממחישים את מערכת הקואורדינטות לציון Camera.Area בתצוגה המקדימה של המצלמה. התיבה הכחולה מציגה את המיקום צורה של אזור מצלמה עם ערכי Rect 333,333,667,667.

הגבולות של מערכת הקואורדינטות הזו תמיד תואמים לקצה החיצוני של התמונה שניתן לראות בה את התצוגה המקדימה של המצלמה ואין לכווץ או להרחיב באמצעות רמת הזום. באופן דומה, סיבוב התמונה תצוגה מקדימה באמצעות Camera.setDisplayOrientation() לא ממופה מחדש את מערכת הקואורדינטות.

איתור פנים

בתמונות עם אנשים, פנים הן בדרך כלל החלק החשוב ביותר בתמונה, וגם צריך לשמש לקביעת המיקוד וגם לקביעת איזון לבן בעת צילום תמונה. מערכת ההפעלה Android 4.0 (API ברמה 14) מספק ממשקי API לזיהוי פנים ולחישוב הגדרות תמונה באמצעות טכנולוגיה לזיהוי פנים.

הערה: כשתכונת זיהוי הפנים פועלת, 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() של הפעילות העיקרית של אפליקציית המצלמה, כי התצוגה המקדימה כבר לא זמינה בשלב הזה בביצוע של האפליקציה.

סרטון בהילוך מהיר

סרטון Time Lapse מאפשר למשתמשים ליצור קליפים שמשלבים תמונות שצולמו כמה שניות או דקות בהפרש של כמה דקות. התכונה הזו משתמשת ב-MediaRecorder כדי להקליט את התמונות למשך זמן מסוים ברצף.

כדי לצלם סרטון Time Lapse באמצעות MediaRecorder, צריך להגדיר את של מכשיר ההקלטה, כאילו אתם מקליטים סרטון רגיל, ומגדירים את הפריימים לשנייה מספר נמוך ושימוש באחת מההגדרות של איכות Time Lapse, כמו שאפשר לראות בדוגמת הקוד שלמטה.

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 דוגמאות נוספות ממחישות את השימוש בממשקי ה-API שמפורטים בדף הזה.

שדות במצלמה שנדרשת להם הרשאה

אפליקציות שפועלות עם Android 10 (API ברמה 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.