You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

需求实现:带跑马灯效果的TextView显示用户实时地址及时间(附定位代码)

我来帮你完整实现这个需求,分为带跑马灯的TextView布局和实时定位+地址解析逻辑两部分,直接上可运行的代码:


1. 实现带跑马灯效果的TextView

首先补全你的XML布局,跑马灯效果需要特定属性才能生效,以下是完整的布局文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:padding="16dp">

    <TextView
        android:id="@+id/marquee_location_tv"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="正在获取位置信息..."
        android:textSize="16sp"
        android:textColor="#333333"
        <!-- 跑马灯核心属性 -->
        android:ellipsize="marquee"
        android:marqueeRepeatLimit="marquee_forever"
        android:focusable="true"
        android:focusableInTouchMode="true"
        android:singleLine="true"
        android:paddingVertical="8dp"/>

</LinearLayout>

属性说明:

  • android:ellipsize="marquee":指定文本溢出时触发跑马灯滚动
  • android:marqueeRepeatLimit="marquee_forever":设置无限循环滚动
  • android:focusable="true" + android:focusableInTouchMode="true":确保TextView能获取焦点(跑马灯的前提)
  • android:singleLine="true":跑马灯仅支持单行文本

2. 实时定位与地址解析逻辑

使用Google推荐的FusedLocationProviderClient实现高准确率的定位,同时结合Geocoder解析经纬度为具体地址,还要记录停留时间并更新TextView。

第一步:添加权限到AndroidManifest.xml

<!-- 前台定位权限 -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<!-- 如需后台定位,添加以下权限 -->
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />

第二步:Activity完整代码(Kotlin示例)

import android.Manifest
import android.content.Context
import android.content.pm.PackageManager
import android.location.Address
import android.location.Geocoder
import android.location.Location
import android.os.Bundle
import android.os.Looper
import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import com.google.android.gms.location.FusedLocationProviderClient
import com.google.android.gms.location.LocationCallback
import com.google.android.gms.location.LocationRequest
import com.google.android.gms.location.LocationResult
import com.google.android.gms.location.LocationServices
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.IOException
import java.util.Locale

class MainActivity : AppCompatActivity() {
    private lateinit var fusedLocationClient: FusedLocationProviderClient
    private lateinit var locationRequest: LocationRequest
    private var locationCallback: LocationCallback? = null
    private var lastLocationTimestamp: Long = 0L
    private val REQUEST_LOCATION_PERMISSION = 1001

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

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

        // 检查权限并启动定位
        checkLocationPermission()
    }

    // 配置定位请求参数
    private fun setupLocationRequest() {
        locationRequest = LocationRequest.create().apply {
            interval = 10000 // 每10秒更新一次位置
            fastestInterval = 5000 // 最快5秒更新一次
            priority = LocationRequest.PRIORITY_HIGH_ACCURACY // 高准确率模式
        }
    }

    // 定位回调:获取到位置后处理
    private fun setupLocationCallback() {
        locationCallback = object : LocationCallback() {
            override fun onLocationResult(locationResult: LocationResult) {
                super.onLocationResult(locationResult)
                locationResult.lastLocation?.let { location ->
                    lastLocationTimestamp = System.currentTimeMillis()
                    // 解析地址并更新UI
                    updateLocationAddress(location)
                }
            }
        }
    }

    // 检查并申请定位权限
    private fun checkLocationPermission() {
        when {
            ContextCompat.checkSelfPermission(
                this,
                Manifest.permission.ACCESS_FINE_LOCATION
            ) == PackageManager.PERMISSION_GRANTED -> {
                // 权限已授予,启动定位
                startLocationUpdates()
            }
            else -> {
                // 动态申请权限
                ActivityCompat.requestPermissions(
                    this,
                    arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
                    REQUEST_LOCATION_PERMISSION
                )
            }
        }
    }

    // 权限申请结果回调
    override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<out String>,
        grantResults: IntArray
    ) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        if (requestCode == REQUEST_LOCATION_PERMISSION) {
            if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                startLocationUpdates()
            } else {
                Toast.makeText(this, "需要定位权限才能获取位置信息", Toast.LENGTH_SHORT).show()
            }
        }
    }

    // 启动位置更新
    private fun startLocationUpdates() {
        try {
            fusedLocationClient.requestLocationUpdates(
                locationRequest,
                locationCallback!!,
                Looper.getMainLooper()
            )
        } catch (e: SecurityException) {
            e.printStackTrace()
        }
    }

    // 解析经纬度为地址,并更新TextView
    private fun updateLocationAddress(location: Location) {
        val geocoder = Geocoder(this, Locale.getDefault())
        CoroutineScope(Dispatchers.IO).launch {
            try {
                // 获取第一个匹配的地址
                val addresses: List<Address>? = geocoder.getFromLocation(
                    location.latitude,
                    location.longitude,
                    1
                )
                addresses?.firstOrNull()?.let { address ->
                    // 拼接显示文本:地址 + 停留时间
                    val displayText = buildString {
                        append("当前位置:${address.getAddressLine(0)}")
                        append(" | 停留时间:${formatElapsedTime(System.currentTimeMillis() - lastLocationTimestamp)}")
                    }
                    // 回到主线程更新UI
                    withContext(Dispatchers.Main) {
                        val marqueeTv = findViewById<TextView>(R.id.marquee_location_tv)
                        marqueeTv.text = displayText
                        // 确保TextView获取焦点,触发跑马灯
                        marqueeTv.isFocusable = true
                        marqueeTv.isFocusableInTouchMode = true
                        marqueeTv.requestFocus()
                    }
                }
            } catch (e: IOException) {
                e.printStackTrace()
                withContext(Dispatchers.Main) {
                    Toast.makeText(this@MainActivity, "地址解析失败,显示经纬度:${location.latitude}, ${location.longitude}", Toast.LENGTH_SHORT).show()
                }
            }
        }
    }

    // 格式化停留时间为HH:mm:ss
    private fun formatElapsedTime(elapsedMs: Long): String {
        val seconds = (elapsedMs / 1000).toInt() % 60
        val minutes = (elapsedMs / (1000 * 60)).toInt() % 60
        val hours = (elapsedMs / (1000 * 60 * 60)).toInt()
        return String.format("%02d:%02d:%02d", hours, minutes, seconds)
    }

    // 停止定位更新,避免内存泄漏
    override fun onDestroy() {
        super.onDestroy()
        locationCallback?.let {
            fusedLocationClient.removeLocationUpdates(it)
        }
    }
}

注意事项

  1. 跑马灯触发问题:如果布局中有其他可焦点控件,可能会抢走TextView的焦点,导致跑马灯不生效。可以自定义一个始终返回焦点的TextView:
class AlwaysFocusedTextView(context: Context, attrs: AttributeSet?) : TextView(context, attrs) {
    override fun isFocused(): Boolean {
        return true
    }
}

然后在XML中替换原TextView即可。

  1. Geocoder兼容性:部分设备或地区可能无法使用Geocoder解析地址,建议在异常处理中显示经纬度作为备选。

  2. 后台定位:如果需要App在后台也能更新位置,除了添加ACCESS_BACKGROUND_LOCATION权限,还需要在系统设置中允许App的后台定位权限。

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 08:30:49