目的:通过指南针应用来学习SensorManager、LocationManger的使用以及对android 6.0动态权限的适配

一、通过android的方向传感器获取手机方位

通过对比前一刻方位和现在手机方位算出手机旋转的角度,然后根据手机实际旋转的角度去旋转指南针的图片。

一般情况下,在android系统中获取手机的方位信息是很简单的事情,在api中有TYPE_ORIENTATION常量,
可以像得到加速度传感器那样得到方向传感器mSensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION);
然而从官网的描述中我们可以看到:“TYPE_ORIENTATION  This constant was deprecated in API level 8.
use SensorManager.getOrientation() instead. ” 即这种方式已经过期啦,不建议使用!

Google建议我们在应用程序中使用SensorManager.getOrientation()来获得原始数据。

看一下官网中对于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 方向角,但用(磁场+加速度)得到的数据范围是(-180~180),也就是说,0表示正北,90表示正东,180/-180表示正南,-90表示正西。而直接通过方向感应器数据范围是(0~359)360/0表示正北,90表示正东,180表示正南,270表示正西。)
  • values[1]: pitch, rotation around the -X axis, i.e the opposite direction of X axis.( pitch 倾斜角 即由静止状态开始,前后翻转)
  • values[2]: roll, rotation around the Y axis. (roll 旋转角 即由静止状态开始,左右翻转)

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 π.

第一个参数是R[] 是一个旋转矩阵,用来保存磁场和加速度的数据,可以理解为这个函数的传入值,通过它这个函数给你求出方位角。
第二个参数就是这个函数的输出了,他有函数自动为我们填充,这就是我们想要的。
这个R[]呢,是通过SensorManager的另一个函数getRotationMatrix 得到的,

public static boolean getRotationMatrix (float[] R, float[] I, float[] gravity, float[] geomagnetic)

解释一下这四个参数,第一个就是我们需要填充的R数组,大小是9
第二个是是一个转换矩阵,将磁场数据转换进实际的重力坐标中 一般默认情况下可以设置为null
第三个是一个大小为3的数组,表示从加速度感应器获取来的数据 在onSensorChanged中
第四个是一个大小为3的数组,表示从磁场感应器获取来的数据 在onSensorChanged中

使用SensorManger还要注意一点,当不需要方向传感器的时候,要其实关闭,尤其是Activity 调用了onPause()生命周期之后,否则会非常耗电!

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的时候,可能会看到以下的报错信息:

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 的新特性,当APP需要使用一些敏感权限时,会对用户进行提示,同时代码中也要做相应处理

 /*** 适配android 6.0 检查权限*/
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设备还是网络
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;// 用来保存第一次按返回键的时间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();// 条件对象,即指定条件过滤获得LocationProvidercriteria.setAccuracy(Criteria.ACCURACY_FINE);// 较高精度criteria.setAltitudeRequired(true);// 是否需要高度信息criteria.setBearingRequired(true);// 是否需要方向信息criteria.setCostAllowed(true);// 是否产生费用criteria.setPowerRequirement(Criteria.POWER_LOW);// 设置低电耗mLocationProvider = mLocationManager.getBestProvider(criteria, true);// 获取条件最好的Provider,若没有权限,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米时更新一次地理位置} 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;}//调用getRotaionMatrix获得变换矩阵mMatrix[]SensorManager.getRotationMatrix(mMatrix, null, mAccelerometerValues, mMagneticFieldValues);SensorManager.getOrientation(mMatrix, mValues);//经过SensorManager.getOrientation(R, values);得到的values值为弧度//values[0]  :azimuth 方向角,但用(磁场+加速度)得到的数据范围是(-180~180),也就是说,0表示正北,90表示正东,180/-180表示正南,-90表示正西。// 而直接通过方向感应器数据范围是(0~359)360/0表示正北,90表示正东,180表示正南,270表示正西。float degree = (float) Math.toDegrees(mValues[0]);setImageAnimation(degree);mCurrentDegree = -degree;
}// 设置指南针图片的动画效果
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 检查权限*/
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

