封面画师:画师JW 封面ID:78143565
传感器的应用
本节案例 需要在真机上进行测试,对于真机测试方法请看到 笔记—— Android 图像处理与多媒体
本节案例 需要在真机上进行测试,对于真机测试方法请看到 笔记—— Android 图像处理与多媒体
什么是传感器
传感器 一种微型的 物理设备,能够探测、感受到外界信号,并按一定规律转换成我们需要的信息。
传感器的坐标系统:
常用的传感器: 光线传感器、磁场传感器、加速度传感器、方向传感器等。
开发传感器应用的步骤:
- 获取 SensorManager 对象:使用
getSystemService(Context.SENSOR_SERVICE) - 获取指定类型的传感器:使用 SensorManager 的
getDefaultSensor(int type)方法 - 为指定传感器注册监听器:在 Activity 的
onResume()方法中调用 SensorManager 的registerListener()方法。
在注册监听器时,需要我们实现 SensorEventListener 接口,然后我们需要重写:
onSensorChanged(SensorEvent event):传感器的 值 发生变化的时候调用
onAccuracyChanged(Sensor sensor, int accuracy):传感器的 精度 发生变化时调动
使用光线传感器获取光的强度
编写布局资源文件activity_main.xml,完成界面布局:
<LinearLayout ...
android:orientation="vertical"
android:gravity="top|center_horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="光线传感器"
android:textSize="40sp"/>
<EditText
android:id="@+id/editText"
android:layout_width="310dp"
android:layout_height="wrap_content"
android:textSize="30sp"/>
</LinearLayout>
编写MainActivity.java实现使用光线传感器:
public class MainActivity extends AppCompatActivity implements SensorEventListener {
private EditText textLIGHT;
private SensorManager sensorManager; //传感器管理器对象
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textLIGHT = findViewById(R.id.editText);
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
}
@Override
protected void onResume() {
super.onResume();
//为光线传感器注册监听器
sensorManager.registerListener(this,sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT),
SensorManager.SENSOR_DELAY_GAME);
}
@Override
protected void onPause() {
super.onPause();
//为光线传感器取消注册的监听器
sensorManager.unregisterListener(this);
}
@Override
public void onSensorChanged(SensorEvent event) {
float[] values = event.values;//获取传感器的值
int sensorType = event.sensor.getType();//获取传感器类型
StringBuilder stringBuilder = null;
//判断传感器是否是光线传感器
if (sensorType == Sensor.TYPE_LIGHT){
stringBuilder = new StringBuilder();
stringBuilder.append("光的强度值");
stringBuilder.append(values[0]); //添加获取传感器的值
//将光的强度值显示到屏幕
textLIGHT.setText(stringBuilder.toString());
}
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) { }
}
磁场传感器
我们手机的磁场传感器可以获取在 x、y、z 方向上的磁场强度。
使用磁场传感器实现指南针
案例在作者的手机上出现闪退且无法运行,因此代码仅供参考。
若找到解决方式请联系作者,一起学习交流。
需要准备一张表示指南针的图片,命名为 compass,放置于 res/drawable 下。
自定义View类PointerView.class,实现指针绘制:
public class PointerView extends View implements SensorEventListener {
private SensorManager sensorManager;//传感器管理器
private Bitmap pointer = null; //定义指针位图对象
private float[] allValues; //磁场传感器的值
public PointerView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
//获取传感器管理器
sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
//为磁场传感器注册监听器
sensorManager.registerListener(this,
sensorManager
.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD),
sensorManager.SENSOR_DELAY_GAME);
//指定要绘制的位图
pointer = BitmapFactory.decodeResource(super.getResources(), R.drawable.compass);
}
//传感器的值发生改变时触发
@Override
public void onSensorChanged(SensorEvent event) {
//判断传感器是否是磁场传感器
if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) {
float[] value = event.values; //获取磁场传感器的值
allValues = value;
super.postInvalidate();//刷新界面
}
}
//传感器的进度发生改变时触发
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//根据X、Y轴磁场强度绘制指针
if (allValues != null) {
float x = allValues[0];//X轴的磁场强度
float y = allValues[1];//Y轴的磁场强度
canvas.restore();//重置绘图对象
//设置旋转中心点为屏幕中心
canvas.translate(super.getWidth() / 2, super.getHeight() / 2);
if (y == 0 && x > 0) {
canvas.rotate(90);//旋转90度
} else if (y == 0 && x < 0) {
canvas.rotate(270);//旋转270度
} else {
if (y >= 0) {
canvas.rotate((float) Math.tanh(x / y) * 90);
} else {
canvas.rotate(180 + (float) Math.tanh(x / y) * 90);
}
}
//绘制指针
canvas.drawBitmap(this.pointer, -this.pointer.getWidth() / 2,
-this.pointer.getHeight() / 2, new Paint());
}
}
}
编写activity_main.xml布局资源文件,自定义界面:
<FrameLayout ...>
<com.yang.compass.PointerView
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</FrameLayout>
MainActivity.java 使用默认即可。
加速度传感器
加速度传感器主要用于测算一些瞬时 加速 或 减速 的动作。比如:摇一摇、计步等。
手机屏幕向上,当手机横向移动时,会产生 X 方向的加速度;前后移动,会产生 Y 方向的加速度;垂直上下移动会产生 Z 方向的加速度。
实现摇一摇功能
编写activity_main.xml实现程序主界面:
<LinearLayout ...
android:gravity="center"
tools:context=".MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="摇一摇试试?"
android:textSize="40sp"/>
</LinearLayout>
编写info.xml界面,实现摇一摇成功后显示的对话框:
<RelativeLayout ...>
<TextView
android:layout_width="500dp"
android:layout_height="500dp"
android:gravity="center"
android:layout_centerInParent="true"
android:text="你上当了!"
android:textSize="40sp" />
</RelativeLayout>
编写MainActivity.java实现摇一摇主逻辑:
public class MainActivity extends AppCompatActivity implements SensorEventListener {
private SensorManager sensorManager;//定义传感器管理器
private Vibrator vibrator; //定义振动器
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//获取传感器管理器
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
//获取振动器
vibrator = (Vibrator) getSystemService(Service.VIBRATOR_SERVICE);
}
@Override
protected void onResume() {
super.onResume();
//为加速度传感器注册监听器
sensorManager.registerListener(
this,sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),
SensorManager.SENSOR_DELAY_GAME);
}
@Override
public void onSensorChanged(SensorEvent event) {
int sensorType = event.sensor.getType();
if (sensorType == Sensor.TYPE_ACCELEROMETER){
float[] values = event.values; //获取传感器的值
if (values[0] > 15 || values[1] > 15 || values[2] > 20){
Toast.makeText(MainActivity.this, "摇一摇", Toast.LENGTH_SHORT).show();
//创建一个对话框构建器
AlertDialog.Builder alertDialog = new AlertDialog.Builder(this);
//设置布局文件
alertDialog.setView(R.layout.info);
alertDialog.show();//显示对话框
vibrator.vibrate(500);//设置振动器频率
sensorManager.unregisterListener(this);
}
}
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
}
测试: 本案例只能实现摇动一次弹出对话框,无法实现多次摇动弹出对话框。
方向传感器
方向传感器主要用于感应手机的摆放状态。
返回值
它可以返回三个角度:
- 在 Z 轴上旋转的角度:手机方向 Y 轴与地磁场北极方向的角度,当手机绕 Z 轴旋转时这个值发生变化。
- 在 X 轴上旋转的角度:手机底部或尾部翘起的角度,当手机绕 X 轴时这个值发生变化。
- 在 Y 轴上旋转的角度:表示手机左侧或右侧翘起的角度,当手机绕 Y 轴时这个值发生变化。
使用方向传感器
获取方向传感器:getDefaultSensor(Sensor.TYPE_ORIENTATION)(新 SDK 中方法已过期)
我们可以使用加速度传感器与磁场传感器结合实现这个功能。如下图:
注意: 我们使用 SensorManager.getOrientation() 得到的 返回值是弧度,而 不是角度,因此我们需要使用 Math.toDegrees()将弧度转换换成角度。
实现水平仪
步骤
- 创建自定义 View,并实现 SenorEventListener 接口
- 布局界面
- 为磁场传感器和加速度传感器注册监听器,并获取传感器的取值
- 获取 z、x、y 轴的旋转角度,并绘制小球。
素材获取:Android利用Sensor(传感器)实现水平仪功能
代码实现
我们需要准备水平仪的仪表盘图片和气泡图标,分别命名为 background 和 bubble,将它们置于 res/drawable 目录下,素材可以使用上图连接进行获取。
创建自定义View——SpiritlevelView.class:
public class SpiritlevelView extends View implements SensorEventListener {
private Bitmap bubble;//绘制气泡
private Bitmap back; //绘制水平仪表盘
float[] accelerometerValues = new float[3];//加速度传感器的值
float[] magneticValues = new float[3];//磁场传感器的值
private final int MAX_ANGLE = 30; //最大倾斜角度
private int bubbleX, bubbleY; //气泡的位置坐标
public SpiritlevelView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
//获取传感器管理器
SensorManager sensorManager =
(SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
//为磁场传感器注册监听器
sensorManager.registerListener(this,
sensorManager
.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD),
SensorManager.SENSOR_DELAY_GAME);
//为加速度传感器注册监听器
sensorManager.registerListener(this,
sensorManager
.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),
SensorManager.SENSOR_DELAY_GAME);
//加载气泡图片
bubble = BitmapFactory.decodeResource(getResources(), R.drawable.bubble);
}
@Override
public void onSensorChanged(SensorEvent event) {//传感器的值改变触发
if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
accelerometerValues = event.values.clone(); //获取加速度传感器的值
} else if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) {
magneticValues = event.values.clone();//获取磁场传感器的值
}
float[] R = new float[9]; //保存旋转数据的数组
float[] values = new float[3]; //保存方向数据的数组
//获得一个包含旋转矩阵的R数组
SensorManager.getRotationMatrix(R, null, accelerometerValues, magneticValues);
SensorManager.getOrientation(R, values);//获取方向值
float xAngle = (float) Math.toDegrees(values[1]); //X轴旋转角度
float yAngle = (float) Math.toDegrees(values[2]); //Y轴旋转角度
//获取气泡的位置坐标
getPosition(xAngle, yAngle);
super.postInvalidate();//刷新界面
}
//传感器的精度改变触发
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
//根据x轴和y轴的旋转角度确定气泡的位置
private void getPosition(float xAngle, float yAngle) {
//气泡位于中间时(水平仪完全水平),气泡的X、Y坐标
int x = (super.getWidth() - bubble.getWidth()) / 2;
int y = (super.getHeight() - bubble.getHeight()) / 2;
/************控制小球的X轴位置**************/
if (Math.abs(yAngle) <= MAX_ANGLE) {//如果Y轴的倾斜角度还在最大角度以内
//根据Y轴的倾斜角度计算X坐标的变化值(倾斜角度越大,X坐标变化越大)
int deltaX =
(int) ((super.getWidth() - bubble.getWidth()) / 2 * yAngle / MAX_ANGLE);
x -= deltaX;
} else if (yAngle > MAX_ANGLE) {//如果Y轴的倾斜角度已经大于MAX_ANGLE,气泡在最左边
x = 0;
} else {//如果与Y轴的倾斜角度已经小于负的MAX_ANGLE,气泡在最右边
x = super.getWidth() - bubble.getWidth();
}
/************控制小球的Y轴位置**************/
if (Math.abs(xAngle) <= MAX_ANGLE) {//如果X轴的倾斜角度还在最大角度之内
//根据X轴的倾斜角度计算Y坐标的变化值(倾斜角度越大,Y坐标变化越大)
int deltaY =
(int) ((super.getHeight() - bubble.getHeight()) / 2 * xAngle / MAX_ANGLE);
y += deltaY;
} else if (xAngle > MAX_ANGLE) {//如果X轴的倾斜角度已经大于MAX_ANGLE,气泡在最下边
y = super.getHeight() - bubble.getHeight();
} else {//如果与X轴的倾斜角度已经小于负的MAX_ANGLE,气泡在最上边
y = 0;
}
//更新小球的位置
bubbleX = x;
bubbleY = y;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//绘制气泡
canvas.drawBitmap(bubble,bubbleX,bubbleY,new Paint());
}
}
编写activity_main.xml布局界面:
<FrameLayout ...>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:src="@drawable/background"/>
<com.yang.gradienter.SpiritlevelView
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</FrameLayout>
MainActivity.java 使用默认即可。
位置服务与地图应用
获取 LocationProvider
LocationProvider 是位置源的意思,用于提供 定位 信息。
常见的 LocationProvider:GPS、network、passive
获取 LocationProvider 方法
- 获取所有可用的 LocationProvider:LocationManager 对象的
getAllProviders()方法 - 通过名称获得 LocationProvider:LocationManager 对象的
getProvider()方法 - 通过 Criteria 类获得 LocationProvider
我们根据代码来进行方法的测试:
编写布局资源文件activity_main.xml对页面进行布局:
<RelativeLayout ...>
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="可用LocationProvider"
android:textColor="#257678"
android:textSize="26sp"
android:textStyle="bold" />
<TextView
android:id="@+id/provider"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#0B80DD"
android:lineSpacingExtra="18sp"
android:layout_centerInParent="true"
android:textStyle="bold"
android:textSize="18sp"/>
</RelativeLayout>
编写MainActivity.java实现获取LocationProvider的三种方式:
public class MainActivity extends AppCompatActivity {
private TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = findViewById(R.id.provider);
LocationManager locationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
/********1. 获取所有的LocationManager**********************/
// List<String> providerName = locationManager.getAllProviders();
// StringBuilder stringBuilder = new StringBuilder();//字符串构建器
// for (Iterator<String> iterator = providerName.iterator();iterator.hasNext();){
// stringBuilder.append(iterator.next()+"\n");
// }
// textView.setText(stringBuilder.toString());//显示获取的LocationProvider名称
/************2. 获取基于GPS的LocationProvider***************/
// LocationProvider provider = locationManager.getProvider(LocationManager.GPS_PROVIDER);
// textView.setText(provider.getName());
/**************3. 获取最佳的LocationProvider*****************/
Criteria criteria = new Criteria();//创建一个过滤条件对象
criteria.setCostAllowed(false);//不收费的
criteria.setAccuracy(Criteria.ACCURACY_FINE); //使用精度最准确的
criteria.setPowerRequirement(Criteria.POWER_LOW); //使用耗电量最低的
String provider = locationManager.getBestProvider(criteria,true);
textView.setText(provider);
}
}
在本次案例中我们获取 GPS 信息,因此我们需要 获取 GPS 的权限。
对 AndroidManifest.xml 进行修改:
<manifest ...>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<application ...
android:requestLegacyExternalStorage="true">
<activity android:name=".MainActivity">
...
</activity>
</application>
</manifest>
同时我们还需要在模拟器内部授予本案例的应用读取位置信息(定位)的权限!
获取定位信息
获取定位信息的步骤
- 获取系统的 LocationManager 对象:
getSystemService(LOCATION_SERVICE) - 设置一个监听器:LocationManager 对象的
requestLocationUpdate()方法 - 获取最近的定位信息:LocationManager 的
getLastKnownLocation()方法
编写布局资源文件activity_main.xml对页面进行布局:
<RelativeLayout ...>
<TextView
android:id="@+id/location"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:textSize="18sp"
android:textStyle="bold"
android:textColor="#06518C"/>
</RelativeLayout>
编写MainActivity.java实现获取定位信息:
public class MainActivity extends AppCompatActivity {
private TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = findViewById(R.id.location);
/****第一步****/
LocationManager locationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
//权限检查
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;
}
/****第二步****/
locationManager.requestLocationUpdates(
LocationManager.GPS_PROVIDER, //指定GPS定位提供者
1000, //间隔时间
1, //位置间隔1米
new LocationListener() {//监听GPS定位信息是否改变
//GPS定位信息发生改变时回调
@Override
public void onLocationChanged(Location location) {
}
//GPS定位状态发生改变时回调
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
//定位提供者启动时触发
@Override
public void onProviderEnabled(String provider) {
}
//定位提供者关闭时触发
@Override
public void onProviderDisabled(String provider) {
}
}
);
/****第四步****/
//获取最新的定位信息
Location location = locationManager.
getLastKnownLocation(LocationManager.GPS_PROVIDER);
//将最新的定位信息传递给locationUpdates()方法
locationUpdates(location);
}
/****第三步****/
public void locationUpdates(Location location){
if (location != null){
//字符串构建器用于记录定位信息
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("您的位置是:\n");
stringBuilder.append("经度:");
stringBuilder.append(location.getLongitude());
stringBuilder.append("\n纬度:");
stringBuilder.append(location.getLatitude());
//显示到页面上
textView.setText(stringBuilder.toString());
}else {
textView.setText("没有获取到GPS信息");
}
}
}
在本次案例中我们获取 GPS 信息,因此我们需要 获取 GPS 的权限。
对 AndroidManifest.xml 进行修改:
<manifest ...>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<application ...
android:requestLegacyExternalStorage="true">
<activity android:name=".MainActivity">
...
</activity>
</application>
</manifest>
同时我们还需要在模拟器内部授予本案例的应用读取位置信息(定位)的权限!
应用百度地图 API
百度地图 API:一套为开发者 免费 使用的基于百度地图的应用程序接口。它提供了定位服务、地图服务、数据服务、出行服务、鹰眼轨迹服务、分析服务等服务。
准备工作
- 首先需要申请秘钥(API Key):百度地图开放平台
新版网址在网页最下方,点击「立即注册」进入申请注册。
注册成功后创建应用,应用类型选择 Android SDK,启动服务选择默认。
然后 获取发布版SHA1,你需要找到你电脑 SDK 或 AVD 目录下有个名为.android 的目录,复制当前文件的位置! 比如:D:/avd/.android 就复制这个目录。
如何确定.android 目录:在这个目录下有个名为 debug.keystore 的文件。
然后我们进入 AS,进入 Terminal 面板,切换到刚刚复制的目录中,然后输入:
keytool -list -v -keystore debug.keystore -alias androiddebugkey
点击回车后,会让你输入秘钥库口令,一般默认都是 android。输入的形式是不回显的。
密码输入成功后,我们可以找到 证书指纹,里面就有 SHA1,我们复制填写即可。
然后我们还需要 填写PackageName,这个值你可以随意填写,但是!你编写的应用的 Java 代码得放在你填写的这个包目录下。
SHA1 和包名会自动构成安全码,我们填写好信息提交就可以了。提交成功后会创建一个应用,应用里面有 访问应用(AK),这个值就是开发秘钥,它在我们编写代码时有用。
到此,我们就 成功申请了秘钥 了。
- 下载百度LBS Android SDK:百度地图开放平台
进入网址后,开发文档 > Android 地图 SDK > 产品下载 > 自定义下载,选择你需要 jar 包开发资源。
我在此选择 全量定位、基础地图、检索功能、LBS 云检索、计算工具、驾车导航(含 TTS)、全景图功能,选择后,点击下方 「开发包」 进行下载。
到此,我们的准备工作就完成了,接下来就该应用百度地图 API 显示百度地图了。
应用百度地图 API 显示地图
步骤
- 配置百度 Android 地图 API
- 布局界面
- 初始化 SDK 应用的 Context 全局变量
- 管理地图生命周期
具体实现
本文未概述清楚的地方请参考:
Android使用百度地图API 利用AS 3.0对于百度地图API的开发
运行应用进行测试时,使用真机测试,真机需要联网!
配置百度地图API:
在 AS 中将项目切换成 project 模式。首先找到我们刚下载的 百度 LBS Android SDK 然后进行解压,解压成功后如图:
我们只使用圈起来的 libs 目录。打开这个目录,将这个目录下的 jar 包复制到我们项目中 libs 目录(AS 中)下,然后选中这些 jar 包,右击,Add As Library,把这些 jar 包添加到类库中。
在我们项目 src/main/ 目录下(AS 中)新建 jniLibs 目录,把 libs 目录下的五个文件夹复制进去,即:
刚刚复制的 jar 包就是上图中的那两个。
代码编写:你编写的应用的 Java 代码必须放在你申请秘钥时填写的包名目录下。 比如,我申请秘钥时填写的包名是 com.yang,因此我将我的 Java 代码必须放在这个包下。

