基于Android Studio的行程记录APK开发指南(三)---界面设计及两种方法获取用户位置

news2024/11/23 17:06:50

前言

  • 本系列教程我们来看看如何使用Android Studio去开发一个APK用于用户的实时行程记录
    • 第一期:基于Android Studio的用户行程记录APK开发指南(一):项目基础配置与速通Kotlin-CSDN博客
    • 第二期:基于Android Studio的行程记录APK开发指南(二):熟悉一个项目结构-CSDN博客
  • 前两期我们已经熟悉了kotlin语言特性和android studio一个项目模板的项目结构,本节将设计一个简单的UI,包含一个图片和文本及其一个按钮,当用户按下按钮时候将输出用户的位置信息

01 添加位置权限

  • 为了在Android应用中获取用户的位置信息,我们需要在AndroidManifest.xml文件中添加位置相关的权限。(上一节我们提到过AndroidManifest描述了应用程序的组件以及应用程序的权限、主题和所需的库。)
  • <manifest>标签内,添加如下代码:
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.INTERNET" />
  1. ACCESS_FINE_LOCATION:允许应用程序访问精确的位置信息。
  2. ACCESS_COARSE_LOCATION:允许应用程序访问大致的位置信息。

02 设计程序界面

2-1 打开界面
  • 我们这一节来设计一个简单的界面,回顾上一节,res/layout/activity_main.xml描述了MainActivity的界面设计请添加图片描述

  • 我们在右上角找到split按钮如下:

  • 请添加图片描述

  • 如此一来我们就可以实时进行xml编写和查看效果展示了请添加图片描述

2-2 导入图片
  • 上一节说到,res/drawable文件夹通常存放图像资源,这里我们导入一张下载好的图片,放到drawable中,这时候图像就可以通过@drawable/pyrmont被访问请添加图片描述

  • 我们在activity_main.xml中添加一个ImageView组件,并指定位置,这时候图片就被导入到我们的界面中了请添加图片描述

  • 这时候我们点击图片右侧圆形,拖动直至右边框,这样图片就被绑定到界面的右侧请添加图片描述

  • 这样无论是怎么样的设备,图像都会加载到屏幕的最右侧 请添加图片描述

  • 同理我们把图片绑定到正中央,代码会根据拖动自动生成对应的代码请添加图片描述

<ImageView  
    android:layout_width="271dp"  
    android:layout_height="278dp"  
    android:src="@drawable/pyrmont"  
    app:layout_constraintBottom_toBottomOf="parent"  
    app:layout_constraintEnd_toEndOf="parent"  
    app:layout_constraintStart_toStartOf="parent"  
    app:layout_constraintTop_toTopOf="parent"  
    app:layout_constraintVertical_bias="0.291">  
</ImageView>

2-3 添加文本
  • 同样我们添加一个文本
<TextView  
    android:id="@+id/text_location"  
    android:layout_width="match_parent"  
    android:layout_height="wrap_content"  
    android:layout_margin="10dp"  
    android:text="当前位置"  
    android:textSize="20sp"  
    app:layout_constraintTop_toBottomOf="@+id/imageView"></TextView>
  • android:id="@+id/text_location": 为TextView设置了一个唯一标识符,这样我们可以在代码中引用它
  • android:layout_width="match_parent": 设置TextView的宽度为父容器(通常是Activity的布局)的宽度。
  • android:layout_height="wrap_content": 设置TextView的高度为足够包裹其内容的最大高度。
  • android:layout_margin="10dp": 在TextView的上下左右四个方向都添加了10dp的边距。dp(密度无关像素)是一个相对单位,用于根据屏幕密度调整元素的大小。
  • android:text="当前位置": 设置TextView中显示的文本内容为“当前位置”。
  • android:textSize="20sp": 设置文本的大小为20sp(scale-independent pixels,与密度无关的像素)。sp单位与dp类似,但它是专门为字体大小设计的,可以根据用户的字体大小偏好自动缩放。
  • 这里我们同样拖动文本框的上部分到派蒙图片的正下方,代码自动生成app:layout_constraintTop_toBottomOf="@+id/imageView"></TextView>请添加图片描述

