安卓应用开发学习:获取经纬度及地理位置描述信息

前段时间,我在学习鸿蒙应用开发的过程中,在鸿蒙系统的手机上实现了获取经纬度及地理位置描述信息(鸿蒙应用开发学习:手机位置信息进阶,从经纬度数据获取地理位置描述信息)。反而学习时间更长的安卓应用开发还未实现获取经纬度及地理位置描述。这几天,我正在看《Android App 开发进阶与项目实战》一书,正好看到了第9章是讲定位导航的。这一章里正好有获取经纬度和详细地址的内容,随书还附带有源码。我照着做,很轻松的实现了用安卓手机获取经纬度和详细地址的功能。特此记录以备忘。

(我的安卓手机上实现了获取经纬度和详细地址)

稍微有点不足的就是,我的手机上显示的定位类型为 null,而书中显示的是卫星定位。这边书是几年前的,基于安卓11的而我的手机系统已经是安卓13,可能操作系统的不同,使得同样的代码运行效果有所不同吧。

我的这个应用中与获取经纬度及详细地址有关的代码如下:

1.获取经纬度及详细地址的Activity文件

src\main\java\......\LocationPageActivity.java

import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;import android.Manifest;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.location.Criteria;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;import com.bahamutjapp.task.GetAddressTask;
import com.bahamutjapp.util.DateUtil;
import com.bahamutjapp.util.SwitchUtil;import java.util.HashMap;
import java.util.Locale;
import java.util.Map;@SuppressLint(value={"DefaultLocale","SetTextI18n"})
public class LocationPageActivity extends AppCompatActivity {private final static String TAG = "myDebug";private Map<String,String> providerMap = new HashMap<>();private TextView tv_location; // 声明一个文本视图对象private String mLocationDesc = ""; // 定位说明private LocationManager mLocationMgr; // 声明一个定位管理器对象private Handler mHandler = new Handler(Looper.myLooper()); // 声明一个处理器对象private boolean isLocationEnable = false; // 定位服务是否可用@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_location_page);tv_location = findViewById(R.id.tv_location);providerMap.put("gps", "卫星定位");providerMap.put("network", "网络定位");SwitchUtil.checkLocationIsOpen(this, "需要打开定位功能才能查看定位信息");}@Overrideprotected void onResume() {super.onResume();mHandler.removeCallbacks(mRefresh); // 移除定位刷新任务initLocation(); // 初始化定位服务mHandler.postDelayed(mRefresh, 100); // 延迟100毫秒启动定位刷新任务}// 初始化定位服务private void initLocation() {// 从系统服务中获取定位管理器mLocationMgr = (LocationManager) getSystemService(Context.LOCATION_SERVICE);Criteria criteria = new Criteria(); // 创建一个定位准则对象// 设置定位精确度。Criteria.ACCURACY_COARSE表示粗略,Criteria.ACCURACY_FIN表示精细criteria.setAccuracy(Criteria.ACCURACY_FINE);criteria.setAltitudeRequired(true); // 设置是否需要海拔信息criteria.setBearingRequired(true); // 设置是否需要方位信息criteria.setCostAllowed(true); // 设置是否允许运营商收费criteria.setPowerRequirement(Criteria.POWER_LOW); // 设置对电源的需求// Log.d(TAG, "初始化定位服务, 准备获取定位管理器的最佳定位提供者");// 获取定位管理器的最佳定位提供者String bestProvider = mLocationMgr.getBestProvider(criteria, true);if (mLocationMgr.isProviderEnabled(bestProvider)) { // 定位提供者当前可用tv_location.setText("正在获取" + providerMap.get(bestProvider) + "对象");mLocationDesc = String.format("【定位信息】\n定位类型为%s", providerMap.get(bestProvider));beginLocation(bestProvider); // 开始定位isLocationEnable = true;} else { // 定位提供者暂不可用tv_location.setText(providerMap.get(bestProvider) + "不可用");isLocationEnable = false;}}// 显示定位结果文本private void showLocation(Location location) {if (location != null) {// 创建一个根据经纬度查询详细地址的任务GetAddressTask task = new GetAddressTask(this, location, address -> {String desc = String.format(Locale.CHINESE,"%s" +"\n\t定位时间为%s," + "\n\t经度为%f,纬度为%f," +"\n\t高度为%d米,精度为%d米," +"\n\t详细地址为%s。",mLocationDesc, DateUtil.formatDate(location.getTime()),location.getLongitude(), location.getLatitude(),Math.round(location.getAltitude()), Math.round(location.getAccuracy()),address);tv_location.setText(desc);});task.start(); // 启动地址查询任务} else {tv_location.setText(mLocationDesc + "\n暂未获取到定位对象");}}// 开始定位private void beginLocation(String method) {// 检查当前设备是否已经开启了定位功能if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)!= PackageManager.PERMISSION_GRANTED) {Toast.makeText(this, "请授予定位权限并开启定位功能", Toast.LENGTH_SHORT).show();return;}// 设置定位管理器的位置变更监听器mLocationMgr.requestLocationUpdates(method, 300, 0, mLocationListener);// 获取最后一次成功定位的位置信息Location location = mLocationMgr.getLastKnownLocation(method);showLocation(location); // 显示定位结果文本}// 定义一个位置变更监听器private LocationListener mLocationListener = new LocationListener() {@Overridepublic void onLocationChanged(Location location) {showLocation(location); // 显示定位结果文本}@Overridepublic void onProviderDisabled(String arg0) {}@Overridepublic void onProviderEnabled(String arg0) {}@Overridepublic void onStatusChanged(String arg0, int arg1, Bundle arg2) {}};// 定义一个刷新任务,若无法定位则每隔一秒就尝试定位private Runnable mRefresh = new Runnable() {@Overridepublic void run() {if (!isLocationEnable) {initLocation(); // 初始化定位服务mHandler.postDelayed(this, 1000);}}};@Overrideprotected void onDestroy() {super.onDestroy();mLocationMgr.removeUpdates(mLocationListener); // 移除定位管理器的位置变更监听器}}

2.Activity文件对应的xml文件

src\main\res\layout\activity_location_page.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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=".LocationPageActivity"><TextViewandroid:id="@+id/tv_locationTitle"android:layout_width="wrap_content"android:layout_height="30dp"android:text="定位导航"android:textSize="24sp"android:textStyle="bold"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent" /><TextViewandroid:id="@+id/tv_location"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginStart="10dp"android:layout_marginTop="50dp"android:layout_marginEnd="10dp"android:paddingStart="10dp"android:paddingEnd="10dp"android:text="【定位信息】"android:textSize="16sp"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toBottomOf="@+id/tv_locationTitle" /></androidx.constraintlayout.widget.ConstraintLayout>

3.GetAddressTask.java文件(此文件根据经纬度数据获取详细地址信息)

src\main\java\......\task\GetAddressTask.java

import android.app.Activity;
import android.location.Location;
import android.util.Log;
import android.widget.Toast;import androidx.annotation.NonNull;import org.json.JSONException;
import org.json.JSONObject;import java.io.IOException;
import java.util.Objects;import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;// 根据经纬度获取详细地址的线程
public class GetAddressTask extends Thread {private static final String TAG = "GetAddressTask";private String mQueryUrl = "https://api.tianditu.gov.cn/geocoder?postStr={'lon':%f,'lat':%f,'ver':1}&type=geocode&tk=253b3bd69713d4bdfdc116255f379841";private Activity mAct; // 声明一个活动实例private OnAddressListener mListener; // 声明一个获取地址的监听器对象private Location mLocation; // 声明一个定位对象public GetAddressTask(Activity act, Location location, OnAddressListener listener) {mAct = act;mListener = listener;mLocation = location;}@Overridepublic void run() {String url = String.format(mQueryUrl, mLocation.getLongitude(), mLocation.getLatitude());Log.d(TAG, "url="+url);OkHttpClient client = new OkHttpClient(); // 创建一个okhttp客户端对象// 创建一个GET方式的请求结构Request request = new Request.Builder().url(url).build();Call call = client.newCall(request); // 根据请求结构创建调用对象// 加入HTTP请求队列。异步调用,并设置接口应答的回调方法call.enqueue(new Callback() {@Overridepublic void onFailure(@NonNull Call call, @NonNull IOException e) { // 请求失败// 回到主线程操纵界面mAct.runOnUiThread(() -> Toast.makeText(mAct,"查询详细地址出错:"+e.getMessage(), Toast.LENGTH_SHORT).show());}@Overridepublic void onResponse(@NonNull Call call, @NonNull final Response response) throws IOException { // 请求成功String resp = Objects.requireNonNull(response.body()).string();Log.d(TAG, "resp="+resp);// 下面从json串中逐级解析formatted_address字段获得详细地址描述try {JSONObject obj = new JSONObject(resp);JSONObject result = obj.getJSONObject("result");String address = result.getString("formatted_address");// 回到主线程操纵界面mAct.runOnUiThread(() -> mListener.onFindAddress(address));} catch (JSONException e) {e.printStackTrace();}}});}// 定义一个查询详细地址的监听器接口public interface OnAddressListener {void onFindAddress(String address);}}

4.DateUtil.java文件(对日期数据进行格式化)

src\main\java\......\util\DateUtil.java

import android.annotation.SuppressLint;import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;@SuppressLint("SimpleDateFormat")
public class DateUtil {// 获取当前的日期时间public static String getNowDateTime() {SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");return sdf.format(new Date());}// 将长整型的时间数值格式化为日期时间字符串public static String formatDate(long time) {Date date = new Date(time);SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");return sdf.format(date);}}

5.SwitchUtil.java文件(获取定位功能开关状态)

src\main\java\......\util\SwitchUtil.java

import android.content.Context;
import android.content.Intent;
import android.location.LocationManager;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.wifi.WifiManager;
import android.provider.Settings;
import android.telephony.TelephonyManager;
import android.util.Log;
import android.widget.Toast;import java.lang.reflect.Method;public class SwitchUtil {private static final String TAG = "SwitchUtil";// 获取定位功能的开关状态public static boolean getLocationStatus(Context ctx) {// 从系统服务中获取定位管理器LocationManager lm = (LocationManager) ctx.getSystemService(Context.LOCATION_SERVICE);return lm.isProviderEnabled(LocationManager.GPS_PROVIDER);}// 检查定位功能是否打开,若未打开则跳到系统的定位功能设置页面public static void checkLocationIsOpen(Context ctx, String hint) {if (!getLocationStatus(ctx)) {Toast.makeText(ctx, hint, Toast.LENGTH_SHORT).show();Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);ctx.startActivity(intent);}}}

6月25日补充:

今天代码进一步研究发现导致页面上定位类型显示为null的原因是在LocationPageActivity.java文件中的源代码“ String bestProvider = mLocationMgr.getBestProvider(criteria, true); ” 的返回值是“fused”。通过搜寻资料,才知道这是一种定位类型(融合定位),介绍资料见下面的链接:

Fused定位

再对代码进行仔细研究,发现上面的代码的返回值赋值给“bestProvider”后,通过执行以下语句获取在页面上显示定位类型的字符串:

mLocationDesc = String.format("【定位信息】\n\t定位类型为%s", providerMap.get(bestProvider));

 这个语句是将“bestProvider”作为参数从providerMap对象中获取对应的值。而providerMap对象是在onCreate方法中赋值的:

providerMap.put("gps", "卫星定位");
providerMap.put("network", "网络定位");

因为只put了两个键值对,没有fused的键值对,因此得到的结果是null。解决方法就是再加入下面这条即可。

providerMap.put("fused", "融合");

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/364557.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

计算机视觉全系列实战教程 (十四):图像金字塔(高斯金字塔、拉普拉斯金字塔)

1.图像金字塔 (1)下采样 从G0 -> G1、G2、G3 step01&#xff1a;对图像Gi进行高斯核卷积操作&#xff08;高斯滤波&#xff09;step02&#xff1a;删除所有的偶数行和列 void cv::pyrDown(cv::Mat &imSrc, //输入图像cv::Mat &imDst, //下采样后的输出图像cv::Si…

第一节:如何开发第一个spring boot3.x项目(自学Spring boot 3.x的第一天)

大家好&#xff0c;我是网创有方&#xff0c;从今天开始&#xff0c;我会记录每篇我自学spring boot3.x的经验。只要我不偷懒&#xff0c;学完应该很快&#xff0c;哈哈&#xff0c;更新速度尽可能快&#xff0c;想和大佬们一块讨论&#xff0c;如果需要讨论的欢迎一起评论区留…

零基础开始学习鸿蒙开发-页面导航栏布局设计

1.设定初始页(Idex.ets) import {find} from ../pages/find import {home} from ../pages/home import {setting} from ../pages/setting Entry Component struct Index {private controller: TabsController new TabsController()State gridMargin: number 10State gridGut…

[图解]建模相关的基础知识-19

1 00:00:00,640 --> 00:00:04,900 前面讲了关系的这些范式 2 00:00:06,370 --> 00:00:11,570 对于我们建模思路来说&#xff0c;有什么样的作用 3 00:00:12,660 --> 00:00:15,230 我们建模的话&#xff0c;可以有两个思路 4 00:00:16,790 --> 00:00:20,600 一个…

项目实训-接口测试(十八)

项目实训-后端接口测试&#xff08;十八&#xff09; 文章目录 项目实训-后端接口测试&#xff08;十八&#xff09;1.概述2.测试对象3.测试一4.测试二 1.概述 本篇博客将记录我在后端接口测试中的工作。 2.测试对象 3.测试一 这段代码是一个单元测试方法&#xff0c;用于验证…

二叉树从根节点出发的所有路径

二叉树从根节点出发的所有路径 看上图中 二叉树结构 从根节点出发的所有路径 如下 6->4->2->1 6->4->2->3 6->4->5 6->8->7 6->8->9 逻辑思路&#xff1a; 按照先序遍历 加 回溯法 实现 代码如下 // 调用此方法&#xff0c;将根节点传递…

【Lua】第二篇:打印函数和注释

文章目录 一. 打印函数二. 注释方式1. 单行注释2. 多行注释 一. 打印函数 Lua 程序是以 .lua 结尾的文件&#xff0c;创建一个的 Test.lua 的文件&#xff0c;使用 print 函数输出字符串"Hello World"&#xff1a; print(Hello World) 保存之后使用命令lua 文件名编…

安卓开发自定义时间日期显示组件

安卓开发自定义时间日期显示组件 问题背景 实现时间和日期显示&#xff0c;左对齐和对齐两种效果&#xff0c;如下图所示&#xff1a; 问题分析 自定义view实现一般思路&#xff1a; &#xff08;1&#xff09;自定义一个View &#xff08;2&#xff09;编写values/attrs.…

如何用Go语言,实现基于宏系统的解释器?

目录 一、Go语言介绍二、什么是宏系统三、什么是解释器四、如何用Go语言实现一个基于宏系统的解释器&#xff1f; 一、Go语言介绍 Go语言&#xff0c;又称为Golang&#xff0c;是一种由谷歌公司开发并开源的编程语言。Go语言的设计目标是提高程序员的生产力&#xff0c;同时具…

Oracle、MySQL、PostGreSQL、SQL Server-空值

Oracle、MySQL、PostGreSQL、SQL Server-null value 最近几年数据库市场百花齐放&#xff0c;在做跨数据库迁移的数据库选型时&#xff0c;除了性能、稳定、安全、运维、功能、可扩展外&#xff0c;像开发中对于值的处理往往容易被人忽视&#xff0c; 之前写过一篇关于PG区别O…

2024年6月26日 (周三) 叶子游戏新闻

老板键工具来唤去: 它可以为常用程序自定义快捷键&#xff0c;实现一键唤起、一键隐藏的 Windows 工具&#xff0c;并且支持窗口动态绑定快捷键&#xff08;无需设置自动实现&#xff09;。 土豆录屏: 免费、无录制时长限制、无水印的录屏软件 《Granblue Fantasy Versus: Risi…

Cisco Identity Services Engine (ISE) 3.3 Patch 2 - 基于身份的网络访问控制和策略实施系统

Cisco Identity Services Engine (ISE) 3.3 Patch 2 - 基于身份的网络访问控制和策略实施系统 思科身份服务引擎 (ISE) - 下一代 NAC 解决方案 请访问原文链接&#xff1a;Cisco Identity Services Engine (ISE) 3.3 Patch 2 - 基于身份的网络访问控制和策略实施系统&#xf…

笔灵AI写作:释放创意,提升写作效率的秘诀

内容为王&#xff0c;在内容创作的世界中尤为重要。然而&#xff0c;面对写作时常常感到无从下手&#xff1a;有时缺乏灵感&#xff0c;有时难以表达清楚自己的想法。AI写作助手的出现&#xff0c;为这些问题提供了创新的解决方案&#xff0c;极大地改变了内容创作的过程。 今…

STM32——使用TIM输出比较产生PWM波形控制舵机转角

一、输出比较简介&#xff1a; 只有高级定时器和通用寄存器才有输入捕获/输出比较电路&#xff0c;他们有四个CCR&#xff08;捕获/比较寄存器&#xff09;&#xff0c;共用一个CNT&#xff08;计数器&#xff09;&#xff0c;而输出比较功能是用来输出PWM波形的。 红圈部分…

C语言调用python

1、linux中安装libpython3.10-dev 通过C语言调用python代码&#xff0c;需要先安装libpython3的dev依赖库&#xff08;不同的ubuntu版本下&#xff0c;python版本可能会有差异&#xff0c; 比如ubuntu 22.04里是libpython3.10-dev&#xff09;。 首先可以通过以下命令验证…

数字信号处理实验一(离散信号及离散系统的MATLAB编程实现)

实验要求&#xff1a; 离散信号及离散系统的MATLAB编程实现&#xff08;2学时&#xff09; 要求&#xff1a; 编写一程序&#xff0c;输出一定长度&#xff08;点数&#xff09;&#xff0c;具有一定幅度、&#xff08;角&#xff09;频率和初始相位的实&#xff08;或复&…

gitee配置ssh教程

生成公钥 执行命令&#xff1a; ssh-keygen -t rsa查看公钥 cat ~/.ssh/id_rsa.pub这个公钥就是要复制粘贴到Gitee中的ssh公钥。 配置Gitee SSH公钥 来到Gitee的ssh公钥中&#xff0c;配置

ONLYOFFICE8.1版本桌面编辑器简单测评

ONLYOFFICE官网链接&#xff1a;在线PDF查看器和转换器 | ONLYOFFICE ONLYOFFICE介绍&#xff1a;https://www.onlyoffice.com/zh/office-suite.aspx OnlyOffice 是一款免费且开源的 Office 协作办公套件&#xff0c;支持桌面端和移动端等多平台&#xff0c;由一家领先的 IT 公…

热管的原理和棒芯的加工

当热管的一端受热时&#xff0c;毛细芯中的液体蒸发汽化&#xff0c;蒸汽在微小的压差下流向另一端&#xff0c;放出热量凝结成液体&#xff0c;液体再靠毛细力&#xff08;或重力&#xff09;的作用&#xff0c;沿多孔材料流回蒸发段。如此循环不已&#xff0c;热量便从一端传…

禁止浏览器对input的自动填充和填充提示(适用于谷歌、火狐、Edge(原IE浏览器)等常见浏览器)

目录 1.要解决的问题2.一技能&#xff1a;原生属性&#xff0c;小试牛刀3.二技能&#xff1a;傀儡input&#xff0c;瞒天过海4.三技能&#xff1a;JavaScript出击&#xff0c;直接开大5.九九八十一难&#xff0c;永远还有最后一难 写在前面&#xff1a; 如有转载&#xff0c;务…