You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Android开发:如何从Google Map获取地址信息并自动填充editbox

一步步实现你的Android地图需求(新手友好版)

嗨,作为Android新手能想到这么完整的需求已经很棒啦!我一步步给你拆解实现方法,保证每一步都清晰易懂~

一、先做前置准备:权限与依赖配置

1. 配置AndroidManifest.xml

首先要给App加上必要的权限和Google Maps的配置:

<!-- 定位权限 -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

<!-- Google Maps 必要配置 -->
<uses-feature
    android:glEsVersion="0x00020000"
    android:required="true" />

<application ...>
    <!-- 替换成你自己的Google Maps API密钥 -->
    <meta-data
        android:name="com.google.android.geo.API_KEY"
        android:value="你的API_KEY"/>
        
    <!-- 地图Activity声明 -->
    <activity android:name=".MapActivity" />
</application>

小贴士:API密钥需要去Google Cloud控制台申请,记得启用Maps SDK for Android哦

2. 添加Gradle依赖

在你的app模块的build.gradle(Module级)里添加Maps和定位服务的依赖:

dependencies {
    // Google Maps
    implementation 'com.google.android.gms:play-services-maps:18.1.0'
    // 定位服务
    implementation 'com.google.android.gms:play-services-location:21.0.1'
}

二、主界面(MainActivity)实现

主界面需要一个触发地图的图片,以及用来填充地址的输入框:

1. 布局文件(activity_main.xml)

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="16dp">

    <!-- 点击打开地图的图片 -->
    <ImageView
        android:id="@+id/iv_open_map"
        android:layout_width="match_parent"
        android:layout_height="200dp"
        android:src="@drawable/ic_map_launcher"
        android:clickable="true"
        android:contentDescription="打开地图"/>

    <!-- 地址输入框区域 -->
    <EditText
        android:id="@+id/et_street"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="街道名称"
        android:layout_marginTop="16dp"/>

    <EditText
        android:id="@+id/et_zipcode"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="邮政编码"
        android:layout_marginTop="8dp"/>

    <EditText
        android:id="@+id/et_state"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="州"
        android:layout_marginTop="8dp"/>

    <EditText
        android:id="@+id/et_country"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="国家"
        android:layout_marginTop="8dp"/>

</LinearLayout>

2. MainActivity代码逻辑

主要是点击图片跳转到MapActivity,并且接收返回的地址信息填充输入框:

class MainActivity : AppCompatActivity() {
    private lateinit var etStreet: EditText
    private lateinit var etZipcode: EditText
    private lateinit var etState: EditText
    private lateinit var etCountry: EditText

    // 定义请求码,用于接收MapActivity的返回结果
    private val MAP_REQUEST_CODE = 1001

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        initViews()
        setupMapLauncher()
    }

    private fun initViews() {
        etStreet = findViewById(R.id.et_street)
        etZipcode = findViewById(R.id.et_zipcode)
        etState = findViewById(R.id.et_state)
        etCountry = findViewById(R.id.et_country)
    }

    private fun setupMapLauncher() {
        findViewById<ImageView>(R.id.iv_open_map).setOnClickListener {
            startActivityForResult(Intent(this, MapActivity::class.java), MAP_REQUEST_CODE)
        }
    }

    // 接收MapActivity返回的地址信息
    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        if (requestCode == MAP_REQUEST_CODE && resultCode == RESULT_OK) {
            data?.apply {
                etStreet.setText(getStringExtra("STREET"))
                etZipcode.setText(getStringExtra("ZIPCODE"))
                etState.setText(getStringExtra("STATE"))
                etCountry.setText(getStringExtra("COUNTRY"))
            }
        }
    }
}

三、地图页面(MapActivity)核心实现

这部分是重点,要实现显示当前位置点击地图加标记逆地理编码获取地址三个功能:

1. 布局文件(activity_map.xml)

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <!-- Google Map 容器 -->
    <fragment
        android:id="@+id/map_fragment"
        android:name="com.google.android.gms.maps.SupportMapFragment"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"/>

    <!-- 提交按钮 -->
    <Button
        android:id="@+id/btn_submit"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="确认地址"
        android:layout_margin="16dp"/>

</LinearLayout>

2. MapActivity代码逻辑