2-4 添加按钮
  • 这里我们来学习如何自定义样式的按钮,我要创建一个灰色背景色,白色按钮的样式
  • 搜先我们先来添加颜色,上一节我们提到了res/values/colors.xml中存放了基本的颜色
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="black">#FF000000</color>
    <color name="white">#FFFFFFFF</color>
    <color name="gray">#A8A8A8</color> <!-- 灰色 -->
</resources>

  • 同时我们在在res文件夹下创建styles.xml,用于存档风格资源
<?xml version="1.0" encoding="utf-8"?>  
<resources>  
    <style name="CustomGrayButtonStyle">  
        <item name="android:textSize">30sp</item>  
        <item name="android:textColor">@color/white</item> <!-- 白色文本 -->  
        <item name="android:backgroundTint">@color/gray</item> <!-- 灰色背景 -->  
        <item name="android:padding">10dp</item>  
        <item name="android:layout_margin">10dp</item>  
        <item name="android:layout_width">187dp</item>  
        <item name="android:layout_height">113dp</item>  
    </style></resources>
  • 回到activity_main.xml,我们就可以直接引用这个按钮了
<Button  
    android:id="@+id/button_getLocation"  
    android:text="开!"  
    style="@style/CustomGrayButtonStyle"  
    app:layout_constraintBottom_toBottomOf="parent"  
    app:layout_constraintEnd_toEndOf="parent"  
    app:layout_constraintStart_toStartOf="parent" />
  • 同时我们绑定按钮在最下方的正中央请添加图片描述

  • 好至此我们简单的界面设计完毕,我们来完善代码


3 编写获取位置具体代码逻辑(方法1)

3-1 实例化控件
  • 我们实例化刚刚设计的按钮和文本,我们需要导入库,这里导入一个ToastToast 是 Android 系统中提供的一种轻量级反馈机制,用于向用户显示简短的消息提示。它是一种非侵入式的 UI 元素,通常在屏幕底部以弹窗的形式出现,并在一段时间后自动消失,不会干扰用户的当前操作。
import android.widget.Button  
import android.widget.TextView
import android.widget.Toast
  • 记得我们第一节提到的延迟实例化的时候使用lateinit
lateinit var button_location: Button  
lateinit var text_location:TextView
  • 获取控件
button_location = findViewById<Button>(R.id.button_getLocation)  
text_location=findViewById<TextView>(R.id.text_location)  
  • 同时我们为按钮绑定一个函数,当用户按下按钮时,会弹出一个信息,过一会就会自动消失

button_location.setOnClickListener {  
	Toast.makeText(this, "按钮被按下", Toast.LENGTH_SHORT).show()
}   

请添加图片描述


3-2 获取位置
  • FusedLocationProviderClient 是 Google Play 服务提供的一个类,用于简化对设备位置信息的访问。它是一个高级的 API,旨在提供更准确和更高效的位置信息,同时减少电池消耗。FusedLocationProviderClient 结合了多种位置来源,包括 GPS、Wi-Fi 和移动网络,以提供最佳的位置信息。
lateinit var fusedLocationProviderClient:FusedLocationProviderClient
  • 这里我们编写一个getUserLocation函数,用于获取用户设备的当前地理位置。
