חיישני מיקום

פלטפורמת 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().

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