Android學習筆記之——手機中幾種自帶傳感器的應用

本博文學習一下Android中幾種傳感器的應用

 

目錄

Android傳感器

使用傳感器步驟

Demo Test

查看手機支持哪些傳感器

界面佈局

傳感器list

光傳感器

近距離傳感器、溫度傳感器、氣壓傳感器

陀螺儀

加速傳感器

重力測試

磁場傳感器

方位傳感器(Orientation Sensors)

參考資料


 

Android傳感器

傳感器就是通過對外界信號的感受和探測,並按照一定的規律把這些信號轉成我們需要的信息的一些微型物理設備,比如我們的手機中就存在很多傳感器。android中提供了相應的接口,讓我們可以訪問這些傳感器,並使用他們的數據。與傳感器交互需要使用Sensor對象。Sennor就是傳感器的一個類,包括傳感器的類型、精度等等他都可以來描述。

Android提供sensor framework也幫助開發者利用設備的傳感器進行開發。傳感器是隻讀的(NFC例外),因此只要設定監聽器接受傳感器信息即可。手機上有各種各樣的傳感器

  • 光傳感器(Light sensor)
  • 接近感應器(Proximity sensor)
  • 溫度傳感器(Temperature sensor)
  • 壓力傳感器(Pressure sensor)
  • 陀螺儀傳感器(Gyroscope sensor)
  • 加速感應器(Accelerometer)
  • 磁場感應器(Magnetic field sensor)
  • 方向感應器(Orientation sensor)
  • 重力感應器(Gravity sensor,Android 2.3引入)
  • 線性加速感應器(Linear acceleration sensor ,Android 2.3引入)
  • 旋轉矢量傳感器(Rotation vector sensor,Android 2.3)
  • 相對溼度傳感器(Relative humidity sensor,Android 4.0)
  • 近場通信(NFC)傳感器(Android 2.3引入),NFC和其他不一樣,具有讀寫功能。

使用傳感器步驟

一般來說,要使用傳感器要分三個步驟:

1、獲取傳感器管理者對象。獲取一個傳感器管理者SensorManager方便對Sensor的管理

// 獲取傳感器管理者對象
SensorManager mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);

2. 獲取指定的傳感器對象。SensorManager有方法是用來獲取指定類型傳感器的,如getDefaultSensor(int type),其中type可以使用Sensor類中的常量設置,Sensor中提供了很多傳感器類型的常量

Sensor sensor = mSensorManager.getDefaultSensor(int type);

其中type就是傳感器的類型了

  • 加速度傳感器Sensor.TYPE_ACCELEROMETER   
  • 方向傳感器Sensor.TYPE_ORIENTATION
  • 磁場傳感器Sensor.TYPE_MAGNETIC_FIELD
  • 溫度傳感器Sensor.TYPE_AMBIENT_TEMPERATURE
  • 光傳感器Sensor.TYPE_LIGHT
  • 壓力傳感器Sensor.TYPE_PRESSURE

3.給傳感器添加監聽。通過sensorManager來爲傳感器註冊一個監聽器,然後程序就可以通過這個監聽來獲取傳感器獲取的數據了(一般來說,在onResume()方法中註冊)

public boolean registerListener(SensorEventListener listener, Sensor sensor, int samplingPeriodUs);

msensorManager.registerListener(this,sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT),SensorManager.SENSOR_DELAY_GAME);
  • 第一個參數是傳感器監聽器
  • 第二個參數是上面獲取到的傳感器對象
  • 第三個參數是指定獲取傳感器數據的頻率

samplingPeriodUs 採樣頻率支持以下幾個頻率值

  • SensorManager.SENSOR_DELAY_FASTEST。最快,延遲最小,比較消耗電量,由於傳遞的爲原始數據,如果算法處理不好將會影響應用的性能
  • SensorManager.SENSOR_DELAY_GAME。適合遊戲的頻率,在一般實時性要求的應用上適合使用這種頻率。
  • SensorManager.SENSOR_DELAY_NORMAL。正常的頻率,實時性要求不是很高的時候適合這種頻率。
  • SensorManager.SENSOR_DELAY_UI。適合普通用戶界面的頻率,這種模式比較省電,而且系統開銷也很小,但是延遲較大,適合在普通的小程序中使用

在實現監聽器的時候,要重寫兩個方法onSensorChanged()和onAccuracyChanged(),第一個是當傳感器的值改變時回調,第二個是當傳感器精度改變時回調。另外,傳感器的值是放在數組中保存的一般有三個值,分別是xyz軸三個方向上的值。

4、最後別忘記解監聽
在onStop()和onPause()方法中註銷監聽器

sensorManager.unregisterListener(this);

 

Demo Test

查看手機支持哪些傳感器

界面佈局

之前博文《Android學習筆記之——Activity》已經介紹過如何使用Menu,本博文的框架則是基於該框架的。按不同的按鈕實現不同sensor

首先在res目錄下新建一個menu文件夾,右擊res目錄→New→Directory,輸入文件夾名menu,點擊OK。

接着在這個文件夾下再新建一個名叫menu_main的菜單文件,右擊menu文件夾→New→Menu resource file。定義菜單文件如下

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:title="傳感器列表" android:id="@+id/test_sensor_list"/>
    <item android:title="光傳感器" android:id="@+id/test_light_sensor"/>
    <item android:title="近距離傳感器" android:id="@+id/test_proximity_sensor"/>
    <item android:title="陀螺儀" android:id="@+id/test_gyroscope_sensor"/>
    <item android:title="加速測量儀" android:id="@+id/test_accelerometer_sensor"/>
    <item android:title="重力測量" android:id="@+id/test_accelerometer_2_sensor"/>
    <item android:title="磁場傳感器" android:id="@+id/test_magnetic_sensor"/>
    <item android:title="方位測量" android:id="@+id/test_orientation"/>