private fun getUserLocation() {  
    if (ContextCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {  
        ActivityCompat.requestPermissions(this, arrayOf(android.Manifest.permission.ACCESS_FINE_LOCATION), 100)  
        Toast.makeText(this,"未获取权限",Toast.LENGTH_LONG).show()  
            return  
    }  
  
    val location=fusedLocationProviderClient.lastLocation  
    location.addOnSuccessListener {  
        if(it!=null)  
        {  
            val latitude = it.latitude.toString()  
            val longitude = it.longitude.toString()  
            text_location.text = "Latitude: $latitude\nLongitude:$longitude"  
        }  
    }  
    location.addOnFailureListener {exception ->  
  
        text_location.text=exception.localizedMessage  
  
        Toast.makeText(this, "获取位置信息失败: ${exception.localizedMessage}", Toast.LENGTH_LONG).show()  
    }  
  
}
  • 搜先我们需要检查应用是否有访问精确位置(GPS)的权限
ContextCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTE
  • 如果用户没有同意,我们需要ActivityCompat.requestPermissions(this, arrayOf(android.Manifest.permission.ACCESS_FINE_LOCATION), 100)请求权限,并将请求码设置为 100。同时,弹出一个 Toast 提示用户未获取权限。
  • 如果应用有权限,调用 fusedLocationProviderClient.lastLocation 方法来获取设备的最后已知位置。这个方法返回一个 Task<Location> 对象
    • addOnSuccessListener添加一个成功监听器,当位置获取成功时,这个监听器会被调用。
    • addOnFailureListener添加一个失败监听器,当位置获取失败时,这个监听器会被调用。
  • 最后我们吧这个函数和按钮绑定
button_location.setOnClickListener{  
    getUserLocation()  
}

3-3 运行
  • 运行会进行位置提示,用户可以选择是否允许使用位置权限请添加图片描述

  • 同意权限将输出位置的经纬度信息请添加图片描述

  • Android Studio 模拟器默认的经纬度位置通常是位于美国加利福尼亚州的谷歌总部附近,即纬度大约为 37.4219999,经度大约为 -122.084。这个位置是模拟器在启动时默认设置的,因此,如果你没有在模拟器设置中更改位置,那么应用获取到的位置信息将会是这个默认位置。请添加图片描述

  • 值得一提的是我们可以修改虚拟设备的GPS位置,找到Extended Controls请添加图片描述

  • 在Location中可以更改地理位置请添加图片描述

  • 此外细心的朋友会发现一个小问题,这里使用的定位是基于Google Play服务的,那如果用户手机不支持这个服务怎么办,后面我们会细说


4 导出APK

APK
  • APK(Android Package Kit)是一种用于打包和分发Android应用程序的文件格式。它包含了应用程序的所有文件,如代码、资源、资产、证书和清单文件等。APK文件是Android操作系统中应用程序安装和更新的基本单元。
  • APK文件通常具有.apk扩展名,并且可以被安装在支持Android的设备上。
导出APK
  • 成功在模拟器运行了这个小demo,相信各位已经迫不及待想在自己手机尝试了吧

  • 在build中选择生成APK请添加图片描述

  • 这里我们选择APK请添加图片描述

  • 初次使用的话需要新建存储钥匙请添加图片描述

  • 这里看着填一下就行请添加图片描述

  • 创建完后选择钥匙存储地,再次next请添加图片描述

  • 选择APK生成位置,选择release版本,点击create等待片刻请添加图片描述

  • APK生成成功后会出现提示请添加图片描述

  • 我们来到安装位置,就拿到我们的APK文件了请添加图片描述


5 实机运行与问题分析

遇到问题
  • 拿到APK放到手机上是否满心欢喜?没事,不出意外的话该出意外了

  • 我们把APK移动到手机上,点击安装,运行,然后,完辣请添加图片描述

  • 仔细的朋友已经明白了,我的手机没有google play服务,天塌了亚(不是)

  • 在用户设备不支持Google Play的情况下,我们可以使用其他方式来获取用户的地理位置。一个常见的选择是使用Android的LocationManager API,该API是Android原生支持的一部分,不需要依赖Google Play服务。


6 获取位置再战(方法2)

6-1 LocationManager
  • LocationListener 是 Android 开发中的一个接口,它允许我们的应用接收关于设备位置变化的更新。当设备的位置发生改变时,系统会通过 LocationListener 调用我们的应用中实现的方法,以便我们能够根据这些变化做出相应的反应。
  • 我们继承自LocationListener
class MainActivity : AppCompatActivity(),LocationListener
  • LocationListener 接口包含以下三个方法:

    1. onLocationChanged(Location location): 当设备的位置发生变化时,这个方法会被调用。我们可以通过传递给这个方法的 Location 对象来获取新的位置信息,如经度、纬度、海拔等。
    2. onStatusChanged(String provider, int status, Bundle extras): 当位置提供者的状态发生变化时,这个方法会被调用。状态变化可能包括位置提供者变为可用、不可用或者位置更新发生错误。provider 参数指定了位置提供者的名称,如 LocationManager.GPS_PROVIDERLocationManager.NETWORK_PROVIDER
    3. onProviderEnabled(String provider): 当位置提供者被启用时,这个方法会被调用。这通常发生在用户在设置中启用 GPS 或网络位置服务时。
    4. onProviderDisabled(String provider): 当位置提供者被禁用时,这个方法会被调用。这通常发生在用户在设置中禁用 GPS 或网络位置服务时。
  • 要使用 LocationListener,我们需要在我们的应用中实现这个接口,并在适当的时候注册和注销监听器。注册监听器通常是通过调用 LocationManager.requestLocationUpdates() 方法来完成的,而注销监听器则是通过调用 LocationManager.removeUpdates() 方法。

  • 但值得一提的是,onStatusChanged,onProviderEnabled,onProviderDisabled这几个函数在API 29以上的版本被设置为过时了请添加图片描述

  • 我们继承自LocationListener,并点击红色灯泡选择复写方法请添加图片描述

  • 选择方法请添加图片描述

  • 代码自动就会为你生成

override fun onLocationChanged(p0: Location) {  
    TODO("Not yet implemented")  
}
6-2 配置LocationMannager
  • LocationManager 是 Android 系统中用于管理设备位置服务的类。它允许您访问各种位置提供者,如 GPS、Wi-Fi 和移动网络,以获取设备的当前位置。LocationManager 提供了多种方法来查询和监听位置更新,以及管理位置提供者的状态。
    • GPS_PROVIDER: 提供最准确的位置数据,但功耗较高。
    • NETWORK_PROVIDER: 使用设备的网络连接(如 Wi-Fi 和移动数据)来获取位置,速度较快但精度较低。
    • PASSIVE_PROVIDER: 不主动获取位置,而是作为其他提供者的辅助,减少电量消耗。
lateinit var location_mannager:LocationManager
  • 同样的我们创建一个getUserLocation的函数
private fun getUserLocation()  
{  
    location_mannager=getSystemService(Context.LOCATION_SERVICE) as LocationManager  
		    location_mannager.requestLocationUpdates(LocationManager.GPS_PROVIDER,5000,5f,this)  
}
  1. location_mannager=getSystemService(Context.LOCATION_SERVICE) as LocationManager
    • 这一行代码获取系统的 LocationManager 服务。getSystemService(Context.LOCATION_SERVICE) 返回一个 Object,我们使用 as 关键字将其转换为 LocationManager 类型。这样,我们就可以使用 LocationManager 的方法来请求位置更新。
  2. 后一行代码是请求位置更新的核心。requestLocationUpdates 方法接受四个参数:
    - LocationManager.GPS_PROVIDER: 指定使用 GPS 作为位置提供者。您也可以使用其他提供者,如 LocationManager.NETWORK_PROVIDER
    - 5000: 这是更新间隔的时间,以毫秒为单位。在这个例子中,它设置为 5000 毫秒(5 秒)。
    - 5f: 这是位置变化的最小距离,以米为单位。在这个例子中,它设置为 5 米。这意味着设备的位置至少需要移动 5 米,才会触发位置更新。
    - this: 这是指向当前活动(MainActivity)的引用,因为活动实现了 LocationListener 接口。当位置更新发生时,onLocationChanged 方法会被调用。
  • 我们进行requestLocationUpdates时根据IDE提示,我们需要在获取位置之前进行权限检查请添加图片描述

  • 和上面一样,我们选择让他帮我补全(doge)请添加图片描述

  • 如下请添加图片描述

  • 我们只需要添加,当用户权限被禁止时候,申请同步

private fun getUserLocation()  
{  
    location_mannager=getSystemService(Context.LOCATION_SERVICE) as LocationManager  
    if (ActivityCompat.checkSelfPermission(  
            this,  
            ACCESS_FINE_LOCATION  
        ) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(  
            this,  
            Manifest.permission.ACCESS_COARSE_LOCATION  
        ) != PackageManager.PERMISSION_GRANTED  
    ) {  
        Toast.makeText(this,"Permission denied", Toast.LENGTH_SHORT).show()  
        ActivityCompat.requestPermissions(  
            this,  
            arrayOf(Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION),100  
        )  
        return  
    }  
    location_mannager.requestLocationUpdates(LocationManager.GPS_PROVIDER,5000,5f,this)  
}
6-3 获取位置信息更新
  • 回到onLocationChanged函数,这次我们直接生成APK,尝试,让后成功!
override fun onLocationChanged(p0: Location) {  
    text_location.text=p0.latitude.toString()+","+p0.longitude.toString()  
Toast.makeText(this,""+p0.latitude.toString()+p0.longitude.toString(),Toast.LENGTH_SHORT).show()
}
6-4 实测
  • 手机实测如下(具体的信息我就不公布了哈哈哈,因为他真的超级准…害怕史密达)请添加图片描述

7 Geocoder(题外话)

  • Geocoder 是一种编程接口,用于将地理坐标(如纬度和经度)转换为具体的地址信息,或者将地址信息转换为地理坐标。这种转换通常被称为地理编码(geocoding)和反向地理编码(reverse geocoding)。
  • 在 Android 开发中,Geocoder 类提供了这种功能。它允许开发者在应用中实现位置搜索、地图显示、地址验证等功能。Geocoder 使用了多种数据源来提供尽可能准确的位置信息,包括街道名称、城市、州、邮政编码和国家等。
override fun onLocationChanged(p0: Location) {  
    val geocoder = Geocoder(this, Locale.getDefault())  
    val addresses: List<Address>?  
    try {  
        addresses = geocoder.getFromLocation(p0.latitude, p0.longitude, 1)  
        if (addresses != null && addresses.isNotEmpty()) {  
            val address = addresses[0]  
            val streetAddress = address.getAddressLine(0) ?: "No street address"  
            val country = address.countryName ?: "No country found"  
            val addressText = if (streetAddress.isNotBlank() && country.isNotBlank()) {  
                "$streetAddress,$country"  
            } else {  
                "No detailed address available"  
            }  
            val latitudeText = "Latitude: ${p0.latitude.toString()}"  
            val longitudeText = "Longitude: ${p0.longitude.toString()}"  
            text_location.text = "$addressText\n$latitudeText\n$longitudeText"  
            Toast.makeText(this, "$addressText\n$latitudeText\n$longitudeText", Toast.LENGTH_SHORT).show()  
        }  
    } catch (e: IOException) {  
        e.printStackTrace()  
        text_location.text = "Geocoder error"  
        Toast.makeText(this, "Geocoder error", Toast.LENGTH_SHORT).show()  
    }  
}
  • 效果如下,手机版就不展示了,是可以用的请添加图片描述

代码汇总

  • 法1:
package com.example.pathrecorderapp  
  
import android.content.pm.PackageManager  
import android.os.Bundle  
import android.widget.Button  
import android.widget.TextView  
import androidx.activity.enableEdgeToEdge  
import androidx.appcompat.app.AppCompatActivity  
import androidx.core.app.ActivityCompat  
import androidx.core.content.ContextCompat  
import androidx.core.view.ViewCompat  
import androidx.core.view.WindowInsetsCompat  
import android.Manifest.permission.ACCESS_FINE_LOCATION  
import android.widget.Toast  
import com.google.android.gms.location.FusedLocationProviderClient  
import com.google.android.gms.location.LocationServices  
  
class MainActivity : AppCompatActivity()  {  
  
  
  
    lateinit var button_location: Button  
    lateinit var text_location:TextView  
    lateinit var fusedLocationProviderClient:FusedLocationProviderClient  
  
    override fun onCreate(savedInstanceState: Bundle?) {  
        super.onCreate(savedInstanceState)  
        enableEdgeToEdge()  
        setContentView(R.layout.activity_main)  
  
  
        fusedLocationProviderClient=LocationServices.getFusedLocationProviderClient(this)  
        button_location = findViewById<Button>(R.id.button_getLocation)  
        text_location=findViewById<TextView>(R.id.text_location)  
  
        button_location.setOnClickListener{  
            getUserLocation()  
  
  
        }  
  
  
        ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->  
            val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())  
            v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)  
            insets  
        }  
    }  
  
    private fun getUserLocation() {  
        if (ContextCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {  
            ActivityCompat.requestPermissions(this, arrayOf(android.Manifest.permission.ACCESS_FINE_LOCATION), 100)  
            Toast.makeText(this,"未获取权限",Toast.LENGTH_LONG).show()  
                return  
        }  
  
        val location=fusedLocationProviderClient.lastLocation  
        location.addOnSuccessListener {  
            if(it!=null)  
            {  
                val latitude = it.latitude.toString()  
                val longitude = it.longitude.toString()  
                text_location.text = "Latitude: $latitude\nLongitude:$longitude"  
            }  
        }  
        location.addOnFailureListener {exception ->  
  
            text_location.text=exception.localizedMessage  
  
            Toast.makeText(this, "获取位置信息失败: ${exception.localizedMessage}", Toast.LENGTH_LONG).show()  
        }  
  
    }  
  
  
}
  • 法2