编写activity_main.xml布局界面:
<RelativeLayout ...>
<com.baidu.mapapi.map.MapView
android:id="@+id/bmapview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="true"/>
</RelativeLayout>
在AndroidManifest.xml中指定开发秘钥、设置权限:
<manifest ...>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<!--获取设备网络状态,禁用后无法获取网络状态-->
<uses-permission android:name="android.permission.INTERNET" />
<!--网络权限,当禁用后,无法进行检索等相关业务-->
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<!--读取设备硬件信息,统计数据-->
<uses-permission android:name="com.android.launcher.permission.READ_SETTINGS" />
<!--读取系统信息,包含系统版本等信息,用作统计-->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<!--获取设备的网络状态,鉴权所需网络代理-->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<!--允许sd卡写权限,需写入地图数据,禁用后无法显示地图-->
<uses-permission android:name="android.permission.WRITE_SETTINGS"
tools:ignore="ProtectedPermissions" />
<!--获取统计数据-->
<uses-permission android:name="android.permission.CAMERA" />
<!--使用步行AR导航,配置Camera权限-->
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<!-- 这个权限用于进行网络定位 -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<!-- 这个权限用于访问GPS定位 -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<!-- 用于访问wifi网络信息,wifi信息会用于进行网络定位 -->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<!-- 获取运营商信息,用于支持提供运营商信息相关的接口 -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<!-- 这个权限用于获取wifi的获取权限,wifi信息会用来进行网络定位 -->
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<!-- 用于读取手机当前的状态 -->
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<!-- 写入扩展存储,向扩展卡写入数据,用于写入离线定位数据 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<!-- 访问网络,网络定位需要上网 -->
<uses-permission android:name="android.permission.INTERNET" />
<!-- SD卡读取权限,用户写入离线定位数据 -->
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"
tools:ignore="ProtectedPermissions" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<application
...
android:requestLegacyExternalStorage="true">
<!--开发秘钥-->
<meta-data
android:name="com.baidu.lbsapi.API_KEY"
android:value="WVbGmRg0YKTz7FEjiVy3H3lgBepGp0rZ"></meta-data>
<!--value就是你申请的AK-->
<!--用别的包名申请的AK在本app中是不能使用的,一个AK对应一个应用-->
<activity android:name=".MainActivity">
...
</activity>
</application>
</manifest>
开发秘钥在 <application> 标签内指定,使用 <meta-data> 进行指定。其中,name 为固定值,即:com.baidu.lbsapi.API_KEY;value 为你申请的秘钥。
编写MainActivity.java初始化地图应用、管理地图生命周期:
public class MainActivity extends AppCompatActivity {
private MapView mMapView;//声明地图组件
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
/*********注意此处的代码顺序,有严格要求***********/
//初始化SDK应用的Context全局变量
SDKInitializer.initialize(getApplicationContext());
setContentView(R.layout.activity_main);
mMapView = findViewById(R.id.bmapview);
}
@Override
protected void onResume() {
super.onResume();
mMapView.onResume();
}
@Override
protected void onPause() {
super.onPause();
mMapView.onPause();
}
@Override
protected void onDestroy() {
super.onDestroy();
mMapView.onDestroy();
mMapView = null;
}
}
到此,我们并没有全部完成。如果我们就这样去真机上测试,大概率无法进入界面,会直接闪退。
我们需要在创建一个继承至Application的java类,我在此创建MyApplication.java:
public class MyApplication extends Application {
@Override
public void onCreate() {
SDKInitializer.initialize(this);
super.onCreate();
}
}
这下,我们是真的完成了代码的编写。
我们可以进入真机测试阶段,但是一定记得要将手机联网,否则无法运行!
定位到我的位置
定位到我的位置的步骤
实现在百度地图 API 上定位我的位置
准备:准备一张定位图标图片资源(也可以不准备,使用默认),将其命名为 loc,置于 res/drawable 目录下。
本案例在 上述 应用百度地图 API 显示地图 的 案例 中 进行修改!
布局资源文件 activity_main.xml 不变,自定义类 MyApplication.java 不变。
在AndroidManifest.xml中添加两条权限,用于获取GPS定位服务:
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
修改MainActivity.java,实现定位服务:
public class MainActivity extends AppCompatActivity {
private MapView mMapView;//声明地图组件
private BaiduMap mBaiduMap;//定义百度地图对象
private boolean isFirstLoc = true;//记录是否是第一次定位
private MyLocationConfiguration.LocationMode locationMode; //当前定位模式
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
/*********注意此处的代码顺序,有严格要求***********/
//初始化SDK应用的Context全局变量
SDKInitializer.initialize(getApplicationContext());
setContentView(R.layout.activity_main);
mMapView = findViewById(R.id.bmapview);
mBaiduMap = mMapView.getMap();//获取百度地图对象
//获取系统的LocationManager对象
LocationManager locationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
//添加需要的权限检查
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信息
locationManager.requestLocationUpdates(
locationManager.GPS_PROVIDER, //GPS定位提供者
1000, //更新数据时间为1秒
1, //位置间隔为1米
//位置监听器
new LocationListener() {//GPS定位信息发生改变时触发,用于更新位置信息
@Override
public void onLocationChanged(Location location) {
locationUpdates(location);//GPS信息发生改变时,更新位置
}
//
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
@Override
public void onProviderEnabled(String provider) {
}
@Override
public void onProviderDisabled(String provider) {
}
}
);
//从GPS中获取最新的定位信息
Location location = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
locationUpdates(location); //将最新的定位信息传递给创建的locationUpdates()方法中
}
@Override
protected void onResume() {
super.onResume();
mMapView.onResume();
}
@Override
protected void onPause() {
super.onPause();
mMapView.onPause();
}
@Override
protected void onDestroy() {
super.onDestroy();
mMapView.onDestroy();
mMapView = null;
}
@Override
protected void onStart() {
super.onStart();
//启动定位图层
mBaiduMap.setMyLocationEnabled(true);
}
@Override
protected void onStop() {
super.onStop();
//停止定位图层
mBaiduMap.setMyLocationEnabled(false);
}
//获取指定的查询信息
public void locationUpdates(Location location) {
if (location != null) { //如果location不为空
//获取当前用户的经纬度
LatLng ll = new LatLng(location.getLatitude(), location.getLongitude());
Log.i("Location", "纬度:" + location.getLatitude()
+ "|经度:" + location.getLongitude());
if (isFirstLoc) {
//更新坐标位置
MapStatusUpdate u = MapStatusUpdateFactory.newLatLng(ll);
mBaiduMap.animateMapStatus(u); //设置地图位置
isFirstLoc = false;//取消第一次定位
}
//构造定位数据
MyLocationData locationData =
new MyLocationData.Builder().accuracy(location.getAccuracy())
.direction(100) //设置方向信息
.latitude(location.getLatitude()) //设置纬度坐标
.longitude(location.getLongitude()) //设置经度坐标
.build();
//设置定位数据
mBaiduMap.setMyLocationData(locationData);
//设置自定义图标
BitmapDescriptor bitmapDescriptor =
BitmapDescriptorFactory.fromResource(R.drawable.loc);
//设置定位模式 自定义定位模式 不设置选择默认
locationMode = MyLocationConfiguration.LocationMode.NORMAL;
//设置构造方式 自定义图标 不设置则选择默认
MyLocationConfiguration config =
new MyLocationConfiguration(locationMode, true, bitmapDescriptor);
//显示定位图标
mBaiduMap.setMyLocationConfiguration(config);
} else {
Log.i("Location", "没有获取到GPS信息");
}
}
}
测试要点:使用真机测试,打开位置服务及权限,真机需要联网,尽量在空旷区域、而不是室内进行测试。
Android 传感器及 LBS 完