</menu>

在主文件下定義整個界面如下

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="android Sensor Test"

        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>
package com.example.sensortest;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // TODO Auto-generated method stub
        getMenuInflater().inflate(R.menu.menu_main, menu);
        //inflate() 方法接收兩個參數,
        //第一個參數用於指定我們通過哪一個資源文件來創建菜單,這裏當然傳入R.menu.menu_main 。
        //第二個參數用於指定我們的菜單項將添加到哪一個Menu 對象當中,這裏直接使用onCreateOptionsMenu() 方法中傳入的menu 參數。
        return super.onCreateOptionsMenu(menu);
    }

    //然後給這些按鍵定義內容(不同的按菜單按鍵啓動不一樣的activity)
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // TODO Auto-generated method stub
        switch(item.getItemId()){
            //通過Intent來啓動別的activity
            case R.id.test_sensor_list:
                startActivity(new Intent(this,SensorListActivity.class));
                break;
            case R.id.test_light_sensor:
//                startActivity(new Intent(this,LightSensorActivity.class));
                break;
            case R.id.test_proximity_sensor:
//                startActivity(new Intent(this,ProximitySensorActivity.class));
                break;
            case R.id.test_gyroscope_sensor:
//                startActivity(new Intent(this,GyroscopeSensorActivity.class));
                break;
            case R.id.test_accelerometer_sensor:
//                startActivity(new Intent(this,AccelerometerSensorActivity.class));
                break;
            case R.id.test_accelerometer_2_sensor:
//                startActivity(new Intent(this,GravityActivity.class));
                break;
            case R.id.test_magnetic_sensor:
//                startActivity(new Intent(this,MagneticFieldSensorActivity.class));
                break;
            case R.id.test_orientation:
//                startActivity(new Intent(this,VirtualJax.class));
                break;
            default:
                break;
        }
        return true;
    }


}

結果如下

傳感器list

package com.example.sensortest;

import androidx.appcompat.app.AppCompatActivity;

import android.hardware.Sensor;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;

import java.util.HashMap;
import java.util.List;

public class SensorListActivity extends AppCompatActivity {

    private TextView tv=null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_sensor_list);

        tv=(TextView) findViewById(R.id.sensor_list_text);
        showSensorList();//自定義的函數,展示手機所帶有的sensor
    }

    private HashMap<Integer, String> sensorTypes = new HashMap<Integer,String>();
    {
        sensorTypes.put(Sensor.TYPE_ACCELEROMETER, "TYPE_ACCELEROMETER");
        sensorTypes.put(Sensor.TYPE_AMBIENT_TEMPERATURE, "TYPE_AMBIENT_TEMPERATURE");
        sensorTypes.put(Sensor.TYPE_GAME_ROTATION_VECTOR, "TYPE_GAME_ROTATION_VECTOR");
        sensorTypes.put(Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR, "TYPE_GEOMAGNETIC_ROTATION_VECTOR");
        sensorTypes.put(Sensor.TYPE_GRAVITY, "TYPE_GRAVITY");
        sensorTypes.put(Sensor.TYPE_GYROSCOPE, "TYPE_GYROSCOPE");
        sensorTypes.put(Sensor.TYPE_GYROSCOPE_UNCALIBRATED,"TYPE_GYROSCOPE_UNCALIBRATED");
        sensorTypes.put(Sensor.TYPE_LIGHT, "TYPE_LIGHT");
        sensorTypes.put(Sensor.TYPE_LINEAR_ACCELERATION, "TYPE_LINEAR_ACCELERATION");
        sensorTypes.put(Sensor.TYPE_MAGNETIC_FIELD, "TYPE_MAGNETIC_FIELD");
        sensorTypes.put(Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED, "TYPE_MAGNETIC_FIELD_UNCALIBRATED");
        sensorTypes.put(Sensor.TYPE_ORIENTATION,"TYPE_ORIENTATION (deprecated)");//use SensorManager.getOrientation() instead.
        sensorTypes.put(Sensor.TYPE_PRESSURE, "TYPE_PRESSURE");
        sensorTypes.put(Sensor.TYPE_PROXIMITY, "TYPE_PROXIMITY");
        sensorTypes.put(Sensor.TYPE_RELATIVE_HUMIDITY, "TYPE_RELATIVE_HUMIDITY");
        sensorTypes.put(Sensor.TYPE_ROTATION_VECTOR, "TYPE_ROTATION_VECTOR");
        sensorTypes.put(Sensor.TYPE_SIGNIFICANT_MOTION,"TYPE_SIGNIFICANT_MOTION");
        sensorTypes.put(Sensor.TYPE_STEP_COUNTER,"TYPE_STEP_COUNTER");
        sensorTypes.put(Sensor.TYPE_STEP_DETECTOR,"TYPE_STEP_DETECTOR");
        sensorTypes.put(Sensor.TYPE_TEMPERATURE, "TYPE_TEMPERATURE(deprecated)");//Sensor.TYPE_AMBIENT_TEMPERATURE
    }

    private void showSensorList(){
        showInfo("本設備具有以下傳感器:");

        //首先獲取傳感器管理器
        SensorManager mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
        //獲取設備的傳感器對象列表
        List<Sensor> list=mSensorManager.getSensorList(Sensor.TYPE_ALL);
        //顯示傳感器對象的信息。
        // 同樣的傳感器類型,不同廠家不同型號會有所不同,例如解析度不同,數值範圍不同,功耗對應用性能重要,
        // 但是老實說這更多是OEM應該關心的問題,因爲器件是他們選擇的。對於應該如果發現功耗過大,就需要儘量避免長時間使用。
        for(Sensor sensor : list){
            showInfo("名字:" + sensor.getName());
            showInfo("  type:" + sensorTypes.get(sensor.getType()) + "(" + sensor.getType() +")");
            showInfo("  vendor:" + sensor.getVendor());
            showInfo("  version:" + sensor.getVersion());
            showInfo("  resolution:" + sensor.getResolution());
            showInfo("  max range:" + sensor.getMaximumRange());
            showInfo("  power:" + sensor.getPower());
        }


    }

    private void showInfo(String info){
        tv.append("\n" + info);
        Log.d("SensorList",info);
    }
}
<?xml version="1.0" encoding="utf-8"?>
<!--<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"-->
<!--    xmlns:app="http://schemas.android.com/apk/res-auto"-->
<!--    xmlns:tools="http://schemas.android.com/tools"-->
<!--    android:layout_width="match_parent"-->
<!--    android:layout_height="match_parent"-->
<!--    tools:context=".SensorListActivity">-->

