חיישני מיקום

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

הערה: חיישן הכיוון הוצא משימוש ב-Android 2.2 (רמת API 8), וסוג חיישן הכיוון הוצא משימוש ב-Android 4.4W (רמת API 20).

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

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

טבלה 1. חיישני מיקום שנתמכים בפלטפורמת Android.

חיישן נתוני אירועים מחיישנים תיאור יחידות מידה
TYPE_GAME_ROTATION_VECTOR SensorEvent.values[0] רכיב וקטור הסיבוב לאורך ציר ה-x (x * sin(θ/2)). ללא יחידה
SensorEvent.values[1] רכיב וקטור הסיבוב לאורך ציר ה-y (y * sin(θ/2)).
SensorEvent.values[2] רכיב וקטור הסיבוב לאורך ציר z‏ (z * sin(θ/2)).
TYPE_GEOMAGNETIC_ROTATION_VECTOR SensorEvent.values[0] רכיב וקטור הסיבוב לאורך ציר ה-x (x * sin(θ/2)). ללא יחידה
SensorEvent.values[1] רכיב וקטור הסיבוב לאורך ציר ה-y (y * sin(θ/2)).
SensorEvent.values[2] רכיב וקטור הסיבוב לאורך ציר z‏ (z * sin(θ/2)).
TYPE_MAGNETIC_FIELD SensorEvent.values[0] עוצמת השדה הגיאומגנטי לאורך ציר ה-X. μT
SensorEvent.values[1] עוצמת השדה הגיאומגנטי לאורך ציר ה-y.
SensorEvent.values[2] עוצמת השדה הגיאומגנטי לאורך ציר z.
TYPE_MAGNETIC_FIELD_UNCALIBRATED SensorEvent.values[0] עוצמת השדה הגיאומגנטי (ללא כיול ברזל קשיח) לאורך ציר ה-x. μT
SensorEvent.values[1] עוצמת השדה הגיאומגנטי (ללא כיול ברזל קשיח) לאורך ציר y.
SensorEvent.values[2] עוצמת השדה הגיאומגנטי (ללא כיול ברזל קשיח) לאורך ציר z.
SensorEvent.values[3] הערכה של הטיה מברזל לאורך ציר ה-x.
SensorEvent.values[4] הערכת הטיה של ברזל לאורך ציר ה-y.
SensorEvent.values[5] הערכה של הטיה מברזל לאורך ציר z.
TYPE_ORIENTATION1 SensorEvent.values[0] כיוון (זווית סביב ציר z). מעלות
SensorEvent.values[1] שיפוע (זווית סביב ציר ה-X).
SensorEvent.values[2] רוטציה (זווית סביב ציר ה-y).
TYPE_PROXIMITY SensorEvent.values[0] המרחק מהעצם.2 ס"מ

1 החיישן הזה הוצא משימוש ב-Android 2.2 (רמת API ‏8), וסוג החיישן הזה הוצא משימוש ב-Android 4.4W (רמת API ‏20). מסגרת החיישן מספקת שיטות חלופיות לקבלת המידע על כיוון המכשיר, כפי שמתואר בקטע חישוב כיוון המכשיר.

2 חלק מחיששני הקירבה מספקים רק ערכים בינאריים שמייצגים 'קרוב' ו'רחוק'.

שימוש בחיישן וקטור הסיבוב של המשחק

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

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

הקוד הבא מראה איך לקבל מופע של חיישן ברירת המחדל של וקטור הסיבוב במשחק:

Kotlin

private lateinit var sensorManager: SensorManager
private var sensor: Sensor? = null
...
sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_GAME_ROTATION_VECTOR)

Java

private SensorManager sensorManager;
private Sensor sensor;
...
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_GAME_ROTATION_VECTOR);

שימוש בחיישן וקטור הסיבוב הגיאומגנטי

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

הקוד הבא מראה איך לקבל מופע של חיישן ברירת המחדל של וקטור הסיבוב הגיאומגנטי:

Kotlin

private lateinit var sensorManager: SensorManager
private var sensor: Sensor? = null
...
sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR)

Java

private SensorManager sensorManager;
private Sensor sensor;
...
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR);

חישוב הכיוון שאליו פונה המכשיר

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

Kotlin

private lateinit var sensorManager: SensorManager
...
// Rotation matrix based on current readings from accelerometer and magnetometer.
val rotationMatrix = FloatArray(9)
SensorManager.getRotationMatrix(rotationMatrix, null, accelerometerReading, magnetometerReading)