Android指南针应用相关推荐

  1. android指南针功能,轻松实现Android指南针功能

    本文实例为大家讲解如何轻松实现Android指南针功能,分享给大家供大家参考.具体如下: (1)布局文件如下: android:layout_width="fill_parent" ...

  2. android 指南针传感器,android 传感器使用 Compass指南针的实现功能

    以下是指南针通过方向传感器而旋转实现. CompassDemo.java: package com.example.activity;import android.app.Activity;impor ...

  3. android指南针校准 代码_Android指南针app的实现原理总结

    要想实现指南针功能,其实主要就是获取手机的方位,通过对比前一刻方位和现在手机方位算出手机旋转的角度,然后根据手机实际旋转的角度去旋转指南针的imageview.关键在于如何获取手机实际方位. 那么如何 ...

  4. android指南针校准 代码_android 指南针app源码(亲测可用)

    [实例简介] [实例截图] [核心代码] package cn.icast.zhinanzhen; import android.app.Activity; import android.conten ...

  5. android 指南针不稳定,Android指南针方向不可靠(低通滤波器)

    虽然我没有在Android上使用指南针,但下面显示的基本处理(在JavaScript中)可能适用于您. 它基于加速度计上的低通滤波器,它是由Windows Phone team推荐的,适用于罗盘(每3 ...

  6. Android指南针代码示例

    今天,我将分享一个有效的代码,为您的android设备制作一个非常简单的罗盘应用程序. 某些android设备(例如Huawei Y300和Lenovo P700i)不完全支持运动传感器,因此该代码不 ...

  7. Android指南针陀螺仪开发

    Android完整指南针demo,主要两个类,一个指南针旋转动画类,一个指南针界面类 先上效果图,如下 1.动画类 2.界面类 以上代码有参考网络上的文章,确实太久远,找不到出处了,还请大神们见谅. ...

  8. arcgis android 指南针,Arcgis runtime for Android 100.5 (六) 自定义指南针

    Arcgis没有提供指南针控件,如果需要,可以自定义一个,很简单 去阿里图标库下载一个能用的指南针如 指南针 放置在布局文件中 xmlns:fab="http://schemas.andro ...

  9. Android 指南针

    开发指南针思路很简单:准备一张指南针图片,该图片上方向指针指向北方.接下来开发一个检测方向的传感器,程序检测到手机顶部绕Z轴转过多少度,让指南针图片反向转过多少度即可. 程序代码: package o ...

最新文章

  1. amp 保留指定位c语言,C语言位运算符学习
  2. python词云有什么用_如何用Python实现词云效果
  3. 中考新大纲:初中数学无非就这26个考点!孩子吃透,再笨也能考115分!
  4. 什么是四路串口服务器?
  5. 微机计算机原理姚向华课后答案,微型计算机操作系统
  6. github上传文件
  7. utilities(C++)——单例(Singleton) (使用智能指针 shared_ptr)
  8. cocos bubbles_像Messenger Bubbles这样的Android浮动小部件
  9. Linux之dirname与basename命令
  10. overflow 属性
  11. 【C语言取反运算符】~2是多少?~-5是多少?
  12. 单片机和嵌入式系统的区别
  13. tensorflow学习之二 alexnet vgg resnet目标分类
  14. 概率论复习笔记【3条件概率】
  15. 见证一张CAD图如何蜕变成一个高大上的三维地形模型(一)
  16. 混合高斯模型(GMM)推导及实现
  17. python开发autocad_利用Python自动化操作AutoCAD的实现
  18. vuejs开发H5页面总结
  19. 我母亲在一家计算机公司工作,写母亲的作文(精选11篇)
  20. 是谁用Python弹奏一曲东风破

热门文章

  1. Python中Django与Echarts的结合用法
  2. 诺基亚公布准5G技术进展;Facebook要推智能电视应用 | IoT黑板报
  3. python怎么编写对称图案_python – 无论matplotlib中的箭头角度如何,都使箭头形状对称...
  4. 不务正业——游记篇 no.1 (重庆)
  5. idea设置代码提示
  6. stm32f0 大小端_STM32F0系列Cortex-M0原理与实践
  7. linux根分区写保护,目录写保护,求助大神
  8. 接了个变态需求:生成 Excel + PDF 导出,用 Java 怎么实现?
  9. 鸿蒙系统可以跟ios媲美吗,国产系统有盼头了?任正非说不需要两三年,鸿蒙就能和iOS媲美...
  10. 关于构建工具网站的摸索以及记录