package com.example.pathrecorderapp  
  
import android.Manifest  
import android.content.pm.PackageManager  
import android.os.Bundle  
import android.widget.Button  
import android.widget.TextView  
import androidx.activity.enableEdgeToEdge  
import androidx.appcompat.app.AppCompatActivity  
import androidx.core.app.ActivityCompat  
import androidx.core.content.ContextCompat  
import androidx.core.view.ViewCompat  
import androidx.core.view.WindowInsetsCompat  
import android.Manifest.permission.ACCESS_FINE_LOCATION  
import android.content.Context  
import android.location.Address  
import android.location.Geocoder  
import android.location.Location  
import android.location.LocationListener  
import android.location.LocationManager  
import android.widget.Toast  
import com.google.android.gms.location.FusedLocationProviderClient  
import com.google.android.gms.location.LocationServices  
import java.io.IOException  
import java.util.Locale  
  
class MainActivity : AppCompatActivity(),LocationListener  {  
  
  
  
    lateinit var button_location: Button  
    lateinit var text_location:TextView  
    lateinit var location_mannager:LocationManager  
  
    override fun onCreate(savedInstanceState: Bundle?) {  
        super.onCreate(savedInstanceState)  
        enableEdgeToEdge()  
        setContentView(R.layout.activity_main)  
  
  
        button_location = findViewById<Button>(R.id.button_getLocation)  
        text_location=findViewById<TextView>(R.id.text_location)  
  
        button_location.setOnClickListener{  
  
            getUserLocation()  
        }  
  
  
        ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->  
            val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())  
            v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)  
            insets  
        }  
    }  
    private fun getUserLocation()  
    {  
        location_mannager=getSystemService(Context.LOCATION_SERVICE) as LocationManager  
        if (ActivityCompat.checkSelfPermission(  
                this,  
                ACCESS_FINE_LOCATION  
            ) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(  
                this,  
                Manifest.permission.ACCESS_COARSE_LOCATION  
            ) != PackageManager.PERMISSION_GRANTED  
        ) {  
            Toast.makeText(this,"Permission denied", Toast.LENGTH_SHORT).show()  
            ActivityCompat.requestPermissions(  
                this,  
                arrayOf(Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION),100  
            )  
            return  
        }  
        location_mannager.requestLocationUpdates(LocationManager.GPS_PROVIDER,5000,5f,this)  
    }  
  
    override fun onLocationChanged(p0: Location) {  
        val geocoder = Geocoder(this, Locale.getDefault())  
        val addresses: List<Address>?  
        try {  
            addresses = geocoder.getFromLocation(p0.latitude, p0.longitude, 1)  
            if (addresses != null && addresses.isNotEmpty()) {  
                val address = addresses[0]  
                val streetAddress = address.getAddressLine(0) ?: "No street address"  
                val country = address.countryName ?: "No country found"  
                val addressText = if (streetAddress.isNotBlank() && country.isNotBlank()) {  
                    "$streetAddress,$country"  
                } else {  
                    "No detailed address available"  
                }  
                val latitudeText = "Latitude: ${p0.latitude.toString()}"  
                val longitudeText = "Longitude: ${p0.longitude.toString()}"  
                text_location.text = "$addressText\n$latitudeText\n$longitudeText"  
                Toast.makeText(this, "$addressText\n$latitudeText\n$longitudeText", Toast.LENGTH_SHORT).show()  
            }  
        } catch (e: IOException) {  
            e.printStackTrace()  
            text_location.text = "Geocoder error"  
            Toast.makeText(this, "Geocoder error", Toast.LENGTH_SHORT).show()  
        }  
    }  
  
  
}