<!--</androidx.constraintlayout.widget.ConstraintLayout>-->
<LinearLayout
    android:orientation="vertical"
    android:layout_height="match_parent"
    android:layout_width="match_parent"
    xmlns:android="http://schemas.android.com/apk/res/android">


    <ScrollView
        android:layout_height="0dp"
        android:layout_width="match_parent"
        android:layout_weight="1">

        <TextView
            android:layout_height="wrap_content"
            android:layout_width="match_parent"
            android:id="@+id/sensor_list_text"/>

    </ScrollView>
</LinearLayout>

光傳感器

光傳感器比較簡單,智能手機都會具有,讀取光亮度值,單位爲SI lux。維基百科:1流明每平方米麪積,就是1勒克斯(lux)。而SI,即國際單位制,一串法文。小例子很簡單,獲取傳感器,甚至監聽器,將監聽器監聽到的數值在TextView上顯示。

package com.example.sensortest;

import androidx.appcompat.app.AppCompatActivity;

import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;

public class LightSensorActivity extends AppCompatActivity implements SensorEventListener {
    private SensorManager sensorManager=null;
    private Sensor lightSensor=null;
    private TextView tv=null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //使用相同的layout
        setContentView(R.layout.activity_sensor_list);
        tv=(TextView) findViewById(R.id.sensor_list_text);

        //獲取light傳感器
        //先定義管理器
        sensorManager=(SensorManager)getSystemService(SENSOR_SERVICE);
        lightSensor=sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
    }


    //在活動準備好和用戶進行交互的時候調用onResume()方法。此時的活動一定位於返回棧的棧頂,並且處於運行狀態。
    @Override
    protected void onResume() {
        //如果有人監聽傳感器,傳感器工作,需要耗電,因此我們應該只在需要的時候進行監聽
        // 如果activity不在前臺就不需要監聽,因此在onResume()註冊監聽器,在onPause()中註銷監聽器
        sensorManager.registerListener(this,lightSensor,SensorManager.SENSOR_DELAY_NORMAL);
        ///第三個參數是指Sensor Event變化通知的頻率,有效值爲NORMAL,UI,GAME,FASTER。
        //有些傳感器很靈敏,短時間會有大量數據,對內存和垃圾回收造成壓力,可能會導致APP的性能問題,因此根據需要選擇合適的頻率。
        //對於旋轉矢量傳感器,通常需要不斷地去讀取。
        super.onResume();

    }

    //這個方法在系統準備去啓動或者恢復另一個活動的時候調用
    @Override
    protected void onPause() {
        // TODO Auto-generated method stub
        sensorManager.unregisterListener(this,lightSensor);
        super.onPause();
    }


    //SensorEventListener要實現兩個接口onAccuracyChanged()和onSensorChanged()。
    // onAccuracyChanged()會在精度改變或在註冊監聽器時調用。
    // accuracy分爲4檔,0(unreliable),1(low),2(medium),3(high)
    // 注意0並不代表有問題,同時是傳感器需要校準。
    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {
        // TODO Auto-generated method stub
        showInfo(sensor.getName() + " accuracy changed: " + accuracy);
    }


     //onSensorChanged()在傳感器數值發生變化已經註冊監聽器時調用,其更新頻率就是註冊中的參數三。
    // 對於光傳感器,有效數值存放在values[0]中的,單位爲SI lunx。光傳感器通常位於上方(一般靠左側),
     // 除了前置攝像頭外還有一個孔,一般就是它。遮蓋會觸發onSensorChanged()
    @Override
    public void onSensorChanged(SensorEvent event) {
        // TODO Auto-generated method stub
        showInfo("Get Sensor Event: " + event.sensor.getName() + " " + event.values[0] );
    }


    private void showInfo(String info){
        tv.append("\n" + info);
        Log.d("LightSensor",info);
    }

}

結果如下圖所示

近距離傳感器、溫度傳感器、氣壓傳感器

近距離傳感器(Proximity sensor)給出物體與設備之間的距離(cm),或者告知物體是近或是遠。有些近距離傳感器值的解析度與最大值一樣,就是隻返回兩個值,0.0或者最大值。我們無法在安裝設備之前知曉傳感器的類型,因此我們要適配這兩種情況。最大值一般在1.0~6.0。在手機中,近距離傳感器和光傳感器通常是同一物理硬件,但是Android在邏輯上視爲兩個獨立的傳感器。

