חיישני מיקום

פלטפורמת Android מסוגלת להפעיל שני חיישנים שמאפשרים לכם לקבוע את המיקום של מכשיר: חיישן השדה הגיאומגנטי ומד התאוצה. פלטפורמת 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. מיקרו-טסלה
SensorEvent.values[1] עוצמת השדה הגיאומגנטי לאורך ציר Y.
SensorEvent.values[2] עוצמת השדה הגיאומגנטי לאורך ציר Z.
TYPE_MAGNETIC_FIELD_UNCALIBRATED SensorEvent.values[0] עוצמת השדה הגיאומגנטי (בלי כיול של ברזל קשה) לאורך ציר X. מיקרו-טסלה
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);

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

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

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

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

חיישן הכיוון מפיק נתונים על ידי עיבוד נתוני החיישנים הגולמיים ממד התאוצה ומחיישן השדה הגיאומגנטי. בגלל העיבוד הכבד שנדרש, הדיוק של חיישן הכיוון נפגע. באופן ספציפי, החיישן הזה אמין רק כשזווית הגלגול היא 0. לכן, חיישן הכיוון הוצא משימוש ב-Android 2.2 (רמת API‏ 8), וסוג חיישן הכיוון הוצא משימוש ב-Android 4.4W (רמת API‏ 20). במקום להשתמש בנתונים גולמיים מחיישן הכיוון, מומלץ להשתמש ב-getRotationMatrix() בשילוב עם 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) או לגרסה מתקדמת יותר, קצב השימוש בחיישן הזה מוגבל.

החיישן הזה מספק נתונים גולמיים של עוצמת השדה (במיקרו-טסלה) לכל אחד משלושת צירי הקואורדינטות. בדרך כלל לא צריך להשתמש בחיישן הזה באופן ישיר. במקום זאת, אתם יכולים להשתמש בחיישן וקטור הסיבוב כדי לקבוע את תנועת הסיבוב הגולמית, או להשתמש במד התאוצה ובחיישן השדה הגיאומגנטי בשילוב עם השיטה 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 ס "מ, אבל הוא יכול להשתנות מחיישן לחיישן. כדי לקבוע את הטווח המקסימלי של חיישן, אפשר להשתמש ב-method‏ getMaximumRange().

כדאי לקרוא גם על