总结

  • 本节介绍了如何使用Andriod Studio进行界面设计,并使用了两种方式进行位置获取,并介绍 了APK的导出
  • 如有错误,欢迎指出,下一节我们看看如何进行自动运行和数据自动保存画图

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2024年SRM管理系统盘点合集,助力企业选型!

本文将盘点六款主流的SRM管理系统&#xff0c;助力企业选型&#xff01; 想象一下这样一个场景&#xff0c;企业的采购部门每天都在为寻找合适的供应商、管理采购订单以及确保物资及时供应而忙碌。如果没有一个有效的 SRM 管理系统&#xff0c;就如同在黑暗中摸索&#xff0c;效…

Transforms的常见用法

文章目录 一、封装函数与普通函数的用法区别二、Image.open()打开图片的格式三、ToTensor打开图片格式四、ToTensor使用五、Normalize归一化使用六、Resize的使用七、Compose - Resize 使用八、RandomCrop&#xff08;&#xff09; 随机裁剪用法 一、封装函数与普通函数的用法区…

Android Camera系列(二):TextureView+Camera

两岸猿声啼不住&#xff0c;轻舟已过万重山—李白 Android Camera系列&#xff08;一&#xff09;&#xff1a;SurfaceViewCamera Android Camera系列&#xff08;二&#xff09;&#xff1a;TextureViewCamera Android Camera系列&#xff08;三&#xff09;&#xff1a;GLS…

