מסגרת Android כוללת תמיכה במצלמות ובתכונות מצלמה שונות שזמינות במכשירים, ומאפשרת לכם לצלם תמונות וסרטונים באפליקציות. במסמך הזה עוסקת גישה מהירה ופשוטה לצילום תמונות וסרטונים, ומתארת גישה מתקדמת ליצירת ולהתאים אישית את חוויית המצלמה למשתמשים שלכם.
הערה:
בדף הזה מתואר
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
- אפשר להשתמש בסוג פעולת הכוונה
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.
הפרטים מתוארים בשיעורי ההכשרה
צילום תמונות בקלות
הקלטת סרטונים בקלות.
פיתוח אפליקציית מצלמה
חלק מהמפתחים עשויים לדרוש ממשק משתמש של מצלמה שמותאם אישית למראה שלהם או שמספק תכונות מיוחדות. כתיבת קוד משלכם לצילום תמונות יכולה לספק למשתמשים חוויה מרתקת יותר.
הערה: המדריך הבא מיועד לממשק ה-API הישן יותר, Camera
, שהוצא משימוש. לאפליקציות מצלמה חדשות או מתקדמות, ה-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 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)
קוד הדוגמה שלמעלה ייתן גישה למצלמה הראשונה, המצלמה האחורית, במכשיר עם יותר ממצלמה אחת.
תכונות המצלמה נבדקות
אחרי שמקבלים גישה למצלמה, אפשר לקבל מידע נוסף על היכולות שלה באמצעות השיטה Camera.getParameters()
ולבדוק את היכולות הנתמכות באובייקט Camera.Parameters
המוחזר. בזמן השימוש
ברמה 9 ואילך של API, צריך להשתמש ב-Camera.getCameraInfo()
כדי לבדוק אם יש מצלמה בחזית
או בחלק האחורי של המכשיר, ואת הכיוון של התמונה.
יצירת תצוגה מקדימה של הכיתה
כדי שמשתמשים יוכלו לצלם תמונות או סרטונים בצורה יעילה, הם צריכים לראות מה מצלמת המכשיר
רואה. סיווג תצוגה מקדימה של מצלמה הוא SurfaceView
שיכול להציג את נתוני התמונה החיים שמגיעים מהמצלמה, כדי שהמשתמשים יוכלו לכוון את התמונה או הסרטון ולצלם אותם.
הקוד לדוגמה הבא מדגים איך ליצור סיווג בסיסי של תצוגה מקדימה של המצלמה
כלולה בפריסה של View
. הכיתה הזו מיישמת את SurfaceHolder.Callback
כדי לתעד את אירועי ה-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()); } } };
מפעילים את הצילום של התמונה על ידי קריאה ל-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
באמצעות קריאה ל-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()
מנוהלות עבורך באופן אוטומטי.
בניגוד לצילום תמונות במצלמת המכשיר, כדי לצלם סרטון צריך להשתמש בסדר קריאה ספציפי מאוד. כדי להכין את האפליקציה לצילומי וידאו ולצלם אותם, צריך לפעול לפי סדר ספציפי של פעולות, כפי שמפורט בהמשך.
- פתיחת המצלמה – משתמשים ב
Camera.open()
כדי לקבל מופע של אובייקט המצלמה. - Connect Preview (חיבור לתצוגה מקדימה) – כדי להכין תצוגה מקדימה של תמונה מהמצלמה בשידור חי, מחברים
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 Preview.
זהירות: צריך להפעיל את שיטות ההגדרה
MediaRecorder
בסדר הזה, אחרת יהיו באפליקציה שגיאות וההקלטה תיכשל. - הכנת MediaRecorder – הכנת
MediaRecorder
באמצעות הגדרות התצורה שסופקו באמצעות קריאה אלMediaRecorder.prepare()
. - התחלת MediaRecorder – התחלת הקלטת וידאו באמצעות קריאה ל-
MediaRecorder.start()
.
- ביטול הנעילה של המצלמה – כדי לבטל את הנעילה של המצלמה לשימוש של
- עצירת ההקלטה של הסרטון – כדי להשלים את ההקלטה של הסרטון, צריך להפעיל את השיטות הבאות בסדר:
- Stop MediaRecorder – כדי להפסיק את הקלטת הסרטון, קוראים ל-
MediaRecorder.stop()
. - איפוס MediaRecorder – אפשר גם להסיר את הגדרות ההגדרה מהמצלמה על-ידי קריאה ל-
MediaRecorder.reset()
. - שחרור MediaRecorder – משחררים את
MediaRecorder
באמצעות קריאה ל-MediaRecorder.release()
. - נעילת המצלמה – ניתן לנעול את המצלמה כדי שניתן יהיה להשתמש בה בסשנים עתידיים של
MediaRecorder
באמצעות התקשרות אלCamera.lock()
. החל מ-Android 4.0 (רמת API 14), הקריאה הזו לא נדרשת אלא אם הקריאה ל-MediaRecorder.prepare()
נכשלה.
- Stop MediaRecorder – כדי להפסיק את הקלטת הסרטון, קוראים ל-
- הפסקת התצוגה המקדימה – כשהפעילות עם המצלמה מסתיימת, מפסיקים את התצוגה המקדימה באמצעות
Camera.stopPreview()
. - שחרור המצלמה – שחרור המצלמה כדי שאפליקציות אחרות יוכלו להשתמש
באמצעות קריאה אל
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()
ה-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 תומכת במגוון רחב של תכונות מצלמה שאפשר לשלוט בהן דרך אפליקציית המצלמה, כמו פורמט התמונה, מצב הפלאש, הגדרות המיקוד ועוד. בקטע הזה מפורטות התכונות הנפוצות של המצלמה, ומוסבר בקצרה איך משתמשים בהן. אפשר לגשת לרוב התכונות של המצלמה ולהגדיר אותן
באמצעות האובייקט 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
מספק method 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
. בדרך כלל, שינויים בפרמטרים גלויים למשתמש באופן מיידי בתצוגה המקדימה של המצלמה באפליקציה.
בצד התוכנה, ייתכן שיחלפו מספר פריימים עד ששינויים בפרמטרים ייכנסו לתוקף
המצלמה מעבדת את ההוראות החדשות ואז שולחת את נתוני התמונה המעודכנים.
חשוב: אי אפשר לשנות חלק מתכונות המצלמה לפי רצונכם. לחשבון במיוחד, שינוי הגודל או הכיוון של התצוגה המקדימה של המצלמה מחייב את התצוגה המקדימה, משנים את גודל התצוגה המקדימה ואז מפעילים מחדש את התצוגה המקדימה. החל מגרסה 4.0 של Android (רמה 14 של API), אפשר לשנות את כיוון התצוגה המקדימה בלי להפעיל מחדש את התצוגה המקדימה.
כדי להטמיע תכונות מצלמה אחרות, צריך יותר קוד, כולל:
- אזורי מדידה והתמקדות
- זיהוי פנים
- סרטון בהילוך מהיר
בחלקים הבאים מוסבר בקצרה איך מטמיעים את התכונות האלה.
אזורי מדידה והתמקדות
בתרחישי צילום מסוימים, מיקוד אוטומטי ומדידת אור לא יניבו את את התוצאות הרצויות. החל מ-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
מתאר צורה מלבנית שממופה על רשת של יחידות בגודל 2000 על 2000. הקואורדינטות -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 ואילך) יוכלו לגשת לערכים של השדות הבאים שמוחזרים על ידי השיטה getCameraCharacteristics()
, הן צריכות את ההרשאה CAMERA
:
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.