package com.example.sensortest;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class ProximitySensorActivity extends AppCompatActivity implements SensorEventListener {

    private SensorManager sensorManager=null;
    private Sensor sensor=null;
    private TextView tv=null;


    //定義一個函數在TextView仲顯示信息
    private void showInfo(String info){
        tv.append("\n" + info);
        Log.d("ProximitySensor",info);
    }


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //使用相同的layout
        setContentView(R.layout.activity_sensor_list);
        tv=(TextView) findViewById(R.id.sensor_list_text);
        //返回
        Button bt=(Button) findViewById(R.id.Back_to_Home);
        bt.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View v) {
                startActivity(new Intent(ProximitySensorActivity.this,MainActivity.class));
            }
        });


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

        //檢查解析度和最大值,如果兩者一樣,說明該近距離傳感器智能給出接近和遠離這兩個狀態。
        showInfo("resolution:"+sensor.getResolution());
        showInfo("max value:"+sensor.getMaximumRange());

    }


    //// 如果activity不在前臺就不需要監聽,因此在onResume()註冊監聽器,在onPause()中註銷監聽器
    @Override
    protected void onResume() {
        sensorManager.registerListener(this,sensor,SensorManager.SENSOR_DELAY_NORMAL);
        super.onResume();
    }
    @Override
    protected void onPause() {
        sensorManager.unregisterListener(this,sensor);
        super.onPause();
    }



    //對於近距離傳感器,有效數值存放在values[0]中的,單位爲cm。
    @Override
    public void onSensorChanged(SensorEvent event) {
        showInfo("傳感器事件:"+event.sensor.getName()+""+event.values[0]);

    }

    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {
        showInfo(sensor.getName()+"accuracy changed:"+accuracy);

    }
}

結果如下圖所示

溫度傳感器
溫度傳感器的讀數同樣在values[0],單位爲攝氏度。TYPE_TEMPERATURE讀取的是設備內部的溫度,例如電池的溫度,而TYPE_AMBIENT_TEMPERATURE讀的是設備外部環境的溫度。沒找到支持溫度傳感器的手機,就不提供相關小例子了。

氣壓傳感器
氣壓傳感器(Pressuer Sensor)用於天氣預測或者測量海拔高度,不要將之與觸摸屏的手指壓力向混淆,這裏測量的是大氣壓力,單位爲hPa(millibar),即百帕。在values[0]中讀取。

陀螺儀

陀螺儀(Gyroscope sensor)測量設備轉動的角速度。最早的陀螺儀發明在中國,科學應用則在西方,陀螺儀是爲士大夫坐轎子看書是免收燭光搖曳發明的,這在很久之前一部西方拍的科教片看到,具體名字忘了。Pro Android 4.0中說陀螺儀的誤差會慢慢積累,因此通與加速傳感器一致使用,通過Kalman filter進行修正。接下來只簡單地進行陀螺儀數據的讀取。
 

package com.example.sensortest;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class GyroscopeSensorActivity extends AppCompatActivity implements SensorEventListener {
    private SensorManager sensorManager = null;
    private Sensor sensor = null;
    private TextView tv = null;

    //在華爲P6的機器上,陀螺儀非常敏感,平放在桌面,由於電腦照成的輕微震動在不斷地刷屏,爲了避免寫UI造成的性能問題,可以只寫Log。
    private void showInfo(String info){
        tv.append("\n" + info);
        Log.d("陀螺儀",info);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        //使用相同的layout
        setContentView(R.layout.activity_sensor_list);
        tv = (TextView)findViewById(R.id.sensor_list_text);


        //返回主頁
        Button bt=(Button) findViewById(R.id.Back_to_Home);
        bt.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View v) {
                startActivity(new Intent(GyroscopeSensorActivity.this,MainActivity.class));
            }
        });

        sensorManager = (SensorManager)getSystemService(SENSOR_SERVICE);
        sensor = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);
        showInfo("resolution is " + sensor.getResolution());

    }


    //在onResume()註冊監聽器,在onPause()中註銷監聽器
    @Override
    protected void onResume() {
        sensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_UI);
        super.onResume();
    }
    @Override
    protected void onPause() {
        sensorManager.unregisterListener(this,sensor);
        super.onPause();
    }


    //重寫的方法

    //對於陀螺儀,測量的是x、y、z三個軸向的角速度,分別從values[0]、values[1]、values[2]中讀取,單位爲弧度/秒。
    @Override
    public void onSensorChanged(SensorEvent event) {
        if(event.sensor.getType() == Sensor.TYPE_GYROSCOPE)
            showInfo("傳感器測量結果:" + " x:" + event.values[0] + " y:" + event.values[1]
                    + " z:" + event.values[2]);
    }

    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {
        showInfo(sensor.getName() + " accuracy changed: " + accuracy);
    }
}

 

加速傳感器

加速度測量傳感器有x、y、z三軸,注意和2D屏幕的座標,以左上角作爲原點的,而且Y向下。注意區分這兩個不同的座標系。

加速傳感器的單位是加速度m/s2。如果手機平放好,x,y在位置爲0,而z軸方向加速度=當前z方向加速度-g。由於g(重力加速度)垂直向下,則g=-9.81m/s2,即z軸 a=0-(-9.81)=9.81m/s2。也就是自由落體是爲0。x、y、z的測量的加速度分別位於value[0]、value[1]、value[2]。

傳感器類型爲Sensor.TYPE_ACCELEROMETER。減去重力這很重要,當設備靜止或者勻速運動時,可以獲得設備的角度。

package com.example.sensortest;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.Surface;
import android.view.View;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.TextView;