2024霸王餐小程序cps,h5公众号小程序开源版系统搭建开发,外卖霸王餐小程序系统源码

目录 前言&#xff1a; 一、霸王餐小程序的操作是怎么样的&#xff1f; 二、霸王餐系统后台 三、怎么搭建部署? 前言&#xff1a; 霸王餐项目基于美团和饿了么平台开发的小程序。 一、霸王餐小程序的操作是怎么样的&#xff1f; 1、进入小程序后选择自己要下单的店铺&am…

MongoDB 向 PostgreSQL 宣战

上周 MongoDB 发布了一份亮眼的季度财报&#xff0c;盘后股价涨幅超过 18%。 值得一提的是&#xff0c;MongoDB 的 CEO Dev Ittycheria 特别提到 MongoDB 正在借助自己的数据库服务 Atlas 从 PostgreSQL 那里挖角。原话是举了一个博彩网站的例子&#xff1a; “Initially, th…

Hvv结束了,裁员提上日程

《Java代码审计》http://mp.weixin.qq.com/s?__bizMzkwNjY1Mzc0Nw&mid2247484219&idx1&sn73564e316a4c9794019f15dd6b3ba9f6&chksmc0e47a67f793f371e9f6a4fbc06e7929cb1480b7320fae34c32563307df3a28aca49d1a4addd&scene21#wechat_redirect Hvv陆陆续续结…

