Android指南针应用
目的:通過(guò)指南針應(yīng)用來(lái)學(xué)習(xí)SensorManager、LocationManger的使用以及對(duì)android 6.0動(dòng)態(tài)權(quán)限的適配
一、通過(guò)android的方向傳感器獲取手機(jī)方位
通過(guò)對(duì)比前一刻方位和現(xiàn)在手機(jī)方位算出手機(jī)旋轉(zhuǎn)的角度,然后根據(jù)手機(jī)實(shí)際旋轉(zhuǎn)的角度去旋轉(zhuǎn)指南針的圖片。
一般情況下,在android系統(tǒng)中獲取手機(jī)的方位信息是很簡(jiǎn)單的事情,在api中有TYPE_ORIENTATION常量, 可以像得到加速度傳感器那樣得到方向傳感器mSensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION); 然而從官網(wǎng)的描述中我們可以看到:“TYPE_ORIENTATION This constant was deprecated in API level 8. use SensorManager.getOrientation() instead. ” 即這種方式已經(jīng)過(guò)期啦,不建議使用!Google建議我們?cè)趹?yīng)用程序中使用SensorManager.getOrientation()來(lái)獲得原始數(shù)據(jù)。
看一下官網(wǎng)中對(duì)于getOrientation()的定義:
public static float[] getOrientation (float[] R, float[] values)
Computes the device’s orientation based on the rotation matrix.
When it returns, the array values is filled with the result:
- values[0]: azimuth, rotation around the -Z axis, i.e. the opposite direction of Z axis.(azimuth 方向角,但用(磁場(chǎng)+加速度)得到的數(shù)據(jù)范圍是(-180~180),也就是說(shuō),0表示正北,90表示正東,180/-180表示正南,-90表示正西。而直接通過(guò)方向感應(yīng)器數(shù)據(jù)范圍是(0~359)360/0表示正北,90表示正東,180表示正南,270表示正西。)
- values[1]: pitch, rotation around the -X axis, i.e the opposite direction of X axis.( pitch 傾斜角 即由靜止?fàn)顟B(tài)開始,前后翻轉(zhuǎn))
- values[2]: roll, rotation around the Y axis. (roll 旋轉(zhuǎn)角 即由靜止?fàn)顟B(tài)開始,左右翻轉(zhuǎn))
Applying these three intrinsic rotations in azimuth, pitch and roll order transforms identity matrix to the rotation
matrix given in input R. All three angles above are in radians and positive in the counter-clockwise direction. Range
of output is: azimuth from -π to π, pitch from -π/2 to π/2 and roll from -π to π.
第一個(gè)參數(shù)是R[] 是一個(gè)旋轉(zhuǎn)矩陣,用來(lái)保存磁場(chǎng)和加速度的數(shù)據(jù),可以理解為這個(gè)函數(shù)的傳入值,通過(guò)它這個(gè)函數(shù)給你求出方位角。
第二個(gè)參數(shù)就是這個(gè)函數(shù)的輸出了,他有函數(shù)自動(dòng)為我們填充,這就是我們想要的。
這個(gè)R[]呢,是通過(guò)SensorManager的另一個(gè)函數(shù)getRotationMatrix 得到的,
public static boolean getRotationMatrix (float[] R, float[] I, float[] gravity, float[] geomagnetic)
解釋一下這四個(gè)參數(shù),第一個(gè)就是我們需要填充的R數(shù)組,大小是9
第二個(gè)是是一個(gè)轉(zhuǎn)換矩陣,將磁場(chǎng)數(shù)據(jù)轉(zhuǎn)換進(jìn)實(shí)際的重力坐標(biāo)中 一般默認(rèn)情況下可以設(shè)置為null
第三個(gè)是一個(gè)大小為3的數(shù)組,表示從加速度感應(yīng)器獲取來(lái)的數(shù)據(jù) 在onSensorChanged中
第四個(gè)是一個(gè)大小為3的數(shù)組,表示從磁場(chǎng)感應(yīng)器獲取來(lái)的數(shù)據(jù) 在onSensorChanged中
使用SensorManger還要注意一點(diǎn),當(dāng)不需要方向傳感器的時(shí)候,要其實(shí)關(guān)閉,尤其是Activity 調(diào)用了onPause()生命周期之后,否則會(huì)非常耗電!
Always make sure to disable sensors you don’t need, especially when your activity is paused. Failing to do so can drain the battery in just a few hours.
二、獲取海拔高度
獲取海拔高度用到的是locationManager,代碼寫的很清楚,但是這里要注意的是,如果你的API版本大于23,在使用locationManger的時(shí)候,可能會(huì)看到以下的報(bào)錯(cuò)信息:
Call requires permission which may be rejected by user: code should explicitly check to see if permission is available (with checkPermission) or explicitly handle a potential SecurityException
我們都知道,這是android 6.0 的新特性,當(dāng)APP需要使用一些敏感權(quán)限時(shí),會(huì)對(duì)用戶進(jìn)行提示,同時(shí)代碼中也要做相應(yīng)處理
/*** 適配android 6.0 檢查權(quán)限*/ private boolean checkLocationPermission() {if (Build.VERSION.SDK_INT >= 23) {return (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) ==PackageManager.PERMISSION_GRANTED &&ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) ==PackageManager.PERMISSION_GRANTED);}return true;}主要代碼:
/*** 指南針Activity* Created by Jundooong on 2016/05/10.*/ public class MainActivity extends AppCompatActivity implements SensorEventListener {public static final String TAG = "MainActivity"; private static final int EXIT_TIME = 2000;// 兩次按返回鍵的間隔判斷 private SensorManager mSensorManager; private Sensor mAccelerometer; private Sensor mMagneticField; private LocationManager mLocationManager; private String mLocationProvider;// 位置提供者名稱,GPS設(shè)備還是網(wǎng)絡(luò) private float mCurrentDegree = 0f; private float[] mAccelerometerValues = new float[3]; private float[] mMagneticFieldValues = new float[3]; private float[] mValues = new float[3]; private float[] mMatrix = new float[9];private long firstExitTime = 0L;// 用來(lái)保存第一次按返回鍵的時(shí)間private TextView mTvCoord; private LinearLayout mLlLocation; private TextView mTvAltitude; private ImageView mIvCompass;@Override protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);initService();findViews(); }private void findViews() {mIvCompass = (ImageView) findViewById(R.id.iv_compass);mTvCoord = (TextView) findViewById(R.id.tv_coord);mTvAltitude = (TextView) findViewById(R.id.tv_altitude);mLlLocation = (LinearLayout) findViewById(R.id.ll_location);mLlLocation.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {initLocationService();updateLocationService();}}); }private void initService() {initSensorService();initLocationService(); }private void initSensorService() {mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);mMagneticField = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD); }private void initLocationService() {mLocationManager = (LocationManager) getSystemService(LOCATION_SERVICE);Criteria criteria = new Criteria();// 條件對(duì)象,即指定條件過(guò)濾獲得LocationProvidercriteria.setAccuracy(Criteria.ACCURACY_FINE);// 較高精度criteria.setAltitudeRequired(true);// 是否需要高度信息criteria.setBearingRequired(true);// 是否需要方向信息criteria.setCostAllowed(true);// 是否產(chǎn)生費(fèi)用criteria.setPowerRequirement(Criteria.POWER_LOW);// 設(shè)置低電耗mLocationProvider = mLocationManager.getBestProvider(criteria, true);// 獲取條件最好的Provider,若沒有權(quán)限,mLocationProvider 為nullLog.e(TAG, "mLocationProvider = " + mLocationProvider); }@Override protected void onResume() {super.onResume();registerService();}private void registerService() {registerSensorService();updateLocationService(); }private void registerSensorService() {mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_NORMAL);mSensorManager.registerListener(this, mMagneticField, SensorManager.SENSOR_DELAY_NORMAL); }private void updateLocationService() {if (!checkLocationPermission()) {mTvCoord.setText(R.string.check_location_permission);return;}if (mLocationProvider != null) {updateLocation(mLocationManager.getLastKnownLocation(mLocationProvider));mLocationManager.requestLocationUpdates(mLocationProvider, 2000, 10, mLocationListener);// 2秒或者距離變化10米時(shí)更新一次地理位置} else {mTvCoord.setText(R.string.cannot_get_location);} }@Override protected void onPause() {super.onPause();unregister(); }private void unregister() {if (mSensorManager != null) {mSensorManager.unregisterListener(this);}if (mLocationManager != null) {if (!checkLocationPermission()) {return;}mLocationManager.removeUpdates(mLocationListener);} }@Override public void onSensorChanged(SensorEvent event) {if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {mAccelerometerValues = event.values;}if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) {mMagneticFieldValues = event.values;}//調(diào)用getRotaionMatrix獲得變換矩陣mMatrix[]SensorManager.getRotationMatrix(mMatrix, null, mAccelerometerValues, mMagneticFieldValues);SensorManager.getOrientation(mMatrix, mValues);//經(jīng)過(guò)SensorManager.getOrientation(R, values);得到的values值為弧度//values[0] :azimuth 方向角,但用(磁場(chǎng)+加速度)得到的數(shù)據(jù)范圍是(-180~180),也就是說(shuō),0表示正北,90表示正東,180/-180表示正南,-90表示正西。// 而直接通過(guò)方向感應(yīng)器數(shù)據(jù)范圍是(0~359)360/0表示正北,90表示正東,180表示正南,270表示正西。float degree = (float) Math.toDegrees(mValues[0]);setImageAnimation(degree);mCurrentDegree = -degree; }// 設(shè)置指南針圖片的動(dòng)畫效果 private void setImageAnimation(float degree) {RotateAnimation ra = new RotateAnimation(mCurrentDegree, -degree, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,0.5f);ra.setDuration(200);ra.setFillAfter(true);mIvCompass.startAnimation(ra); }/*** 適配android 6.0 檢查權(quán)限*/ private boolean checkLocationPermission() {if (Build.VERSION.SDK_INT >= 23) {return (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) ==PackageManager.PERMISSION_GRANTED &&ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) ==PackageManager.PERMISSION_GRANTED);}return true;}/*** 更新位置信息*/ private void updateLocation(Location location) {Log.e(TAG, "location = " + location);if (null == location) {mTvCoord.setText(getString(R.string.cannot_get_location));mTvAltitude.setVisibility(View.GONE);} else {mTvAltitude.setVisibility(View.VISIBLE);StringBuilder sb = new StringBuilder();double longitude = location.getLongitude();double latitude = location.getLatitude();double altitude = location.getAltitude();if (latitude >= 0.0f) {sb.append(getString(R.string.location_north, latitude));} else {sb.append(getString(R.string.location_south, (-1.0 * latitude)));}sb.append(" ");if (longitude >= 0.0f) {sb.append(getString(R.string.location_east, longitude));} else {sb.append(getString(R.string.location_west, (-1.0 * longitude)));}mTvCoord.setText(getString(R.string.correct_coord, sb.toString()));mTvAltitude.setText(getString(R.string.correct_altitude, altitude));}}LocationListener mLocationListener = new LocationListener() {@Overridepublic void onLocationChanged(Location location) {updateLocation(location);}@Overridepublic void onStatusChanged(String provider, int status, Bundle extras) {if (status != LocationProvider.OUT_OF_SERVICE) {if (!checkLocationPermission()) {mTvCoord.setText(R.string.check_location_permission);return;}updateLocation(mLocationManager.getLastKnownLocation(mLocationProvider));} else {mTvCoord.setText(R.string.check_location_permission);}}@Overridepublic void onProviderEnabled(String provider) {}@Overridepublic void onProviderDisabled(String provider) {} };@Override public void onAccuracyChanged(Sensor sensor, int accuracy) {}@Override public void onBackPressed() {long curTime = System.currentTimeMillis();if (curTime - firstExitTime < EXIT_TIME) {finish();} else {Toast.makeText(this, R.string.exit_toast, Toast.LENGTH_SHORT).show();firstExitTime = curTime;}}}
完整代碼地址:https://github.com/UserWang/Android-ConcisionCompass
參考文章:http://developer.android.com/intl/zh-cn/reference/android/hardware/SensorManager.html
http://blog.csdn.net/microliang/article/details/15815091
總結(jié)
以上是生活随笔為你收集整理的Android指南针应用的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 点击tab栏如何让tab置顶
- 下一篇: Android Sensor感应器介绍,