public class AccelerometerSensorActivity extends AppCompatActivity implements SensorEventListener {
    private SensorManager sensorManager = null;
    private Sensor sensor = null,gravitySensor = null,linearAcceleSensor = null;
    private TextView tv = null;
    private WindowManager window = null;

//    private void showInfo(String info){
//        tv.append("\n" + info);
//        Log.d("加速度儀",info);
//    }
    private void showInfo1(String info){
        tv.setText(info + "\n" + tv.getText());
        tv.append("\n" + info);
        Log.d("加速度儀",info);
    }


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //使用相同的layout
        setContentView(R.layout.activity_sensor_list);
        tv = (TextView)findViewById(R.id.sensor_list_text);


        //返回主頁
        Button bt=(Button) findViewById(R.id.Back_to_Home);
        bt.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View v) {
                startActivity(new Intent(AccelerometerSensorActivity.this,MainActivity.class));
            }
        });

        //定義傳感器管理器
        sensorManager = (SensorManager)getSystemService(SENSOR_SERVICE);

        sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
        gravitySensor = sensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY);//重力傳感器(獲取重力傳感器的值的目標是減去重力)
        linearAcceleSensor = sensorManager.getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION);//線性加速度

        showInfo1("resolution is " + sensor.getResolution());
        showInfo1("API爲" + Build.VERSION.SDK_INT);
        window = (WindowManager)getSystemService(WINDOW_SERVICE);
        //返回值爲Surface.ROTATION_0(0)、Surface.ROTATION_90(1)、Surface.ROTATION_180(2)和Surface.ROTATION_270(3),
        //可以用來確定屏幕UI的旋轉方向。
        // 注意:需要開啓“自動旋轉”纔能有效檢查,否則均爲Surface.ROTATION_0(手機以豎屏爲主,一般都會0,但不保證都如此)。
        // 不是所以的手機都能檢測到這4個值,例如P6,沒有Surface.ROTATION_180,即UI不支持倒過來,如果有某個數值不支持,通過getRotation()獲取的數值可能並不準確,仍以P6爲例,如果我們順時針轉90°,得到Surface.ROTATION_90,繼續順時針轉至180°,無檢測新數值,仍未Surface.ROTATION_90,再繼續順時針轉90°(至270°),仍顯示爲Surface.ROTATION_90,而非Surface.ROTATION_270。
        showRotation();//展示旋轉角

    }


    //在onResume()註冊監聽器,在onPause()中註銷監聽器
    @Override
    protected void onResume() {
        sensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_UI);
        sensorManager.registerListener(this, gravitySensor, SensorManager.SENSOR_DELAY_UI);
        sensorManager.registerListener(this, linearAcceleSensor, SensorManager.SENSOR_DELAY_UI);
        super.onResume();
    }
    @Override
    protected void onPause() {
        sensorManager.unregisterListener(this);
        super.onPause();
    }


    private int count = 1;
    // 對於加速器,測量的是x、y、z三個軸向的加速度,分別從values[0]、values[1]、values[2]中讀取。
    @Override
    public void onSensorChanged(SensorEvent event) {
        if (count++%40==0){//還不到40個計數時
            int type = event.sensor.getType();
            if(type == Sensor.TYPE_ACCELEROMETER){
                showInfo1("加速器:" + " x:" + event.values[0] + " y:" + event.values[1]+ " z:" + event.values[2]);
            }else if(type == Sensor.TYPE_GRAVITY){
                showInfo1("重力儀:" + " x:" + event.values[0] + " y:" + event.values[1]+ " z:" + event.values[2]);
            }else if(type == Sensor.TYPE_LINEAR_ACCELERATION){
                showInfo1("線性加速儀" + " x:" + event.values[0] + " y:" + event.values[1] + " z:" + event.values[2]);
            }
            count = 1;
        }

    }

    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {
        // TODO Auto-generated method stub
        showInfo1(sensor.getName() + " accuracy changed: " + accuracy);

    }

    //將角度顯示出來
    private void showRotation(){
        int rotation = window.getDefaultDisplay().getRotation();//獲取旋轉的方向
        switch(rotation){
            case Surface.ROTATION_0:
                showInfo1("方向:ROTATION 0(" + rotation + ")");
                break;
            case Surface.ROTATION_90:
                showInfo1("方向:ROTATION 90(" + rotation + ")");
                break;
            case Surface.ROTATION_180:
                showInfo1("方向:ROTATION 180(" + rotation + ")");
                break;
            case Surface.ROTATION_270:
                showInfo1("方向:ROTATION 270(" + rotation + ")");
                break;
            default:
                showInfo1("方向:(" + rotation + ")");
                break;
        }

    }
}

重力測試

package com.example.sensortest;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class GravityActivity extends AppCompatActivity implements SensorEventListener {
    private SensorManager sensorManager = null;
    private Sensor sensor = null;
    private TextView tv = null;

//    private void showInfo(String info){
//        tv.append("\n" + info);
//        Log.d("重力",info);
//    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //使用相同的layout
        setContentView(R.layout.activity_sensor_list);
        tv = (TextView)findViewById(R.id.sensor_list_text);

        //返回主頁
        Button bt=(Button) findViewById(R.id.Back_to_Home);
        bt.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View v) {
                startActivity(new Intent(GravityActivity.this,MainActivity.class));
            }
        });

        sensorManager = (SensorManager)getSystemService(SENSOR_SERVICE);
        sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);

    }


    //在onResume()註冊監聽器,在onPause()中註銷監聽器
    @Override
    protected void onResume() {
        // TODO Auto-generated method stub
        super.onResume();
        sensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_UI);
    }
    @Override
    protected void onPause() {
        sensorManager.unregisterListener(this,sensor);
        super.onPause();
    }