// Express the updated rotation matrix as three orientation angles.
val orientationAngles = FloatArray(3)
SensorManager.getOrientation(rotationMatrix, orientationAngles)

Java

private SensorManager sensorManager;
...
// Rotation matrix based on current readings from accelerometer and magnetometer.
final float[] rotationMatrix = new float[9];
SensorManager.getRotationMatrix(rotationMatrix, null,
    accelerometerReading, magnetometerReading);

// Express the updated rotation matrix as three orientation angles.
final float[] orientationAngles = new float[3];
SensorManager.getOrientation(rotationMatrix, orientationAngles);

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

  • Azimuth (מעלות סיבוב סביב ציר z-). זהו הזווית בין כיוון המצפן הנוכחי של המכשיר לבין צפון מגנטי. אם הקצה העליון של המכשיר פונה לצפון המגנטי, האזימוט הוא 0 מעלות. אם הקצה העליון פונה דרומה, האזימוט הוא 180 מעלות. באופן דומה, אם הקצה העליון פונה מזרח, האזימוט הוא 90 מעלות, ואם הקצה העליון פונה מערב, האזימוט הוא 270 מעלות.
  • נטייה (מעלות של סיבוב סביב ציר ה-x). זוהי הזווית שבין מישור מקביל למסך המכשיר למישור מקביל לאדמה. אם מחזיקים את המכשיר במקביל לאדמה, כך שהקצה התחתון קרוב אליכם, ומטים את הקצה העליון של המכשיר לכיוון האדמה, זווית ההטיה תהפוך לחיוב. הטיה בכיוון ההפוך – כלומר, הזזת הקצה העליון של המכשיר הרחק מהאדמה – גורמת לזווית השיפוע להיות שלילית. טווח הערכים הוא -90 מעלות עד 90 מעלות.
  • רוטציה (מעלות סיבוב סביב ציר ה-y). זוהי הזווית בין מישור אנכי למסך המכשיר לבין מישור אנכי לאדמה. אם מחזיקים את המכשיר במקביל לאדמה, כך שהקצה התחתון קרוב אליכם, ומטים את הקצה הימני של המכשיר כלפי האדמה, זווית הנטייה תהיה חיובית. אם מטים את המכשיר בכיוון ההפוך – כלומר, מזיזים את הקצה הימני של המכשיר לכיוון הקרקע – זווית הנטייה הופכת לשלילית. טווח הערכים הוא מ-180 מעלות ל-180 מעלות.

הערה: ההגדרה של תנועת הסיבוב של החיישן השתנתה כדי לשקף את הרוב המכריע של ההטמעות בסביבה העסקית של חיישני הגיאוגרפיה.

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

חיישן הכיוון מסיק את הנתונים שלו על ידי עיבוד נתוני החיישן הגולמיים מהתאוצה ומחיישן השדה הגיאומגנטי. בגלל העיבוד הכבד, רמת הדיוק של חיישן הכיוון פוחתת. באופן ספציפי, החיישן הזה מהימן רק כשזווית הנטייה היא 0. כתוצאה מכך, חיישן הכיוון הוצא משימוש ב-Android 2.2 (רמת API 8), וסוג חיישן הכיוון הוצא משימוש ב-Android 4.4W (רמת API 20). במקום להשתמש בנתונים גולמיים מחיישן הכיוון, מומלץ להשתמש ב-method‏ getRotationMatrix() בשילוב עם method‏ getOrientation() כדי לחשב את ערכי הכיוון, כפי שמתואר בדוגמת הקוד הבאה. כחלק מהתהליך הזה, אפשר להשתמש ב-method‏ remapCoordinateSystem() כדי לתרגם את ערכי הכיוון למסגרת העזר של האפליקציה.

Kotlin

class SensorActivity : Activity(), SensorEventListener {

    private lateinit var sensorManager: SensorManager
    private val accelerometerReading = FloatArray(3)
    private val magnetometerReading = FloatArray(3)

    private val rotationMatrix = FloatArray(9)
    private val orientationAngles = FloatArray(3)

