מסגרת Android כוללת תמיכה במצלמות שונות ובתכונות מצלמה שזמינות במכשירים, ומאפשרת לכם לצלם תמונות וסרטונים באפליקציות שלכם. במסמך הזה נסביר על גישה פשוטה ומהירה לצילום תמונות וסרטונים, וגם על גישה מתקדמת ליצירת חוויות מצלמה מותאמות אישית למשתמשים.
הערה:
בדף הזה מתואר
Camera
הסיווג, שהוצא משימוש. מומלץ להשתמש בספריית Jetpack CameraX או, בתרחישי שימוש ספציפיים, במחלקה camera2
. CameraX ו-Camera2 פועלות ב-Android מגרסה 5.0 (רמת API 21) ומעלה.
כדאי לעיין במקורות המידע הבאים שקשורים לנושא:
שיקולים
לפני שמפעילים את האפשרות להשתמש במצלמות במכשירי Android באפליקציה, כדאי לשקול כמה שאלות לגבי האופן שבו האפליקציה מתכוונת להשתמש בתכונת החומרה הזו.
- דרישה לגבי מצלמה – האם השימוש במצלמה כל כך חשוב לאפליקציה שלך, שאתה לא רוצה שהאפליקציה תותקן במכשיר שאין בו מצלמה? אם כן, צריך להצהיר על דרישת המצלמה במניפסט.
- Quick Picture או Customized Camera – איך האפליקציה תשתמש במצלמה? האם אתם רוצים רק לצלם תמונה או קליפ וידאו במהירות, או שהאפליקציה שלכם תספק דרך חדשה להשתמש במצלמות? כדי לצלם תמונה או סרטון במהירות, אפשר להשתמש באפליקציות מצלמה קיימות. כדי לפתח תכונה מותאמת אישית למצלמה, אפשר לעיין בקטע יצירת אפליקציית מצלמה.
- הדרישה לגבי שירותים שפועלים בחזית – מתי האפליקציה שלך יוצרת אינטראקציה עם המצלמה? ב-Android 9 (רמת API 28) ואילך, לאפליקציות שפועלות ברקע אין גישה למצלמה. לכן, צריך להשתמש במצלמה כשהאפליקציה פועלת בחזית או כחלק משירות שפועל בחזית.
- אחסון – האם התמונות או הסרטונים שהאפליקציה שלך יוצרת מיועדים להיות גלויים רק לאפליקציה שלך, או שהם משותפים כך שאפליקציות אחרות כמו הגלריה או אפליקציות אחרות של מדיה ורשתות חברתיות יוכלו להשתמש בהם? האם רוצים שהתמונות והסרטונים יהיו זמינים גם אם האפליקציה תוסר? בקטע שמירת קובצי מדיה מוסבר איך מטמיעים את האפשרויות האלה.
העקרונות הבסיסיים
מסגרת Android תומכת בצילום תמונות וסרטונים באמצעות android.hardware.camera2
API או מצלמה Intent
. אלה המחלקות הרלוונטיות:
android.hardware.camera2
- החבילה הזו היא ה-API העיקרי לשליטה במצלמות של המכשיר. אפשר להשתמש בו כדי לצלם תמונות או סרטונים כשמפתחים אפליקציית מצלמה.
Camera
- Class הזה הוא API ישן יותר שיצא משימוש לשליטה במצלמות של מכשירים.
SurfaceView
- This class is used to present a live camera preview to the user.
MediaRecorder
- This class is used to record video from the camera.
Intent
- אפשר להשתמש בסוג פעולת כוונה
MediaStore.ACTION_IMAGE_CAPTURE
אוMediaStore.ACTION_VIDEO_CAPTURE
כדי לצלם תמונות או סרטונים בלי להשתמש ישירות באובייקטCamera
.
הצהרות במניפסט
לפני שמתחילים לפתח את האפליקציה באמצעות Camera API, צריך לוודא שבקובץ המניפסט יש את ההצהרות המתאימות שמאפשרות שימוש בחומרת המצלמה ובתכונות קשורות אחרות.
- הרשאת גישה למצלמה – האפליקציה צריכה לבקש הרשאה להשתמש במצלמה של המכשיר.
<uses-permission android:name="android.permission.CAMERA" />
הערה: אם אתם משתמשים במצלמה על ידי הפעלה של אפליקציית מצלמה קיימת, לא צריך לבקש את ההרשאה הזו באפליקציה שלכם.
- תכונות המצלמה – האפליקציה צריכה גם להצהיר על שימוש בתכונות המצלמה, למשל:
<uses-feature android:name="android.hardware.camera" />
רשימת התכונות של המצלמה מופיעה בחומר העזר בנושא תכונות.
אם מוסיפים תכונות של מצלמה למניפסט, מערכת Google Play מונעת את ההתקנה של האפליקציה במכשירים שלא כוללים מצלמה או שלא תומכים בתכונות המצלמה שציינתם. מידע נוסף על שימוש בסינון לפי תכונות ב-Google Play זמין במאמר Google Play וסינון לפי תכונות.
אם האפליקציה יכולה להשתמש במצלמה או בתכונה של המצלמה כדי לפעול בצורה תקינה, אבל היא לא דורשת את זה, צריך לציין את זה בקובץ המניפסט באמצעות המאפיין
android:required
ולהגדיר אותו לערךfalse
:<uses-feature android:name="android.hardware.camera" android:required="false" />
- הרשאת אחסון – האפליקציה יכולה לשמור תמונות או סרטונים באחסון החיצוני של המכשיר (כרטיס SD) אם היא מיועדת ל-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, שהוצאה משימוש. לשימוש באפליקציות חדשות או מתקדמות של מצלמה, מומלץ להשתמש ב-android.hardware.camera2
API החדש יותר.
אלה השלבים הכלליים ליצירת ממשק מצלמה מותאם אישית לאפליקציה:
- זיהוי המצלמה וגישה אליה – יצירת קוד לבדיקה אם יש מצלמות ולבקשת גישה.
- יצירת מחלקה לתצוגה מקדימה – יוצרים מחלקה לתצוגה מקדימה של המצלמה שמרחיבה את
SurfaceView
ומטמיעה את הממשקSurfaceHolder
. בשיעור הזה מוצגת תצוגה מקדימה של התמונות בשידור חי מהמצלמה. - בניית פריסת תצוגה מקדימה – אחרי שיש לכם את המחלקה של התצוגה המקדימה של המצלמה, יוצרים פריסת תצוגה שכוללת את התצוגה המקדימה ואת פקדי ממשק המשתמש שרוצים.
- הגדרת מאזינים ללכידה – חיבור מאזינים לפקדים של הממשק כדי להתחיל לכידת תמונות או סרטונים בתגובה לפעולות של המשתמש, כמו לחיצה על לחצן.
- צילום ושמירה של קבצים – הגדרת הקוד לצילום תמונות או סרטונים ושמירת הפלט.
- שחרור המצלמה – אחרי השימוש במצלמה, האפליקציה צריכה לשחרר אותה בצורה תקינה כדי שאפליקציות אחרות יוכלו להשתמש בה.
חומרת המצלמה היא משאב משותף שצריך לנהל אותו בקפידה כדי שהאפליקציה שלכם לא תתנגש עם אפליקציות אחרות שאולי גם רוצות להשתמש בה. בסעיפים הבאים מוסבר איך לזהות חומרה של מצלמה, איך לבקש גישה למצלמה, איך לצלם תמונות או סרטונים ואיך לשחרר את המצלמה כשהאפליקציה סיימה להשתמש בה.
זהירות: חשוב לזכור לשחרר את האובייקט על ידי קריאה ל-Camera.release()
כשהאפליקציה מסיימת להשתמש בו.Camera
אם האפליקציה לא משחררת את המצלמה כמו שצריך, כל הניסיונות הבאים לגשת למצלמה, כולל אלה של האפליקציה שלכם, ייכשלו ויכולים לגרום לסגירה של האפליקציה שלכם או של אפליקציות אחרות.
זיהוי חומרת המצלמה
אם האפליקציה לא דורשת מצלמה באופן ספציפי באמצעות הצהרה במניפסט, כדאי לבדוק אם מצלמה זמינה בזמן הריצה. כדי לבצע את הבדיקה הזו, משתמשים בשיטה 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 9) ואילך, אפשר לבדוק את מספר המצלמות שזמינות במכשיר באמצעות השיטה Camera.getNumberOfCameras()
.
גישה למצלמות
אם קבעתם שלמכשיר שבו האפליקציה פועלת יש מצלמה, אתם צריכים לבקש גישה אליה באמצעות קבלת מופע של Camera
(אלא אם אתם משתמשים בintent כדי לגשת למצלמה).
כדי לגשת למצלמה הראשית, משתמשים בשיטה Camera.open()
ומוודאים שמתבצעת לכידה של חריגים, כמו שמוצג בקוד שלמטה:
Kotlin
/** A safe way to get an instance of the Camera object. */ fun getCameraInstance(): Camera? { return try { Camera.open() // attempt to get a Camera instance } catch (e: Exception) { // Camera is not available (in use or does not exist) null // returns null if camera is unavailable } }
Java
/** A safe way to get an instance of the Camera object. */ public static Camera getCameraInstance(){ Camera c = null; try { c = Camera.open(); // attempt to get a Camera instance } catch (Exception e){ // Camera is not available (in use or does not exist) } return c; // returns null if camera is unavailable }
חשוב לדעת: כשמשתמשים ב-Camera.open()
, תמיד צריך לבדוק אם יש חריגים. אם לא תבדקו חריגים במקרים שבהם המצלמה נמצאת בשימוש או לא קיימת, המערכת תסגור את האפליקציה.
במכשירים שמותקנת בהם גרסה Android 2.3 (רמת API 9) ומעלה, אפשר לגשת למצלמות ספציפיות באמצעות
Camera.open(int)
. קוד הדוגמה שלמעלה יגש למצלמה הראשונה שפונה לאחור במכשיר עם יותר ממצלמה אחת.
בדיקת תכונות המצלמה
אחרי שמקבלים גישה למצלמה, אפשר לקבל מידע נוסף על היכולות שלה באמצעות השיטה Camera.getParameters()
ולבדוק את האובייקט Camera.Parameters
שמוחזר כדי לראות אילו יכולות נתמכות. כשמשתמשים ב-API ברמה 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 (רמת API 24) ואילך, אי אפשר יותר להניח שיחס הגובה-רוחב של התצוגה המקדימה זהה לפעילות שלכם גם אחרי הקריאה ל-setDisplayOrientation()
.
בהתאם לגודל החלון ויחס הגובה-רוחב, יכול להיות שתצטרכו להתאים תצוגה מקדימה רחבה של המצלמה לפריסה לאורך, או להיפך, באמצעות פריסה של תיבת מכתבים.
הצגת תצוגה מקדימה בפריסה
צריך למקם מחלקה של תצוגה מקדימה של מצלמה, כמו הדוגמה שמוצגת בקטע הקודם, בפריסת הפעילות יחד עם רכיבי UI אחרים לצילום תמונה או סרטון. בקטע הזה נסביר איך ליצור פריסה ופעילות בסיסיות לתצוגה המקדימה.
קוד הפריסה הבא מספק תצוגה בסיסית מאוד שאפשר להשתמש בה כדי להציג תצוגה מקדימה של המצלמה. בדוגמה הזו, הרכיב 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()
כדי להגדיר את סיבוב תמונת התצוגה המקדימה. כדי לשנות את כיוון התצוגה המקדימה כשהמשתמש משנה את הכיוון של הטלפון, צריך קודם לעצור את התצוגה המקדימה באמצעות Camera.stopPreview()
, לשנות את הכיוון ואז להפעיל אותה מחדש באמצעות Camera.startPreview()
, בתוך השיטה surfaceChanged()
של מחלקת התצוגה המקדימה.
בפעילות של תצוגת המצלמה, מוסיפים את הכיתה לתצוגה המקדימה לרכיב 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()
בדוגמה שלמעלה מתייחסת לשיטת הדוגמה שמוצגת במאמר גישה למצלמות.
צילום תמונות
אחרי שיוצרים מחלקה לתצוגה מקדימה ופריסת תצוגה שבה אפשר להציג אותה, אפשר להתחיל לצלם תמונות באמצעות האפליקציה. בקוד האפליקציה, צריך להגדיר רכיבי Listener עבור אמצעי הבקרה של ממשק המשתמש כדי להגיב לפעולת משתמש על ידי צילום תמונה.
כדי לאחזר תמונה, משתמשים בשיטה 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()); } } };
מפעילים את צילום התמונה באמצעות קריאה ל-method 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.release()
כשהאפליקציה מסיימת להשתמש בו.Camera
מידע על שחרור המצלמה זמין במאמר בנושא שחרור המצלמה.
צילום סרטונים
צילום וידאו באמצעות ה-framework של Android מחייב ניהול קפדני של אובייקט Camera
ותיאום עם המחלקה MediaRecorder
. כשמקליטים סרטון באמצעות Camera
, צריך לנהל את השיחות ב-Camera.lock()
וב-Camera.unlock()
כדי לאפשר ל-MediaRecorder
גישה לחומרת המצלמה, בנוסף לשיחות ב-Camera.open()
וב-Camera.release()
.
הערה: החל מ-Android 4.0 (רמת API 14), הקריאות Camera.lock()
ו-Camera.unlock()
מנוהלות עבורכם באופן אוטומטי.
בניגוד לצילום תמונות באמצעות מצלמת המכשיר, צילום וידאו דורש סדר קריאות ספציפי מאוד. כדי להכין את האפליקציה לצילום סרטונים ולצלם סרטונים בהצלחה, צריך לפעול לפי סדר ביצוע ספציפי, כמו שמפורט בהמשך.
- Open Camera (פתיחת המצלמה) – משתמשים ב-
Camera.open()
כדי לקבל מופע של אובייקט המצלמה. - חיבור לתצוגה מקדימה – כדי להכין תצוגה מקדימה של תמונה מהמצלמה בשידור חי, מחברים
SurfaceView
למצלמה באמצעותCamera.setPreviewDisplay()
. - התחלת תצוגה מקדימה – מתקשרים אל
Camera.startPreview()
כדי להתחיל להציג את התמונות החיות מהמצלמה. - התחלת הקלטת וידאו – צריך לבצע את השלבים הבאים בסדר הנכון כדי להקליט וידאו:
- ביטול הנעילה של המצלמה – ביטול הנעילה של המצלמה כדי ש-
MediaRecorder
יוכל להשתמש בה על ידי התקשרות אלCamera.unlock()
. - מגדירים את MediaRecorder – מתקשרים לשיטה
MediaRecorder
הבאה בסדר הזה. מידע נוסף מופיע בMediaRecorder
מאמרי העזרה.-
setCamera()
– הגדרת המצלמה שתשמש לצילום וידאו, שימוש במופע הנוכחי שלCamera
באפליקציה. -
setAudioSource()
– הגדרת מקור האודיו, באמצעותMediaRecorder.AudioSource.CAMCORDER
. -
setVideoSource()
– הגדרת מקור הסרטון, שימוש ב-MediaRecorder.VideoSource.CAMERA
. - מגדירים את הפורמט והקידוד של פלט הווידאו. ב-Android 2.2 (רמת API 8) ומעלה, משתמשים בשיטה
MediaRecorder.setProfile
ומקבלים מופע של פרופיל באמצעותCamcorderProfile.get()
. בגרסאות Android שקודמות לגרסה 2.2, צריך להגדיר את פורמט פלט הווידאו ואת פרמטרי הקידוד:-
setOutputFormat()
– הגדרת פורמט הפלט, ציון הגדרת ברירת המחדל אוMediaRecorder.OutputFormat.MPEG_4
. -
setAudioEncoder()
– הגדרת סוג קידוד הצליל, ציון הגדרת ברירת המחדל אוMediaRecorder.AudioEncoder.AMR_NB
. -
setVideoEncoder()
– הגדרת סוג קידוד הווידאו, ציון הגדרת ברירת המחדל אוMediaRecorder.VideoEncoder.MPEG_4_SP
.
-
-
setOutputFile()
– הגדרת קובץ הפלט. אפשר להשתמש ב-getOutputMediaFile(MEDIA_TYPE_VIDEO).toString()
משיטת הדוגמה שבקטע שמירת קובצי מדיה. -
setPreviewDisplay()
– מציינים את רכיב הפריסה של התצוגה המקדימהSurfaceView
עבור האפליקציה. משתמשים באותו אובייקט שצוין עבור תצוגה מקדימה של Connect.
שימו לב: אתם צריכים להפעיל את שיטות ההגדרה האלה
MediaRecorder
בסדר הזה, אחרת האפליקציה תיתקל בשגיאות וההקלטה תיכשל. -
- Prepare MediaRecorder – הכנת
MediaRecorder
עם הגדרות שסופקו על ידי קריאה ל-MediaRecorder.prepare()
. - Start MediaRecorder – כדי להתחיל להקליט סרטון, קוראים ל-
MediaRecorder.start()
.
- ביטול הנעילה של המצלמה – ביטול הנעילה של המצלמה כדי ש-
- Stop Recording Video (הפסקת הקלטת הסרטון) – כדי להשלים הקלטת סרטון בהצלחה, צריך להפעיל את השיטות הבאות בסדר הזה:
- Stop MediaRecorder – הפסקת הקלטת הווידאו על ידי קריאה ל-
MediaRecorder.stop()
. - איפוס MediaRecorder – אפשרות נוספת היא להסיר את הגדרות התצורה ממכשיר ההקלטה על ידי קריאה ל-
MediaRecorder.reset()
. - Release MediaRecorder – שחרור
MediaRecorder
על ידי קריאה ל-MediaRecorder.release()
. - נעילת המצלמה – נעילת המצלמה כדי שיהיה אפשר להשתמש בה בהפעלות עתידיות של
MediaRecorder
על ידי התקשרות אלCamera.lock()
. החל מ-Android 4.0 (רמת API 14), הקריאה הזו לא נדרשת אלא אם הקריאהMediaRecorder.prepare()
נכשלת.
- Stop MediaRecorder – הפסקת הקלטת הווידאו על ידי קריאה ל-
- הפסקת התצוגה המקדימה – כשהפעילות מסתיימת והמצלמה לא בשימוש, מפסיקים את התצוגה המקדימה באמצעות
Camera.stopPreview()
. - Release Camera (שחרור המצלמה) – שחרור המצלמה כדי שאפליקציות אחרות יוכלו להשתמש בה באמצעות קריאה ל-
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
יש הגדרות ברירת מחדל, אבל יכול להיות שתרצו לשנות את ההגדרות האלה באפליקציה שלכם:
setVideoEncodingBitRate()
setVideoSize()
setVideoFrameRate()
setAudioEncodingBitRate()
setAudioChannels()
setAudioSamplingRate()
התחלה והפסקה של MediaRecorder
כשמתחילים ומפסיקים הקלטת סרטון באמצעות המחלקה MediaRecorder
, צריך לפעול לפי סדר מסוים, כמו שמופיע בהמשך.
- ביטול הנעילה של המצלמה באמצעות
Camera.unlock()
- מגדירים את
MediaRecorder
כמו בדוגמת הקוד שלמעלה - איך מתחילים הקלטה באמצעות
MediaRecorder.start()
- הקלטת הסרטון
- הפסקת ההקלטה באמצעות
MediaRecorder.stop()
- שחרור מכשיר ההקלטה של המדיה באמצעות
MediaRecorder.release()
- נעילת המצלמה באמצעות
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 (רמת 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 תומכת במגוון רחב של תכונות מצלמה שאפשר לשלוט בהן באמצעות אפליקציית המצלמה, כמו פורמט תמונה, מצב פלאש, הגדרות פוקוס ועוד. בקטע הזה מפורטות תכונות נפוצות של מצלמות, ומוסבר בקצרה איך להשתמש בהן. אפשר לגשת לרוב התכונות של המצלמה ולהגדיר אותן באמצעות האובייקט 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 | הקלטת פריימים עם השהיות מוגדרות כדי ליצור סרטון בהילוך מהיר |
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
, לבצע קריאה ל-method 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), אפליקציית המצלמה יכולה לספק אמצעי בקרה נוספים כדי לאפשר לאפליקציה או למשתמשים לציין אזורים בתמונה שייעשה בהם שימוש לקביעת הגדרות הפוקוס או רמת התאורה, ולהעביר את הערכים האלה לחומרת המצלמה לשימוש בצילום תמונות או סרטונים.
האזורים למדידת האור ולפוקוס פועלים באופן דומה מאוד לתכונות אחרות במצלמה, כלומר אתם שולטים בהם באמצעות שיטות באובייקט 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.