增加位置实时显示窗口
This commit is contained in:
		| @@ -46,6 +46,9 @@ android { | |||||||
| dependencies { | dependencies { | ||||||
|     api fileTree(dir: 'libs', include: ['*.jar']) |     api fileTree(dir: 'libs', include: ['*.jar']) | ||||||
| 	 | 	 | ||||||
|  | 	// 谷歌定位服务核心依赖(FusedLocationProviderClient所在库) | ||||||
|  |     api 'com.google.android.gms:play-services-location:21.0.1' | ||||||
|  |      | ||||||
|     // SSH |     // SSH | ||||||
|     api 'com.jcraft:jsch:0.1.55' |     api 'com.jcraft:jsch:0.1.55' | ||||||
|     // Html 解析 |     // Html 解析 | ||||||
|   | |||||||
| @@ -1,8 +1,8 @@ | |||||||
| #Created by .winboll/winboll_app_build.gradle | #Created by .winboll/winboll_app_build.gradle | ||||||
| #Mon Sep 29 18:02:11 HKT 2025 | #Mon Sep 29 10:40:51 GMT 2025 | ||||||
| stageCount=2 | stageCount=2 | ||||||
| libraryProject= | libraryProject= | ||||||
| baseVersion=15.0 | baseVersion=15.0 | ||||||
| publishVersion=15.0.1 | publishVersion=15.0.1 | ||||||
| buildCount=0 | buildCount=2 | ||||||
| baseBetaVersion=15.0.2 | baseBetaVersion=15.0.2 | ||||||
|   | |||||||
| @@ -3,6 +3,19 @@ | |||||||
|     xmlns:android="http://schemas.android.com/apk/res/android" |     xmlns:android="http://schemas.android.com/apk/res/android" | ||||||
|     package="cc.winboll.studio.positions"> |     package="cc.winboll.studio.positions"> | ||||||
| 	 | 	 | ||||||
|  | 	<!-- 1. 定位必需权限声明 --> | ||||||
|  |     <!-- 精确定位权限(GPS+网络定位,核心) --> | ||||||
|  |     <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> | ||||||
|  |     <!-- 粗略定位权限(仅安卓12+需要,兼容低版本可加) --> | ||||||
|  |     <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> | ||||||
|  |     <!-- 网络权限(可选,用于网络定位,提升室内定位精度) --> | ||||||
|  |     <uses-permission android:name="android.permission.INTERNET" /> | ||||||
|  |  | ||||||
|  |     <!-- 2. 声明定位硬件支持(可选,告诉系统应用需要定位功能) --> | ||||||
|  |     <uses-feature | ||||||
|  |         android:name="android.hardware.location.gps" | ||||||
|  |         android:required="false" /> <!-- false=无GPS也能使用(网络定位) --> | ||||||
|  | 		 | ||||||
|     <application |     <application | ||||||
|         android:allowBackup="true" |         android:allowBackup="true" | ||||||
|         android:icon="@drawable/ic_launcher" |         android:icon="@drawable/ic_launcher" | ||||||
| @@ -31,6 +44,12 @@ | |||||||
|  |  | ||||||
|         <activity android:name=".GlobalApplication$CrashActivity"/> |         <activity android:name=".GlobalApplication$CrashActivity"/> | ||||||
|  |  | ||||||
|  |         <activity android:name="cc.winboll.studio.positions.activities.LocationActivity"/> | ||||||
|  | 			 | ||||||
|  | 		<!-- 4. 谷歌定位服务版本声明(避免版本兼容问题) --> | ||||||
|  |         <meta-data | ||||||
|  |             android:name="com.google.android.gms.version" | ||||||
|  |             android:value="@integer/google_play_services_version" /> | ||||||
|     </application> |     </application> | ||||||
|  |  | ||||||
| </manifest> | </manifest> | ||||||
|   | |||||||
| @@ -5,6 +5,9 @@ import androidx.appcompat.app.AppCompatActivity; | |||||||
| import androidx.appcompat.widget.Toolbar; | import androidx.appcompat.widget.Toolbar; | ||||||
| import cc.winboll.studio.libappbase.LogView; | import cc.winboll.studio.libappbase.LogView; | ||||||
| import com.hjq.toast.ToastUtils; | import com.hjq.toast.ToastUtils; | ||||||
|  | import android.view.View; | ||||||
|  | import cc.winboll.studio.positions.activities.LocationActivity; | ||||||
|  | import android.content.Intent; | ||||||
|  |  | ||||||
| public class MainActivity extends AppCompatActivity { | public class MainActivity extends AppCompatActivity { | ||||||
|  |  | ||||||
| @@ -28,4 +31,8 @@ public class MainActivity extends AppCompatActivity { | |||||||
|         super.onResume(); |         super.onResume(); | ||||||
|         mLogView.start(); |         mLogView.start(); | ||||||
|     } |     } | ||||||
|  | 	 | ||||||
|  | 	public void onPositions(View view) { | ||||||
|  | 		startActivity(new Intent(this, LocationActivity.class)); | ||||||
|  | 	} | ||||||
| } | } | ||||||
|   | |||||||
| @@ -0,0 +1,219 @@ | |||||||
|  | package cc.winboll.studio.positions.activities; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * @Author ZhanGSKen&豆包大模型<zhangsken@qq.com> | ||||||
|  |  * @Date 2025/09/29 18:22 | ||||||
|  |  * @Describe 当前位置实时显示 | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  |  | ||||||
|  | import android.Manifest; | ||||||
|  | import android.content.pm.PackageManager; | ||||||
|  | import android.location.Location; | ||||||
|  | import android.os.Bundle; | ||||||
|  | import android.widget.TextView; | ||||||
|  | import android.widget.Toast; | ||||||
|  | import androidx.annotation.NonNull; | ||||||
|  | import androidx.appcompat.app.AppCompatActivity; | ||||||
|  | import androidx.core.app.ActivityCompat; | ||||||
|  | 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 com.google.android.gms.tasks.OnSuccessListener; | ||||||
|  | import cc.winboll.studio.positions.R; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * 实时定位活动窗口: | ||||||
|  |  * 1. 申请定位必需权限(精确定位+粗略定位) | ||||||
|  |  * 2. 初始化FusedLocationProviderClient(谷歌官方定位服务,兼容所有安卓版本) | ||||||
|  |  * 3. 实时监听位置变化,更新显示经度、纬度 | ||||||
|  |  */ | ||||||
|  | public class LocationActivity extends AppCompatActivity { | ||||||
|  | 	 | ||||||
|  | 	public static final String TAG = "LocationActivity"; | ||||||
|  | 	 | ||||||
|  | 	// 1. 核心组件与常量定义 | ||||||
|  | 	private static final int REQUEST_LOCATION_PERMISSIONS = 1004; // 定位权限请求码 | ||||||
|  | 	private FusedLocationProviderClient fusedLocationClient; // 定位核心客户端 | ||||||
|  | 	private LocationCallback locationCallback; // 位置变化监听器 | ||||||
|  | 	private LocationRequest locationRequest; // 定位请求配置(频率、精度等) | ||||||
|  |  | ||||||
|  | 	// UI控件:用于显示经纬度(需在布局中定义对应ID) | ||||||
|  | 	private TextView tvLongitude; // 经度显示 | ||||||
|  | 	private TextView tvLatitude;  // 纬度显示 | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 	@Override | ||||||
|  | 	protected void onCreate(Bundle savedInstanceState) { | ||||||
|  | 		super.onCreate(savedInstanceState); | ||||||
|  | 		// 2. 加载布局(需手动创建对应布局文件,见下方说明) | ||||||
|  | 		setContentView(R.layout.activity_location); | ||||||
|  |  | ||||||
|  | 		// 3. 绑定UI控件(与布局文件中的TextView ID对应) | ||||||
|  | 		tvLongitude = findViewById(R.id.tv_longitude); | ||||||
|  | 		tvLatitude = findViewById(R.id.tv_latitude); | ||||||
|  |  | ||||||
|  | 		// 4. 初始化定位相关组件 | ||||||
|  | 		initLocationConfig(); | ||||||
|  |  | ||||||
|  | 		// 5. 检查并申请定位权限(无权限则申请,有权限则直接启动定位) | ||||||
|  | 		if (checkLocationPermissions()) { | ||||||
|  | 			startRealTimeLocation(); // 权限已授予 → 启动实时定位 | ||||||
|  | 		} else { | ||||||
|  | 			requestLocationPermissions(); // 权限未授予 → 申请权限 | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 	/** | ||||||
|  | 	 * 初始化定位配置:设置定位精度、更新频率等 | ||||||
|  | 	 */ | ||||||
|  | 	private void initLocationConfig() { | ||||||
|  | 		// 初始化定位客户端(谷歌官方推荐,替代旧的LocationManager) | ||||||
|  | 		fusedLocationClient = LocationServices.getFusedLocationProviderClient(this); | ||||||
|  |  | ||||||
|  | 		// 配置定位请求:实时更新(1秒请求一次,最小间隔500毫秒,最高精度) | ||||||
|  | 		locationRequest = new LocationRequest.Builder(1000) // 定位更新间隔(毫秒) | ||||||
|  | 			.setMinUpdateIntervalMillis(500) // 最小更新间隔(避免频繁更新耗电) | ||||||
|  | 			.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY) // 高精度定位(优先GPS) | ||||||
|  | 			.build(); | ||||||
|  |  | ||||||
|  | 		// 初始化位置变化监听器:位置更新时触发(实时更新UI) | ||||||
|  | 		locationCallback = new LocationCallback() { | ||||||
|  | 			@Override | ||||||
|  | 			public void onLocationResult(@NonNull LocationResult locationResult) { | ||||||
|  | 				super.onLocationResult(locationResult); | ||||||
|  | 				// 获取最新位置信息(locationResult包含最近一次或多次位置) | ||||||
|  | 				Location latestLocation = locationResult.getLastLocation(); | ||||||
|  | 				if (latestLocation != null) { | ||||||
|  | 					// 6. 提取经纬度并更新UI(实时显示) | ||||||
|  | 					double longitude = latestLocation.getLongitude(); // 经度(东经为正,西经为负) | ||||||
|  | 					double latitude = latestLocation.getLatitude();   // 纬度(北纬为正,南纬为负) | ||||||
|  |  | ||||||
|  | 					// 更新TextView显示(保留6位小数,精度足够日常使用) | ||||||
|  | 					tvLongitude.setText(String.format("当前经度:%.6f", longitude)); | ||||||
|  | 					tvLatitude.setText(String.format("当前纬度:%.6f", latitude)); | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		}; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 	/** | ||||||
|  | 	 * 检查定位必需权限是否已授予(精确定位+粗略定位,覆盖所有安卓版本) | ||||||
|  | 	 */ | ||||||
|  | 	private boolean checkLocationPermissions() { | ||||||
|  | 		// 安卓12+(API31+)新增精确定位权限,需同时检查2个权限;低版本只需检查1个 | ||||||
|  | 		/*if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) { | ||||||
|  | 			return ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED | ||||||
|  | 				&& ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED; | ||||||
|  | 		} else {*/ | ||||||
|  | 			return ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED; | ||||||
|  | 		//} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 	/** | ||||||
|  | 	 * 申请定位必需权限(弹系统授权弹窗) | ||||||
|  | 	 */ | ||||||
|  | 	private void requestLocationPermissions() { | ||||||
|  | 		/*if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) { | ||||||
|  | 			// 安卓12+:同时申请精确定位+粗略定位 | ||||||
|  | 			ActivityCompat.requestPermissions( | ||||||
|  | 				this, | ||||||
|  | 				new String[]{Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION}, | ||||||
|  | 				REQUEST_LOCATION_PERMISSIONS | ||||||
|  | 			); | ||||||
|  | 		} else {*/ | ||||||
|  | 			// 安卓12以下:仅申请精确定位(包含粗略定位能力) | ||||||
|  | 			ActivityCompat.requestPermissions( | ||||||
|  | 				this, | ||||||
|  | 				new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, | ||||||
|  | 				REQUEST_LOCATION_PERMISSIONS | ||||||
|  | 			); | ||||||
|  | 		//} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 	/** | ||||||
|  | 	 * 启动实时定位:注册位置监听器,开始接收位置更新 | ||||||
|  | 	 */ | ||||||
|  | 	private void startRealTimeLocation() { | ||||||
|  | 		// 权限兜底检查(避免异常) | ||||||
|  | 		if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { | ||||||
|  | 			Toast.makeText(this, "定位权限未授予,无法启动定位", Toast.LENGTH_SHORT).show(); | ||||||
|  | 			return; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// 1. 先获取一次当前位置(快速显示初始经纬度) | ||||||
|  | 		fusedLocationClient.getLastLocation() | ||||||
|  | 			.addOnSuccessListener(this, new OnSuccessListener<Location>() { | ||||||
|  | 				@Override | ||||||
|  | 				public void onSuccess(Location location) { | ||||||
|  | 					if (location != null) { | ||||||
|  | 						// 显示初始经纬度 | ||||||
|  | 						tvLongitude.setText(String.format("当前经度:%.6f", location.getLongitude())); | ||||||
|  | 						tvLatitude.setText(String.format("当前纬度:%.6f", location.getLatitude())); | ||||||
|  | 					} else { | ||||||
|  | 						// 无历史位置(如首次启动),提示“等待定位更新” | ||||||
|  | 						tvLongitude.setText("当前经度:等待更新..."); | ||||||
|  | 						tvLatitude.setText("当前纬度:等待更新..."); | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 			}); | ||||||
|  |  | ||||||
|  | 		// 2. 注册监听器,接收实时位置更新(持续监听) | ||||||
|  | 		fusedLocationClient.requestLocationUpdates( | ||||||
|  | 			locationRequest, | ||||||
|  | 			locationCallback, | ||||||
|  | 			getMainLooper() // 在主线程更新UI(避免线程异常) | ||||||
|  | 		); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 	/** | ||||||
|  | 	 * 处理权限申请结果(用户同意/拒绝后触发) | ||||||
|  | 	 */ | ||||||
|  | 	@Override | ||||||
|  | 	public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { | ||||||
|  | 		super.onRequestPermissionsResult(requestCode, permissions, grantResults); | ||||||
|  | 		if (requestCode == REQUEST_LOCATION_PERMISSIONS) { | ||||||
|  | 			// 检查是否所有必需权限都已授予 | ||||||
|  | 			boolean allGranted = true; | ||||||
|  | 			for (int result : grantResults) { | ||||||
|  | 				if (result != PackageManager.PERMISSION_GRANTED) { | ||||||
|  | 					allGranted = false; | ||||||
|  | 					break; | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			if (allGranted) { | ||||||
|  | 				// 权限同意 → 启动实时定位 | ||||||
|  | 				startRealTimeLocation(); | ||||||
|  | 			} else { | ||||||
|  | 				// 权限拒绝 → 提示用户(无法定位) | ||||||
|  | 				Toast.makeText(this, "定位权限被拒绝,无法显示位置信息", Toast.LENGTH_SHORT).show(); | ||||||
|  | 				tvLongitude.setText("当前经度:无权限"); | ||||||
|  | 				tvLatitude.setText("当前纬度:无权限"); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 	/** | ||||||
|  | 	 * 活动销毁时:停止定位监听(避免内存泄漏、减少耗电) | ||||||
|  | 	 */ | ||||||
|  | 	@Override | ||||||
|  | 	protected void onDestroy() { | ||||||
|  | 		super.onDestroy(); | ||||||
|  | 		// 移除定位监听器(核心:防止Activity销毁后仍在监听,导致内存泄漏) | ||||||
|  | 		if (fusedLocationClient != null && locationCallback != null) { | ||||||
|  | 			fusedLocationClient.removeLocationUpdates(locationCallback); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
							
								
								
									
										34
									
								
								positions/src/main/res/layout/activity_location.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								positions/src/main/res/layout/activity_location.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | |||||||
|  | <?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_horizontal|center_vertical" | ||||||
|  |     android:orientation="vertical" | ||||||
|  |     android:padding="20dp"> | ||||||
|  |  | ||||||
|  |     <!-- 标题 --> | ||||||
|  |     <TextView | ||||||
|  |         android:layout_width="wrap_content" | ||||||
|  |         android:layout_height="wrap_content" | ||||||
|  |         android:text="实时位置信息" | ||||||
|  |         android:textSize="22sp" | ||||||
|  |         android:textStyle="bold"/> | ||||||
|  |  | ||||||
|  |     <!-- 经度显示(大字体,清晰可见) --> | ||||||
|  |     <TextView | ||||||
|  |         android:id="@+id/tv_longitude" | ||||||
|  |         android:layout_width="wrap_content" | ||||||
|  |         android:layout_height="wrap_content" | ||||||
|  |         android:text="当前经度:等待更新..." | ||||||
|  |         android:textSize="18sp"/> | ||||||
|  |  | ||||||
|  |     <!-- 纬度显示(大字体,清晰可见) --> | ||||||
|  |     <TextView | ||||||
|  |         android:id="@+id/tv_latitude" | ||||||
|  |         android:layout_width="wrap_content" | ||||||
|  |         android:layout_height="wrap_content" | ||||||
|  |         android:text="当前纬度:等待更新..." | ||||||
|  |         android:textSize="18sp"/> | ||||||
|  |  | ||||||
|  | </LinearLayout> | ||||||
|  |  | ||||||
| @@ -26,11 +26,11 @@ | |||||||
| 		android:layout_weight="1.0" | 		android:layout_weight="1.0" | ||||||
| 		android:gravity="center_vertical|center_horizontal"> | 		android:gravity="center_vertical|center_horizontal"> | ||||||
|  |  | ||||||
| 		<TextView | 		<Button | ||||||
| 			android:layout_width="wrap_content" | 			android:layout_width="wrap_content" | ||||||
| 			android:layout_height="wrap_content" | 			android:layout_height="wrap_content" | ||||||
| 			android:text="Positions" | 			android:text="Positions" | ||||||
| 			android:textAppearance="?android:attr/textAppearanceLarge"/> | 			android:onClick="onPositions"/> | ||||||
|  |  | ||||||
| 	</LinearLayout> | 	</LinearLayout> | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 ZhanGSKen
					ZhanGSKen