Android 天气APP(二十四)地图天气(上)自动定位和地图点击定位

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第21天,点击查看活动详情

前言

之前也看过和风天气自己做的APP,主页面的地图点击之后,定位到某一个点,然后查看这个地方的天气,实际思路还是通过区县来查询天气的,只不过,加上了地图就比较的直观,看起来也会觉得很上档次,所以我也决定做一个这样的功能。 这篇文章实现的效果图如下:

正文

说到地图就想到了百度地图,当然你要是想用高德地图也是可以的,对接就可以了,我这里就不介绍了,还记得在第一篇文章的时候,就注册了百度地图开放平台的账号,并且申请了应用的key用于定位,点击百度地图开放平台进入。登录账号之后进入控制台,找到之前的那个应用,我的当然是GoodWeather,你们的要看自己的名字。

一、修改应用配置

还记得吗?我之前只选择了定位这个功能,点击设置进入配置页面

这是之前配置的。 如下图所示勾选,然后提交

提交之后就不用管了,点击开发文档 选择Android地图SDK

然后点击自定义下载

按照下图进行选择,然后选择标准开发包,下载

点击开发包然后开始下载,下载好之后解压文件。目录如下

选中这7个文件,粘贴到你的项目中libs下面,这时候你的项目会报错,因为缺少依赖的jar包,所以要Sync,等待完成之后你的jar可以打开的时候,你的项目就不会报错了。

然后BuildClean Project 清除掉一些之前的配置

现在运行一下,确保不影响我们之前的代码。

OK,很明显没问题,那么就开始操作吧。

二、地图显示

首先打开你的Application,在onCreate中放入地图SDK初始化的代码

java 复制代码
		//在使用SDK各组件之前初始化context信息,传入ApplicationContext
        SDKInitializer.initialize(this);

        //自4.3.0起,百度地图SDK所有接口均支持百度坐标和国测局坐标,用此方法设置您使用的坐标类型.
        //包括BD09LL和GCJ02两种坐标,默认是BD09LL坐标。
        SDKInitializer.setCoordType(CoordType.BD09LL);

然后就可以来显示地图了,以我的性格,那肯定是新建一个页面来做这个的。在ui包下新建一个MapWeatherActivity,然后修改布局activity_map_weather.xml 代码如下:

css 复制代码
<?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,修改代码如下:

java 复制代码
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代码如下:

css 复制代码
<?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,代码如下:

java 复制代码
	private LocationClient mLocationClient;//定位
    private BaiduMap mBaiduMap;//百度地图
java 复制代码
	/**
     * 定位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()));
        }
    }

注释都是代码里,应该够清楚了吧,官方接入文档都没有我这个清楚。 然后再写一个初始化定位的方法。

java 复制代码
	/**
     * 初始化定位
     */
    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的生命周期要对地图做相应的处理。代码如下:

java 复制代码
	@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层级的

java 复制代码
zoom(4.0f)

运行一下:

根据自己的需求来就行了。所以我设置的是13,大概就能看清楚附近的区/县就可以了,因为和风天气API请求的最低单位就是区/县。

四、点击地图定位

当然光是一个当前定位是不足以满足用户的,用户一般是都是想点那里就点那里,你点了没反应就是你的软件有问题,然后直接给你卸载,你哭都没地方哭去。下面来看看具体实现吧。

先声明需要的变量

java 复制代码
	private Marker marker;//标点也可以说是覆盖物
    private BitmapDescriptor bitmap;//标点的图标
    private double markerLatitude = 0;//标点纬度
    private double markerLongitude = 0;//标点经度
    private double latitude;//定位纬度
    private double longitude;//定位经度

刚才都说了是点击地图定位,那么肯定就需要一个点击事件对吧。当然这个事件肯定不是常规的View.OnClickListener,而是百度地图已经封装好的点击方法。

java 复制代码
	/**
     * 初始化地图点击
     */
    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下处理点击之后的业务逻辑就可以了。

java 复制代码
	//地图单击事件回调函数
	@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,否则你找不到这个控件。

java 复制代码
implementation 'com.google.android.material:material:1.0.0'

简单修改activity_map_weather.xml

css 复制代码
<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

java 复制代码
	@BindView(R.id.btn_auto_location)
    FloatingActionButton btnAutoLocation;//重新定位按钮

然后在点击的时候重置标点的经纬度参数值,清除标点,再重新定位

java 复制代码
	/**
     * 点击事件
     */
    @OnClick(R.id.btn_auto_location)
    public void onViewClicked() {
        markerLatitude = 0;
        markerLongitude = 0;
        marker.remove();//清除标点
        initLocation();
    }

在定位成功的回调中对定位按钮进行显示和隐藏的控制。 然后在新建一个initView方法,放入进入页面的一些基本配置。

OK,运行一下。

六、根据经纬度获取实际的定位信息

我们已经拿到经纬度信息了,那么这个信息需要转换为实际的位置,否则别人也不知道你定位在哪里,那就没有什么意义了。 先声明地址解析

java 复制代码
private GeoCoder geoCoder;//百度地址解析

然后创建一个地址解析结果监听器

java 复制代码
	/**
     * 编码结果监听
     */
    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中赋值和添加监听

java 复制代码
geoCoder = GeoCoder.newInstance();//赋值
geoCoder.setOnGetGeoCodeResultListener(onGetGeoCoderResultListener);//反编码结果监听

最后就是在定位监听返回时获得坐标时,进行反编译 运行一下:

OK,现在已经拿到相应的省市县数据了,那么就可以进行天气的数据渲染和请求了。请看下一篇文章

源码地址:GoodWeather 欢迎 StarFork

相关推荐
每次的天空13 分钟前
Android学习总结之应用启动流程(从点击图标到界面显示)
android·学习
一清三白2 小时前
Android Studio 连接雷电模拟器教程
android
姜行运2 小时前
数据结构【栈和队列附顺序表应用算法】
android·c语言·数据结构·算法
wang_peng3 小时前
android studio 基础
android·ide·android studio
〆、风神5 小时前
EasyExcel 数据字典转换器实战:注解驱动设计
android·java·注解
stevenzqzq5 小时前
Android studio xml布局预览中 Automotive和Autotive Distant Display的区别
android·xml·android studio
QING6186 小时前
Kotlin commonPrefixWith用法及代码示例
android·kotlin·源码阅读
QING6186 小时前
Kotlin groupByTo用法及代码示例
android·kotlin·源码阅读
兰琛11 小时前
Compose组件转换XML布局
android·xml·kotlin
水w13 小时前
【Android Studio】解决报错问题Algorithm HmacPBESHA256 not available
android·开发语言·android studio