/////////////////////////////////////////////////
    private float[] gravity = new float[3];
    private float[] motion  = new float[3];
    private double ratioY;
    private double angle;
    private int counter = 1;


    @Override
    public void onSensorChanged(SensorEvent event) {
        for (int i=0;i<3;i++){
            //accelermeter是很敏感的,看之前小例子的log就知道。
            // 因爲重力是恆力,我們移動設備,它的變化不會太快,不象搖晃手機這樣的外力那樣突然
            // 因此通過low-pass filter對重力進行過濾
            gravity[i] = (float) (0.1 * event.values[i] + 0.9 * gravity[i]);
            motion[i] = event.values[i] - gravity[i];
        }
        //計算重力在Y軸方向的量,即G*cos(α)
        ratioY = gravity[1]/SensorManager.GRAVITY_EARTH;
        if(ratioY > 1.0)
            ratioY = 1.0;
        if(ratioY < -1.0)
            ratioY = -1.0;
        //獲得α的值,根據z軸的方向修正正負值。
        angle = Math.toDegrees(Math.acos(ratioY));
        if(gravity[2] < 0)
            angle = - angle;

        //避免掃屏,每10次變化顯示一次值
        if(counter ++ % 10 == 0){
            tv.setText("Raw Values : \n"
                    +  "   x,y,z = "+ event.values[0] + "," + event.values[1] + "," + event.values[2] + "\n"
                    +  "Gravity values : \n"
                    +  "   x,y,z = "+ gravity[0] + "," + gravity[1] + "," + gravity[2] + "\n"
                    +  "Motion values : \n"
                    +  "   x,y,z = "+ motion[0] + "," + motion[1] + "," + motion[2] + "\n"
                    +  "Y軸角度 :" + angle	);
            tv.invalidate();
            counter = 1;
        }
    }

    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {
//        showInfo(sensor.getName() + " accuracy changed: " + accuracy);
    }
}

 

磁場傳感器

磁場傳感器可以用來檢測磁場大小,和加速度傳感器一樣,有x、y、z軸三個方向,單位爲uT(microteslas)。磁場傳感器也稱爲compass(指南針),在uses-feature中使用android.hardware.sensor.compass作爲其名字。

下面代碼可以獲得磁場的值

package com.example.sensortest;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class MagneticFieldSensorActivity extends AppCompatActivity implements SensorEventListener {

    private SensorManager sensorManager = null;
    private Sensor sensor = null;
    private TextView tv = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        //使用相同的layout
        setContentView(R.layout.activity_sensor_list);
        tv = (TextView)findViewById(R.id.sensor_list_text);


        //返回主頁
        Button bt=(Button) findViewById(R.id.Back_to_Home);
        bt.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View v) {
                startActivity(new Intent(MagneticFieldSensorActivity.this,MainActivity.class));
            }
        });


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


    //在onResume()註冊監聽器,在onPause()中註銷監聽器
    protected void onPause() {
        // TODO Auto-generated method stub
        sensorManager.unregisterListener(this, sensor);
        super.onPause();
    }

    @Override
    protected void onResume() {
        sensorManager.registerListener(this, sensor,SensorManager.SENSOR_DELAY_UI);
        super.onResume();
    }



    private int count = 1;

    @Override
    public void onSensorChanged(SensorEvent event) {
        if (count++==20){//由於不穩定,所以每20次,再輸出一次
            double value = Math.sqrt(event.values[0]*event.values[0] + event.values[1]*event.values[1]
                    +event.values[2]*event.values[2]);
            String str = String.format("X:%8.4f , Y:%8.4f , Z:%8.4f \n總值爲:%8.4f", event.values[0],event.values[1],event.values[2],value);

            count = 1;
            tv.setText(str);
            Log.d("磁場感應器",str);
        }

    }

    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {

    }
}

方位傳感器(Orientation Sensors)

磁場傳感器和加速度傳感器結合可獲得設備擺放角度,兩者結合可以獲取方位。這些計算由Android代勞了,SensorManager提供getRotationMatrix(),獲得轉動的矩陣,並進一步通過getOrientation()獲得方位矩陣。Android還有方位傳感器(orientation Sensors),不是物理實體,而是通過acceleration傳感器和磁場感應器來獲取方位,而在Android2.2開始方位傳感器就被deprecated了。

設備擺放情況通過azimuth、pitch和roll來表示。

azimuth即方位角,就是手機方向和正北的夾角,百度百科這樣描述方位角:是從某點的指北方向線起,依順時針方向到目標方向線之間的水平夾角。pitch和roll可能是引用了航天的術語。azimuth,pitch和roll分別是z軸、X軸和Y軸的旋轉角度。

老方法採用orientation傳感器,azimuth從0~360。pitch是在x軸方向的轉動角度,其實就是Y軸和水平面的仰角,範圍爲-180~180,正的爲朝下(手機頭低於水平面),負的爲朝上,pitch的方向逆時針爲正。roll是Y軸防線的轉動角度,實際就是X軸和水平面的角度,範圍在-90~90,同樣逆時針爲正,右軸高於左軸時爲正,右軸高於左軸時爲負。

新的方法,azimuth的範圍是-180~180,當然我們可以進行適當的處理,如果小於零,則加360,這樣就可以和orientation的得到的值一樣。需要注意的是pitch的範圍是-90~90,機頭上翹爲負;roll的範圍是-180~180,和老方法相反,右軸高時爲負。

這兩種方式的數值可能會使人有些迷糊,我們在使用之前先查文檔弄清楚就是了。我曾經有個疑惑爲何有兩個數值的範圍是360度,其中一個數值只有180度。想想球面就知道了,以地球儀爲例,經度範圍是360°,維度範圍是180°,就可以確定球面上的任何一點,以球心到該點假設是手機中軸線,還有一個圍繞Y軸360°轉的角度的第三維,這就可以確定所有的排放方式。

package com.example.sensortest;

import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;