    public override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.main)
        sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
    }

    override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) {
        // Do something here if sensor accuracy changes.
        // You must implement this callback in your code.
    }

    override fun onResume() {
        super.onResume()

        // Get updates from the accelerometer and magnetometer at a constant rate.
        // To make batch operations more efficient and reduce power consumption,
        // provide support for delaying updates to the application.
        //
        // In this example, the sensor reporting delay is small enough such that
        // the application receives an update before the system checks the sensor
        // readings again.
        sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)?.also { accelerometer ->
            sensorManager.registerListener(
                    this,
                    accelerometer,
                    SensorManager.SENSOR_DELAY_NORMAL,
                    SensorManager.SENSOR_DELAY_UI
            )
        }
        sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD)?.also { magneticField ->
            sensorManager.registerListener(
                    this,
                    magneticField,
                    SensorManager.SENSOR_DELAY_NORMAL,
                    SensorManager.SENSOR_DELAY_UI
            )
        }
    }

    override fun onPause() {
        super.onPause()

        // Don't receive any more updates from either sensor.
        sensorManager.unregisterListener(this)
    }

    // Get readings from accelerometer and magnetometer. To simplify calculations,
    // consider storing these readings as unit vectors.
    override fun onSensorChanged(event: SensorEvent) {
        if (event.sensor.type == Sensor.TYPE_ACCELEROMETER) {
            System.arraycopy(event.values, 0, accelerometerReading, 0, accelerometerReading.size)
        } else if (event.sensor.type == Sensor.TYPE_MAGNETIC_FIELD) {
            System.arraycopy(event.values, 0, magnetometerReading, 0, magnetometerReading.size)
        }
    }

    // Compute the three orientation angles based on the most recent readings from
    // the device's accelerometer and magnetometer.
    fun updateOrientationAngles() {
        // Update rotation matrix, which is needed to update orientation angles.
        SensorManager.getRotationMatrix(
                rotationMatrix,
                null,
                accelerometerReading,
                magnetometerReading
        )

        // "rotationMatrix" now has up-to-date information.

        SensorManager.getOrientation(rotationMatrix, orientationAngles)

        // "orientationAngles" now has up-to-date information.
    }
}

Java

public class SensorActivity extends Activity implements SensorEventListener {

    private SensorManager sensorManager;
    private final float[] accelerometerReading = new float[3];
    private final float[] magnetometerReading = new float[3];

    private final float[] rotationMatrix = new float[9];
    private final float[] orientationAngles = new float[3];

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
    }

    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {
        // Do something here if sensor accuracy changes.
        // You must implement this callback in your code.
    }

    @Override
    protected void onResume() {
        super.onResume();

        // Get updates from the accelerometer and magnetometer at a constant rate.
        // To make batch operations more efficient and reduce power consumption,
        // provide support for delaying updates to the application.
        //
        // In this example, the sensor reporting delay is small enough such that
        // the application receives an update before the system checks the sensor
        // readings again.
        Sensor accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
        if (accelerometer != null) {
            sensorManager.registerListener(this, accelerometer,
                SensorManager.SENSOR_DELAY_NORMAL, SensorManager.SENSOR_DELAY_UI);
        }
        Sensor magneticField = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
        if (magneticField != null) {
            sensorManager.registerListener(this, magneticField,
                SensorManager.SENSOR_DELAY_NORMAL, SensorManager.SENSOR_DELAY_UI);
        }
    }

    @Override
    protected void onPause() {
        super.onPause();

        // Don't receive any more updates from either sensor.
        sensorManager.unregisterListener(this);
    }

    // Get readings from accelerometer and magnetometer. To simplify calculations,
    // consider storing these readings as unit vectors.
    @Override
    public void onSensorChanged(SensorEvent event) {
        if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
          System.arraycopy(event.values, 0, accelerometerReading,
              0, accelerometerReading.length);
        } else if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) {
            System.arraycopy(event.values, 0, magnetometerReading,
                0, magnetometerReading.length);
        }
    }

    // Compute the three orientation angles based on the most recent readings from
    // the device's accelerometer and magnetometer.
    public void updateOrientationAngles() {
        // Update rotation matrix, which is needed to update orientation angles.
        SensorManager.getRotationMatrix(rotationMatrix, null,
            accelerometerReading, magnetometerReading);

        // "rotationMatrix" now has up-to-date information.

        SensorManager.getOrientation(rotationMatrix, orientationAngles);

        // "orientationAngles" now has up-to-date information.
    }
}

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

שימוש בחיישן השדה הגיאומגנטי

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

Kotlin

private lateinit var sensorManager: SensorManager
private var sensor: Sensor? = null
...
sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD)