class MapActivity : AppCompatActivity(), OnMapReadyCallback {
    private lateinit var mMap: GoogleMap
    private lateinit var fusedLocationClient: FusedLocationProviderClient
    private var selectedMarker: Marker? = null // 保存用户点击的标记

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_map)

        // 初始化定位客户端
        fusedLocationClient = LocationServices.getFusedLocationProviderClient(this)

        // 获取地图实例
        val mapFragment = supportFragmentManager.findFragmentById(R.id.map_fragment) as SupportMapFragment
        mapFragment.getMapAsync(this)

        // 提交按钮点击事件
        findViewById<Button>(R.id.btn_submit).setOnClickListener {
            selectedMarker?.position?.let { latLng ->
                getAddressFromLatLng(latLng)
            } ?: run {
                Toast.makeText(this, "请先在地图上选择位置", Toast.LENGTH_SHORT).show()
            }
        }
    }

    // 地图初始化完成回调
    override fun onMapReady(googleMap: GoogleMap) {
        mMap = googleMap
        // 先请求定位权限,再显示当前位置
        requestLocationPermission()
        // 设置地图点击监听,添加标记
        setupMapClickListener()
    }

    // 动态请求定位权限
    private fun requestLocationPermission() {
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
            != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(
                this,
                arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
                1002
            )
        } else {
            // 权限已授予,显示当前位置
            showCurrentLocation()
        }
    }

    // 显示用户当前位置
    private fun showCurrentLocation() {
        mMap.isMyLocationEnabled = true // 显示我的位置按钮
        fusedLocationClient.lastLocation.addOnSuccessListener { location: Location? ->
            location?.let {
                val currentLatLng = LatLng(it.latitude, it.longitude)
                // 移动地图到当前位置,缩放级别15(数值越大越近)
                mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(currentLatLng, 15f))
            } ?: run {
                Toast.makeText(this, "无法获取当前位置", Toast.LENGTH_SHORT).show()
            }
        }
    }

    // 处理权限请求结果
    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        if (requestCode == 1002) {
            if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                showCurrentLocation()
            } else {
                Toast.makeText(this, "需要定位权限才能显示当前位置", Toast.LENGTH_SHORT).show()
            }
        }
    }

    // 设置地图点击监听,添加标记
    private fun setupMapClickListener() {
        mMap.setOnMapClickListener { latLng ->
            // 移除之前的标记,保证只有一个选中标记
            selectedMarker?.remove()
            // 添加新标记
            selectedMarker = mMap.addMarker(MarkerOptions().position(latLng))
        }
    }

    // 通过经纬度获取地址信息(逆地理编码)
    private fun getAddressFromLatLng(latLng: LatLng) {
        val geocoder = Geocoder(this, Locale.getDefault())
        lifecycleScope.launch {
            try {
                val addresses = geocoder.getFromLocation(latLng.latitude, latLng.longitude, 1)
                addresses?.firstOrNull()?.let { address ->
                    // 提取需要的地址信息
                    val street = address.thoroughfare ?: "" // 街道
                    val zipcode = address.postalCode ?: "" // 邮编
                    val state = address.adminArea ?: "" // 州/省
                    val country = address.countryName ?: "" // 国家

                    // 返回结果给MainActivity
                    val resultIntent = Intent().apply {
                        putExtra("STREET", street)
                        putExtra("ZIPCODE", zipcode)
                        putExtra("STATE", state)
                        putExtra("COUNTRY", country)
                    }
                    setResult(RESULT_OK, resultIntent)
                    finish()
                } ?: run {
                    Toast.makeText(this@MapActivity, "无法获取地址信息", Toast.LENGTH_SHORT).show()
                }
            } catch (e: IOException) {
                Toast.makeText(this@MapActivity, "地址解析失败:${e.message}", Toast.LENGTH_SHORT).show()
            }
        }
    }
}

四、关键细节提示

  • API密钥申请:一定要去Google Cloud控制台创建项目,启用Maps SDK for Android,然后生成API密钥,不然地图会加载失败。
  • 权限处理:如果用户拒绝定位权限,要友好提示,或者提供手动选择位置的备选方案。
  • 逆地理编码异常:Geocoder可能因为网络或地区问题解析失败,一定要加异常捕获。
  • 标记管理:代码里每次点击地图都会移除之前的标记,保证只有一个选中的位置,避免用户混淆。

内容的提问来源于stack exchange,提问作者user8472639

火山引擎 最新活动