import android.Manifest;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.hardware.GeomagneticField;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.location.Criteria;
import android.location.Location;
import android.location.LocationManager;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.Surface;
import android.view.View;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.ToggleButton;

public class VirtualJax extends AppCompatActivity implements SensorEventListener {

//    private ToggleButton toggleButton = null;//一個可選的按鈕選項
    private TextView oldOne = null, nowOne = null;//兩種不同的方法獲得方向數據
    private SensorManager sensorManager = null;
    private Sensor accelSensor = null, compassSensor = null, orientSensor = null, rotVecSensor = null;
    //定義數組將方向傳感器的數據放於數組中
    private float[] accelValues = new float[3], compassValues = new float[3], orientValues = new float[3], rotVecValues = null;
    private int mRotation;
    private LocationManager locManager = null;

    @SuppressWarnings("deprecation")//表示不檢測過期的方法
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_virtual_jax);//跟之前的幾個activity不同,這裏定義了新的activity

        //返回主頁
        Button bt=(Button) findViewById(R.id.Back_to_Home);
        bt.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View v) {
                startActivity(new Intent(VirtualJax.this,MainActivity.class));
            }
        });

        oldOne = (TextView) findViewById(R.id.orientation);//採用orientation傳感器
        nowOne = (TextView) findViewById(R.id.preferred);//推薦的方式
//        toggleButton = (ToggleButton) findViewById(R.id.toggle);

        //定義傳感器管理器
        sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
        //定義四個傳感器
        accelSensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);//加速度傳感器
        compassSensor = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);//地磁傳感器
        orientSensor = sensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION);//方位傳感器
        rotVecSensor = sensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR);//旋轉角度傳感器???


//        //當橫屏的時候應該對座標進行修訂
//        WindowManager window = (WindowManager) this.getSystemService(WINDOW_SERVICE);
//        //檢驗當前的版本號
//        if (Build.VERSION.SDK_INT < 8)
//            mRotation = window.getDefaultDisplay().getOrientation();
//        else
//            mRotation = window.getDefaultDisplay().getRotation();
//
//        locManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);

    }


    //在onResume()註冊監聽器,在onPause()中註銷監聽器
    @Override
    protected void onResume() {
        // TODO Auto-generated method stub
//        isAllowRemap = toggleButton.isChecked();
        sensorManager.registerListener(this, accelSensor, SensorManager.SENSOR_DELAY_GAME);
        sensorManager.registerListener(this, compassSensor, SensorManager.SENSOR_DELAY_GAME);
        sensorManager.registerListener(this, orientSensor, SensorManager.SENSOR_DELAY_GAME);
        sensorManager.registerListener(this, rotVecSensor, SensorManager.SENSOR_DELAY_GAME);
        super.onResume();
    }

    @Override
    protected void onPause() {
        // TODO Auto-generated method stub
        sensorManager.unregisterListener(this, accelSensor);
        sensorManager.unregisterListener(this, compassSensor);
        sensorManager.unregisterListener(this, orientSensor);
        sensorManager.unregisterListener(this, rotVecSensor);
        super.onPause();
    }




    ///////////////////////////////////////////////////////********************************//////////////////////////////
    private boolean ready = false; //檢查是否同時具有加速度傳感器和磁場傳感器
    private float[] inR = new float[9], outR = new float[9];
    private float[] inclineMatrix = new float[9];
    private float[] prefValues = new float[3];
    private double mInclination;
    private int count = 1;
    private float[] rotvecR = new float[9], rotQ = new float[4];
    private float[] rotvecOrientValues = new float[3];

    @SuppressWarnings("deprecation")//表示不檢測過期的方法
    @Override
    public void onSensorChanged(SensorEvent event) {
        // TODO Auto-generated method stub
        //將相關傳感器的數值分別讀入accelValues,compassValues(磁力感應器的數值)和orientValues和rotVecValues數組中
        switch (event.sensor.getType()) {//獲取傳感器的類型
            case Sensor.TYPE_ACCELEROMETER://當時加速度傳感器時
                for (int i = 0; i < 3; i++) {
                    accelValues[i] = event.values[i];//將三個值分別放於accelValues中
                }
                if (compassValues[0] != 0) //即accelerator和magnetic傳感器都有數值
                    ready = true;//此時檢測同時具有加速度傳感器與地磁傳感器
                break;

            case Sensor.TYPE_MAGNETIC_FIELD://獲取地磁傳感器的值
                for (int i = 0; i < 3; i++) {
                    compassValues[i] = event.values[i];//將三個值分別放於compassValues中
                }
                if (accelValues[2] != 0) //即accelerator和magnetic傳感器都有數值,換一個軸向檢查
                    ready = true;//此時檢測同時具有加速度傳感器與地磁傳感器
                break;

            case Sensor.TYPE_ORIENTATION://如果是方向傳感器
                for (int i = 0; i < 3; i++) {
                    orientValues[i] = event.values[i];//將三個值分別放於orientValues中
                }
                break;

            case Sensor.TYPE_ROTATION_VECTOR://對於旋轉傳感器
                if (rotVecValues == null) {
                    rotVecValues = new float[event.values.length];
                }
                for (int i = 0; i < rotVecValues.length; i++) {
                    rotVecValues[i] = event.values[i];
                }
                break;
        }

        if (!ready)//此時如果沒有有加速度與地磁傳感器,則退出返回
            return;

        //計算:inclination matrix 傾角矩陣 I(inclineMatrix) 以及 the rotation matrix 旋轉矩陣 R(inR)
        //根據加速傳感器的數值accelValues[3]和磁力感應器的數值compassValues[3],進行矩陣計算,獲得方位
        if (SensorManager.getRotationMatrix(inR, inclineMatrix, accelValues, compassValues)) {

            //下面是旋轉屏幕的情況,此處不用
//            if (isAllowRemap && mRotation == Surface.ROTATION_90) {
//                //參數二表示設備X軸成爲新座標的Y軸,參數三表示設備的Y軸成爲新座標-x軸(方向相反)
//                SensorManager.remapCoordinateSystem(inR, SensorManager.AXIS_Y, SensorManager.AXIS_MINUS_X, outR);
//                SensorManager.getOrientation(outR, prefValues);
//            } else {

				/* Computes the device's orientation based on the rotation matrix.
				 * 	When it returns, the array values is filled with the result:
				 * 根據rotation matrix計算設備的方位。,範圍數組:
				values[0]: azimuth, rotation around the Z axis.
				values[1]: pitch, rotation around the X axis.
				values[2]: roll, rotation around the Y axis.*/
                SensorManager.getOrientation(inR, prefValues);//根據rotation matrix計算設備的方位
//            }
            //根據inclination matrix計算磁仰角。
            //計算磁仰角:地球表面任一點的地磁場總強度的矢量方向與水平面的夾角。
            mInclination = SensorManager.getInclination(inclineMatrix);

            //顯示測量值
            if (count++ % 100 == 0) {
                doUpdate(null);
                count = 1;
            }

        } else {
            Toast.makeText(this, "無法獲得矩陣(SensorManager.getRotationMatrix)", Toast.LENGTH_LONG);
            finish();
        }

        if (rotVecValues != null) {
            SensorManager.getQuaternionFromVector(rotQ, rotVecValues);
            SensorManager.getRotationMatrixFromVector(rotvecR, rotVecValues);
            SensorManager.getOrientation(rotvecR, rotvecOrientValues);
        }
    }


    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {
        // TODO Auto-generated method stub
    }

    //定義一個函數來顯示測量值
    public void doUpdate(View v) {
        if (!ready)
            return;

        //preValues[0]是方位角,範圍是-pi到pi,通過Math.toDegrees轉換爲角度
        float mAzimuth = (float) Math.toDegrees(prefValues[0]);//方角位,地平經度
		/*//糾正爲orientation的數值。
		 * if(mAzimuth < 0)
			mAzimuth += 360.0;*/


        String msg = String.format("Acceleration sensor + magnetic sensor:\nazimuth:%7.3f\npitch: %7.3f\nroll: %7.3f\n地磁仰角:%7.3f\n重適配座標=%s\n%s\n",
                mAzimuth, Math.toDegrees(prefValues[1]), Math.toDegrees(prefValues[2]),
                Math.toDegrees(mInclination),
                (isAllowRemap && mRotation == Surface.ROTATION_90) ? "true" : "false", info);

        if (rotvecOrientValues != null && mRotation == Surface.ROTATION_0) {
            msg += String.format("Rotation Vector Sensor:\nazimuth %7.3f\npitch %7.3f\nroll %7.3f\nw,x,y,z %6.2f,%6.2f,%6.2f,%6.2f\n",
                    Math.toDegrees(rotvecOrientValues[0]),
                    Math.toDegrees(rotvecOrientValues[1]),
                    Math.toDegrees(rotvecOrientValues[2]),
                    rotQ[0], rotQ[1], rotQ[2], rotQ[3]);
            //Log.d("WEI","Quaternion w,x,y,z=" + rotQ[0] + "," + rotQ[1] + "," + rotQ[2] + "," + rotQ[3]);
        }
        nowOne.setText(msg);

        msg = String.format("Orientation Sensor:\nazimuth:%7.3f\npitch: %7.3f\nroll: %7.3f",
                orientValues[0], orientValues[1], orientValues[2]);
        oldOne.setText(msg);

    }

    private boolean isAllowRemap = false;

    public void doToggle(View v) {
        isAllowRemap = ((ToggleButton) v).isChecked();
    }

    private String info = "";

    public void doGeoNorth(View v) {
        if (!ready)
            return;

        String providerName = locManager.getBestProvider(new Criteria(), true);
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            // TODO: Consider calling
            //    ActivityCompat#requestPermissions
            // here to request the missing permissions, and then overriding
            //   public void onRequestPermissionsResult(int requestCode, String[] permissions,
            //                                          int[] grantResults)
            // to handle the case where the user grants the permission. See the documentation
            // for ActivityCompat#requestPermissions for more details.
            return;
        }
        Location loc = locManager.getLastKnownLocation(providerName);


        if(loc == null && locManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)){
            //請注意,設備要打開網絡定位的選項。在室內,由於不容易搜索到GPS,建議採用network方式。
            //否則,locManager.isProviderEnabled("network")爲false,不能使用網絡方式,而GPS在室內搜半天衛星都不一定有
            providerName = LocationManager.NETWORK_PROVIDER;
            loc = locManager.getLastKnownLocation(providerName);
        }
        if(loc == null)
            return;

        info = "定位:"+ providerName+ "\n"+ String.format(" %9.5f,%9.5f",(float)loc.getLongitude(),(float)loc.getLatitude())+"\n";
        Log.d("WEI","" + loc);


        GeomagneticField geo = new GeomagneticField((float)loc.getLatitude(),(float)loc.getLongitude(),
                (float)loc.getAltitude(),System.currentTimeMillis());

        float declination = geo.getDeclination();
        info += String.format("磁偏角:%7.3f\n", declination);
    }


}

 

參考資料

https://blog.csdn.net/fukaimei/article/details/78321851

https://www.csdn.net/gather_2f/MtTakg5sMDkzNi1ibG9n.html

https://blog.csdn.net/flowingflying/article/details/6212512(主要參考代碼)

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章