反常识!科研巨头扎堆夕阳行业?A股研发之王是它?

这是邢不行第 119 期量化小讲堂的分享 作者 | 邢不行 2023年华为研发费用再创新高&#xff0c;高达1600亿。 多年高研发投入让华为在一众领域遥遥领先。 研发费用占全年收入23% 遍观全球&#xff0c;各行各业巨头也极为重视研发。 2022年全球研发投入排名 细数它们的成功史…

严管下快速通道何在?

首先我们要清楚什么是快速通道&#xff1f; 其实就是一句话&#xff0c;是券商为高净值客户提供的一种特殊交易通道。可以提高你的交易速度&#xff0c;但是这里面又细分了很多。 但是VIP通道也就是快速交易通道其实里面还细分了很多种种类的&#xff0c;我们简单区分下&#x…

JavaEE:多线程进阶(CAS)

文章目录 CAS什么是 CASCAS 伪代码 CAS有哪些应用CAS的ABA问题什么是ABA问题ABA问题带来的BUG解决方案 CAS 什么是 CAS CAS: 全称Compare and swap&#xff0c;字面意思:”比较并交换“&#xff0c;一个 CAS 涉及到以下操作: 我们假设内存中的原数据V&#xff0c;旧的预期值A…

【Python报错已解决】`Provisional headers are shown Learn more`

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 文章目录 引言&#xff1a;一、问题描述&#xff1a;1.1 报错示例&#xff1a;1.2 报错分析&#xff1a;1.3 解决思路&#xff…

软件测试面试如何正确谈薪

又是一波离职高峰&#xff0c;很多小伙伴已经开始投身跳槽的准备中了。大家选择跳槽无非是想增加自己的工资收入&#xff0c;所以面试过程中的谈薪环节就显得尤为重要&#xff0c;谈的好与不好&#xff0c;未来整个的薪资水平都可能受影响。 那面试中&#xff0c;当问到“你的…

SprinBoot+Vue二手回收微信小程序的设计与实现

目录 1 项目介绍2 项目截图3 核心代码3.1 Controller3.2 Service3.3 Dao3.4 application.yml3.5 SpringbootApplication3.5 Vue3.6 uniapp代码 4 数据库表设计5 文档参考6 计算机毕设选题推荐7 源码获取 1 项目介绍 博主个人介绍&#xff1a;CSDN认证博客专家&#xff0c;CSDN平…

【C++】vector的简单模拟实现

目录 一、vector的基本实现机制&#xff1a; 二、vector的部分接口模拟实现&#xff1a; 1、构造与析构&#xff1a; 1、普通构造&#xff1a; 2、拷贝构造&#xff1a; 3、析构函数&#xff1a; 2、关于扩容&#xff1a; 1、reserve&#xff1a; 2、resize 3、增删查…

SpringCloud开发实战(六):Feign的最佳实践

目录 SpringCloud开发实战&#xff08;一&#xff09;&#xff1a;搭建SpringCloud框架 SpringCloud开发实战&#xff08;二&#xff09;&#xff1a;通过RestTemplate实现远程调用 SpringCloud开发实战&#xff08;三&#xff09;&#xff1a;集成Eureka注册中心 SpringCloud开…

基于SpringBoot的高校BBS在线互动论坛系统

&#x1f4a5;&#x1f4a5;源码和论文下载&#x1f4a5;&#x1f4a5;&#xff1a;基于SpringBoot的高校BBS在线互动论坛系统-源码论文报告数据库.rar 1. 系统介绍 本论文设计并实现了一个基于Spring Boot和Vue的校园论坛系统&#xff0c;该系统分为用户和管理员两个角色。用户…

9/4 链表-力扣 234、19

234.回文链表 给你一个单链表的头节点 head &#xff0c;请你判断该链表是否为回文链表&#xff1b;如果是&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 输入&#xff1a;head [1,2,2,1] 输出&#xff1a;true 思考&#xff1a;链表遍历只能从前往后&a…

Android studio 更换下载的gradle

首先我们下载gradle 打个比方如果我们下载了一个github上的项目&#xff0c;而它使用的是gradle-6.5-bin.zip https://services.gradle.org/distributions/gradle-6.5-bin.zip 用浏览器去下载&#xff0c;可能需要翻墙 解压到电脑里 找到setting里的这一项&#xff0c;设置…

plc1200 weiluntong001

快接口 快代码 main代码 电脑IP地址 编译&#xff0c;启动仿真&#xff0c;下载到仿真PLCsim 必要时候可以设备离线。 打开并监视块。 打开netto plcsim 添加 本机IP&#xff0c;选择&#xff0c;双击。 PLC启动仿真之后&#xff0c;出现这个IP地址&#xff0…

88、k8s之pv+pvc

一、pv和pvc pv pv&#xff1a;Persistent volume 是k8s虚拟化的存储资源&#xff0c;实际上就是存储&#xff0c;例如本地的硬盘&#xff0c;网络文件系统&#xff08;nfs&#xff09; lvm RAID oss&#xff08;ceph&#xff09; 云存储。 pvc pvc&#xff1a;Persisten…

关于SPI通信失败的一种情况(CRC校验不匹配的问题)

问题 该项目中&#xff0c;使用外置的ADC芯片采集电压电流&#xff0c;主控MCU通过SPI与ADC芯片通信。调试时&#xff0c;SPI通信一直失败&#xff0c;与之前成功的项目对比&#xff0c;发现是SPI配置的问题。 void MX_SPI2_Init(void) {/* USER CODE BEGIN SPI2_Init 0 *//*…