Java

private SensorManager sensorManager;
private Sensor sensor;
...
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);

הערה: אם האפליקציה שלכם מטרגטת ל-Android 12 (רמת API 31) ואילך, החיישן הזה מוגבל בקצב.

החיישן הזה מספק נתונים גולמיים של עוצמת השדה (ב-μT) לכל אחד משלושת צירי הקואורדינטות. בדרך כלל אין צורך להשתמש בחיישן הזה באופן ישיר. במקום זאת, אפשר להשתמש בחיישן וקטור הסיבוב כדי לקבוע תנועה סיבובית גולמית, או להשתמש במד התאוצה ובחיישן השדה הגיאומגנטי בשילוב עם השיטה getRotationMatrix() כדי לקבל את מטריצת הסיבוב ואת מטריצת ההטיה. לאחר מכן תוכלו להשתמש במטריצות האלה בשיטות getOrientation() ו-getInclination() כדי לקבל נתונים של כיוון צפון ונטייה גיאומגנטית.

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

שימוש במגנטומטר ללא כיול

מגנטומטר לא מכויל דומה לחיישן השדה הגיאומגנטי, מלבד העובדה שלא מתבצעת כיול ברזל קשיח בשדה המגנטי. עדיין חלים על השדה המגנטי כיול המפעל ופיצוי הטמפרטורה. מגנטומטר לא מכויל שימושי לטיפול באומדנים לא טובים של רכיבי ברזל קשיח. באופן כללי, הערך של geomagneticsensor_event.values[0] יהיה קרוב לערך של uncalibrated_magnetometer_event.values[0] - uncalibrated_magnetometer_event.values[3]. כלומר,

calibrated_x ~= uncalibrated_x - bias_estimate_x

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

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

Kotlin

private lateinit var sensorManager: SensorManager
private var sensor: Sensor? = null
...
sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED)

Java

private SensorManager sensorManager;
private Sensor sensor;
...
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED);

שימוש בחיישן הקרבה

חיישן הקירבה מאפשר לכם לקבוע את המרחק של אובייקט מהמכשיר. הקוד הבא מראה איך לקבל מופע של חיישן הקרבה שמוגדר כברירת מחדל:

Kotlin

private lateinit var sensorManager: SensorManager
private var sensor: Sensor? = null
...
sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY)

Java

private SensorManager sensorManager;
private Sensor sensor;
...
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);

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

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

הקוד הבא מראה איך משתמשים בחיישן הקרבה:

Kotlin

class SensorActivity : Activity(), SensorEventListener {

    private lateinit var sensorManager: SensorManager
    private var proximity: Sensor? = null

    public override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.main)

        // Get an instance of the sensor service, and use that to get an instance of
        // a particular sensor.
        sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
        proximity = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY)
    }

    override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) {
        // Do something here if sensor accuracy changes.
    }

    override fun onSensorChanged(event: SensorEvent) {
        val distance = event.values[0]
        // Do something with this sensor data.
    }

    override fun onResume() {
        // Register a listener for the sensor.
        super.onResume()

        proximity?.also { proximity ->
            sensorManager.registerListener(this, proximity, SensorManager.SENSOR_DELAY_NORMAL)
        }
    }

    override fun onPause() {
        // Be sure to unregister the sensor when the activity pauses.
        super.onPause()
        sensorManager.unregisterListener(this)
    }
}

Java

public class SensorActivity extends Activity implements SensorEventListener {
    private SensorManager sensorManager;
    private Sensor proximity;

    @Override
    public final void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        // Get an instance of the sensor service, and use that to get an instance of
        // a particular sensor.
        sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
        proximity = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
    }

    @Override
    public final void onAccuracyChanged(Sensor sensor, int accuracy) {
        // Do something here if sensor accuracy changes.
    }

    @Override
    public final void onSensorChanged(SensorEvent event) {
        float distance = event.values[0];
        // Do something with this sensor data.
    }

    @Override
    protected void onResume() {
        // Register a listener for the sensor.
        super.onResume();
        sensorManager.registerListener(this, proximity, SensorManager.SENSOR_DELAY_NORMAL);
      }

    @Override
    protected void onPause() {
        // Be sure to unregister the sensor when the activity pauses.
        super.onPause();
        sensorManager.unregisterListener(this);
    }
}

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

כדאי גם לקרוא את המאמרים הבאים: