Android 天气APP(二十四)地图天气(上)自动定位和地图点击定位
文章目录
- 前言
- 正文
- 一、修改应用配置
- 二、地图显示
- 三、定位到当前所在位置
- 四、点击地图定位
- 五、回到当前位置并清除标点
- 六、根据经纬度获取实际的定位信息
前言
之前也看过和风天气自己做的APP,主页面的地图点击之后,定位到某一个点,然后查看这个地方的天气,实际思路还是通过区县来查询天气的,只不过,加上了地图就比较的直观,看起来也会觉得很上档次,所以我也决定做一个这样的功能。 这篇文章实现的效果图如下:
正文
说到地图就想到了百度地图,当然你要是想用高德地图也是可以的,对接就可以了,我这里就不介绍了,还记得在第一篇文章的时候,就注册了百度地图开放平台的账号,并且申请了应用的key用于定位,点击百度地图开放平台进入。登录账号之后进入控制台,找到之前的那个应用,我的当然是GoodWeather,你们的要看自己的名字,
一、修改应用配置
还记得吗?我之前只选择了定位这个功能,点击设置进入配置页面
这是之前配置的。
如下图所示勾选,然后提交
提交之后就不用管了,点击开发文档选择Android地图SDK
然后点击自定义下载
按照下图进行选择,然后选择标准开发包,下载
点击开发包然后开始下载,下载好之后解压文件。目录如下
选中这7个文件,粘贴到你的项目中libs下面,这时候你的项目会报错,因为缺少依赖的jar包,所以要Sync,等待完成之后你的jar可以打开的时候,你的项目就不会报错了。
然后Build → Clean Project 清除掉一些之前的配置
现在运行一下,确保不影响我们之前的代码。
OK,很明显没问题,那么就开始操作吧。
二、地图显示
首先打开你的Application,在onCreate中放入地图SDK初始化的代码
//在使用SDK各组件之前初始化context信息,传入ApplicationContext
SDKInitializer.initialize(this);
//自4.3.0起,百度地图SDK所有接口均支持百度坐标和国测局坐标,用此方法设置您使用的坐标类型.
//包括BD09LL和GCJ02两种坐标,默认是BD09LL坐标。
SDKInitializer.setCoordType(CoordType.BD09LL);
然后就可以来显示地图了,以我的性格,那肯定是新建一个页面来做这个的。在ui包下新建一个MapWeatherActivity,然后修改布局activity_map_weather.xml 代码如下:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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=".ui.MapWeatherActivity">
<!--地图控件-->
<com.baidu.mapapi.map.MapView
android:clickable="true"
android:id="@+id/map_view"
android:fitsSystemWindows="true"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</FrameLayout>
地图有了,然后就是做沉浸式效果,打开MapWeatherActivity,修改代码如下:
package com.llw.goodweather.ui;
import android.os.Bundle;
import com.baidu.mapapi.map.MapView;
import com.llw.goodweather.R;
import com.llw.goodweather.utils.StatusBarUtil;
import com.llw.mvplibrary.base.BaseActivity;
import butterknife.BindView;
import butterknife.ButterKnife;
/**
* 地图天气
*/
public class MapWeatherActivity extends BaseActivity {
@BindView(R.id.map_view)
MapView mapView;//地图控件
@Override
public void initData(Bundle savedInstanceState) {
StatusBarUtil.transparencyBar(context);//透明状态栏
StatusBarUtil.StatusBarLightMode(context);//状态栏黑色字体
}
@Override
public int getLayoutId() {
return R.layout.activity_map_weather;
}
}
可以看到这里继承了BaseActiivty,实现里面的两个构造方法,然后通过注解的方式绑定地图控件,在页面初始化initData()的时候修改状态栏的背景和字体颜色。在getLayoutId()中指定布局。 那么这里就先告一段落了,因为我是新建的页面,所以我需要到主页面MainActivity中找一个地方去进入这个地图页面MapWeatherActivity。就选择左上角了。 修改activity_main.xml中的布局
icon_map.png,尺寸是48*48,白色的所以你看不见,不过依然可以保存
推荐一个图标网站,我APP上的图标基本都是在这个上面找的,真心不错。 iconfont
这个地图的图标也是在上面找的。可以看到右边已经有一个更多功能的按钮了,我在左边加一个地图的。图标作为按钮,右边的图标我也做了一些修改,看上面的图进行改动,触摸时会有一个半透明的背景,这个drawable代码如下:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true">
<shape>
<solid android:color="#22000000"/>
<corners android:radius="@dimen/dp_25"/>
</shape>
</item>
<item>
<shape>
<solid android:color="#00000000"/>
</shape>
</item>
</selector>
然后进入到MainActivity中
点击跳转
OK,运行看一下效果吧。说实话这个GIF大小上限5M真的很坑爹,最坑爹是我的GIF命名4.83M,结果还是告诉我超过限制,没有办法只能降低GIF的清晰度了。
可以看到不是很清晰,但是我也没办法呀。地图确实已经看到了,但是为什么是在北京呢?因为这是默认的初始位置,所以需要自己去定位到当前位置。
三、定位到当前所在位置
首先在MapWeatherActivity中增加一个定位的监听方法,实现BDLocationListener,代码如下:
private LocationClient mLocationClient;//定位
private BaiduMap mBaiduMap;//百度地图
/**
* 定位SDK监听,
*/
private class MyLocationListener implements BDLocationListener {
/**
* 监听返回数据 MapView 销毁后不在处理新接收的位置
* @param bdLocation 定位信息
*/
@Override
public void onReceiveLocation(BDLocation bdLocation) {
if (bdLocation == null || mapView == null) {//做null处理,避免APP崩溃
return;
}
MyLocationData locationData = new MyLocationData.Builder()//定位构造器
.accuracy(bdLocation.getRadius())//设置定位数据的精度信息,单位:米
.direction(bdLocation.getDirection())//设置定位数据的方向信息
.latitude(bdLocation.getLatitude())//设置定位数据的纬度
.longitude(bdLocation.getLongitude())//设置定位数据的经度
.build();//构建生成定位数据对象
mBaiduMap.setMyLocationData(locationData);//设置定位数据,只有开启定位图层之后才会生效
//创建一个经纬度构造对象,传入定位返回的经纬度,Latitude是纬度,Longitude是经度,一对经纬度值代表地球上一个地点。
LatLng latLng = new LatLng(bdLocation.getLatitude(),bdLocation.getLongitude());
MapStatus.Builder builder = new MapStatus.Builder()//创建地图状态构造器
.target(latLng)//设置地图中心点,传入经纬度对象
.zoom(13.0f);//设置地图缩放级别 13 表示 比例尺/2000米 2公里
//改变地图状态,使用地图状态更新工厂中的新地图状态方法,传入状态构造器
mBaiduMap.animateMapStatus(MapStatusUpdateFactory.newMapStatus(builder.build()));
}
}
注释都是代码里,应该够清楚了吧,官方接入文档都没有我这个清楚。 然后再写一个初始化定位的方法。
/**
* 初始化定位
*/
private void initLocation() {
mBaiduMap.setMyLocationEnabled(true);//开启定位图层
mLocationClient = new LocationClient(context);//定位初始化
MyLocationListener listener = new MyLocationListener();//创建定位监听器
mLocationClient.registerLocationListener(listener);//注册定位监听,否则监听无效
LocationClientOption option = new LocationClientOption();//创建定位设置
option.setOpenGps(true);//打开GPS
option.setCoorType("bd09ll");//设置坐标类型 可以设置BD09LL和GCJ02两种坐标
option.setScanSpan(0);//设置扫描间隔,单位是毫秒,0 则表示只定位一次,设置毫秒不能低于1000,也就是1秒
mLocationClient.setLocOption(option);//传入定位设置
mLocationClient.start();//开始定位
}
在这个方法中,对定位做了监听和设置,然后启动定位。 最后只要在initData方法中调用就可以了。
最后就是关于Activity的生命周期要对地图做相应的处理。代码如下:
@Override
protected void onResume() {
super.onResume();
mapView.onResume();//生命周期管理
}
@Override
protected void onPause() {
super.onPause();
mapView.onPause();//生命周期管理
}
@Override
protected void onDestroy() {
super.onDestroy();
mLocationClient.stop();// 退出时销毁定位
mBaiduMap.setMyLocationEnabled(false);// 关闭定位图层
mapView.onDestroy();// 在activity执行onDestroy时必须调用mMapView.onDestroy()
}
然后就可以运行了,运行看看吧。
可以看到定位还是蛮准的,定位时间取决于你的网速。当然如果你想要地址显示的更精确一些的话,可以修改
可以参照这个表来进行设置
显示层级 |
比例尺/米 |
比例尺/公里 |
---|---|---|
4 |
1000000 |
1000 |
5 |
500000 |
500 |
6 |
200000 |
200 |
7 |
100000 |
100 |
8 |
50000 |
50 |
9 |
25000 |
25 |
10 |
20000 |
20 |
11 |
10000 |
10 |
12 |
5000 |
5 |
13 |
2000 |
2 |
14 |
1000 |
1 |
15 |
500 |
0.5 |
16 |
200 |
0.2 |
17 |
100 |
0.1 |
18 |
50 |
0.05 |
19 |
20 |
0.02 |
20 |
10 |
0.01 |
21 |
5 |
0.005 |
比如说我设置成4层级的
zoom(4.0f)
运行一下:
根据自己的需求来就行了。所以我设置的是13,大概就能看清楚附近的区/县就可以了,因为和风天气API请求的最低单位就是区/县。
四、点击地图定位
当然光是一个当前定位是不足以满足用户的,用户一般是都是想点那里就点那里,你点了没反应就是你的软件有问题,然后直接给你卸载,你哭都没地方哭去。下面来看看具体实现吧。
先声明需要的变量
private Marker marker;//标点也可以说是覆盖物
private BitmapDescriptor bitmap;//标点的图标
private double markerLatitude = 0;//标点纬度
private double markerLongitude = 0;//标点经度
private double latitude;//定位纬度
private double longitude;//定位经度
刚才都说了是点击地图定位,那么肯定就需要一个点击事件对吧。当然这个事件肯定不是常规的View.OnClickListener,而是百度地图已经封装好的点击方法。
/**
* 初始化地图点击
*/
private void initMapOnClick() {
mBaiduMap.setOnMapClickListener(new BaiduMap.OnMapClickListener() {
//地图内 Poi 单击事件回调函数 那么poi是什么呢?你可以当做兴趣点,
// 比如我想知道我当前所在地有那些餐厅,那么餐厅就是poi,
// 而你点击这个poi就会拿到详情的信息数据,当然不在我的业务需求之内,所以只做解释
@Override
public void onMapPoiClick(MapPoi mapPoi) {
}
//地图单击事件回调函数
@Override
public void onMapClick(LatLng latLng) {
}
});
}
这里添加一个标点marker的图标
可以看到要实现两个构造方法,而我只需要在onMapClick下处理点击之后的业务逻辑就可以了。
//地图单击事件回调函数
@Override
public void onMapClick(LatLng latLng) {
bitmap = BitmapDescriptorFactory.fromResource(R.mipmap.icon_marka);// 设置marker图标
//通过LatLng获取经纬度
markerLatitude = latLng.latitude;//获取纬度
markerLongitude = latLng.longitude;//获取经度
mBaiduMap.clear();//清除之前的图层
MarkerOptions options = new MarkerOptions()//创建标点marker设置对象
.position(latLng)//设置标点的定位
.icon(bitmap);//设置标点图标
marker = (Marker) mBaiduMap.addOverlay(options);//在地图上显示标点
//点击地图之后重新定位
initLocation();
}
当点击地图时,或者经纬度,然后清除当前的图层,再配置标点的坐标和图标,然后添加到地图上,这时候重新定位一下,定位到标点的这个地方,所以要在监听的回调里面中修改原来的代码
在拿到定位监听到之后首先判断是自动定位,还是点击地图定位。因为如果你点了地图,那么markerLatitude就不会是为0的,因为我在点击地图的时候给markerLatitude赋了值,作为判断条件区分你是自动还是手动。然后赋值给一个全局的经纬度变量,再把这个变量放到定位数据里,再设置成地图中心坐标,最后渲染出来,就可以做到,我点那里就定位到哪里了,指哪打哪。OK,没有效果图那就是扯淡,运行一下:
效果还是不错滴!但是我又想回到原来的位置呢? Wath!!!!
五、回到当前位置并清除标点
先来说一下实现的业务逻辑,当我一进入这个页面时,是自动定位的,这是要隐藏自动定位按钮,当我点击定位按钮时,清除标点回到当前定位地址。这个按钮我打算用浮动按钮来做,因为它隐藏和显示的时候会自带动画效果,相当不错。 在build.gradle中添加,有就不用了,然后Sync,否则你找不到这个控件。
implementation 'com.google.android.material:material:1.0.0'
简单修改activity_map_weather.xml
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/btn_auto_location"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:layout_marginLeft="@dimen/dp_50"
android:layout_marginBottom="@dimen/dp_10"
android:clickable="true"
android:src="@mipmap/icon_auto_location"
app:backgroundTint="@color/transparent_bg_3"
app:backgroundTintMode="screen"
app:borderWidth="@dimen/dp_0"
app:fabSize="mini"
app:hoveredFocusedTranslationZ="@dimen/dp_18"
app:pressedTranslationZ="@dimen/dp_18"
app:rippleColor="@color/blue_one" />
这个按钮会显示在页面的左下角比例尺的左边。 icon_auto_location.png如下,其他的参数都是常规数据,背景颜色尺寸一些都是可以自己写的。
然后回到MapWeatherActivity
@BindView(R.id.btn_auto_location)
FloatingActionButton btnAutoLocation;//重新定位按钮
然后在点击的时候重置标点的经纬度参数值,清除标点,再重新定位
/**
* 点击事件
*/
@OnClick(R.id.btn_auto_location)
public void onViewClicked() {
markerLatitude = 0;
markerLongitude = 0;
marker.remove();//清除标点
initLocation();
}
在定位成功的回调中对定位按钮进行显示和隐藏的控制。
然后在新建一个initView方法,放入进入页面的一些基本配置。
OK,运行一下。
六、根据经纬度获取实际的定位信息
我们已经拿到经纬度信息了,那么这个信息需要转换为实际的位置,否则别人也不知道你定位在哪里,那就没有什么意义了。 先声明地址解析
private GeoCoder geoCoder;//百度地址解析
然后创建一个地址解析结果监听器
/**
* 编码结果监听
*/
private OnGetGeoCoderResultListener onGetGeoCoderResultListener = new OnGetGeoCoderResultListener() {
/**
* 编码结果返回 就是通过具体位置信息获取坐标
* @param geoCodeResult 编码返回结果
*/
@Override
public void onGetGeoCodeResult(GeoCodeResult geoCodeResult) {
}
/**
* 反编码结果返回 就是通过坐标获取具体位置信息
* @param reverseGeoCodeResult 反编码返回结果
*/
@Override
public void onGetReverseGeoCodeResult(ReverseGeoCodeResult reverseGeoCodeResult) {
if (reverseGeoCodeResult == null
|| reverseGeoCodeResult.error != SearchResult.ERRORNO.NO_ERROR) {
// 没有检测到结果
return;
}
ReverseGeoCodeResult.AddressComponent addressDetail = reverseGeoCodeResult.getAddressDetail();
//需要的地址信息就在AddressComponent 里
ToastUtils.showShortToast(context,"地址信息:"+addressDetail.province+addressDetail.city+addressDetail.district+addressDetail.street);
Log.d("dataStr",addressDetail.province+addressDetail.city+addressDetail.district+addressDetail.street);
}
};
然后在initView中赋值和添加监听
geoCoder = GeoCoder.newInstance();//赋值
geoCoder.setOnGetGeoCodeResultListener(onGetGeoCoderResultListener);//反编码结果监听
最后就是在定位监听返回时获得坐标时,进行反编译
运行一下:
OK,现在已经拿到相应的省市县数据了,那么就可以进行天气的数据渲染和请求了。
- 补丁管理:不要以持续运行时间为自豪
- 微信小程序再次深夜放大招!竟然可以玩起直播来了?
- 上海设专项资金支持人工智能创新发展
- 监管下发145号文摸底互联网平台与交易所合作
- 条码支付确立规范引导金融创新趋利避害
- 百度Apollo自动驾驶组“国家队”,全线产品将在CES首度亮相
- Sass 基础(三)
- 2018年——霍金怎么样谈未来人工智能,地球还有多少时间
- Linux探秘之用户态与内核态
- 微信小游戏重磅上线,H5游戏和页游迎来新天地?
- 名为“闪电比特币”Lightning Bitcoin,LBTC的新型比特币硬叉被释放
- IDHub亮相区块链行业巅峰盛宴 分享网络生活的“进化论”
- 总结:如何加速你的 WordPress 站点?
- 百年老牌的创新之路:看可口可乐如何用AI、大数据颠覆传统营销
- java教程
- Java快速入门
- Java 开发环境配置
- Java基本语法
- Java 对象和类
- Java 基本数据类型
- Java 变量类型
- Java 修饰符
- Java 运算符
- Java 循环结构
- Java 分支结构
- Java Number类
- Java Character类
- Java String类
- Java StringBuffer和StringBuilder类
- Java 数组
- Java 日期时间
- Java 正则表达式
- Java 方法
- Java 流(Stream)、文件(File)和IO
- Java 异常处理
- Java 继承
- Java 重写(Override)与重载(Overload)
- Java 多态
- Java 抽象类
- Java 封装
- Java 接口
- Java 包(package)
- Java 数据结构
- Java 集合框架
- Java 泛型
- Java 序列化
- Java 网络编程
- Java 发送邮件
- Java 多线程编程
- Java Applet基础
- Java 文档注释
- 一致性哈希算法,在分布式开发中你必须会写,来看完整代码
- FFmpeg 开发(04):FFmpeg + OpenGLES 实现音频可视化播放
- 【Postgres扩展】pg_auto_failover支持高可用性和自动故障转移
- 「PostgreSQL」用MapReduce的方式思考,但使用SQL
- 每天百万交易的支付系统,生产环境该怎么设置JVM堆内存大小
- gb格式注释文件转换成gff3注释文件格式
- 有人问你Elasticsearch分布式架构原理,将这篇文章丢过去
- 每日两题 T1
- 每日两题 T2
- 每日两题 T3
- 搭建node服务(三):使用TypeScript
- 聊聊dubbo-go的EchoFilter
- 网页|实现酷炫3D相册
- JAVA|牛客网题解
- 谈一谈|递归解析之DFS全排列