diff --git a/positions/build.gradle b/positions/build.gradle
index b7aaa28b..356a27ae 100644
--- a/positions/build.gradle
+++ b/positions/build.gradle
@@ -70,7 +70,7 @@ dependencies {
//api 'androidx.vectordrawable:vectordrawable-animated:1.1.0'
//api 'androidx.fragment:fragment:1.1.0'
- api 'cc.winboll.studio:libaes:15.9.3'
- api 'cc.winboll.studio:libapputils:15.8.5'
- api 'cc.winboll.studio:libappbase:15.9.5'
+ api 'cc.winboll.studio:libaes:15.10.2'
+ api 'cc.winboll.studio:libapputils:15.10.2'
+ api 'cc.winboll.studio:libappbase:15.10.9'
}
diff --git a/positions/build.properties b/positions/build.properties
index 45635f07..b620072f 100644
--- a/positions/build.properties
+++ b/positions/build.properties
@@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle
-#Tue Sep 30 17:50:26 HKT 2025
+#Tue Sep 30 13:02:23 GMT 2025
stageCount=4
libraryProject=
baseVersion=15.0
publishVersion=15.0.3
-buildCount=0
+buildCount=9
baseBetaVersion=15.0.4
diff --git a/positions/src/main/AndroidManifest.xml b/positions/src/main/AndroidManifest.xml
index 65f5e10f..1b9f1800 100644
--- a/positions/src/main/AndroidManifest.xml
+++ b/positions/src/main/AndroidManifest.xml
@@ -2,20 +2,20 @@
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
-
+ android:required="false"/>
+
-
-
+
+ android:value="@integer/google_play_services_version"/>
+
+
+
-
+
\ No newline at end of file
diff --git a/positions/src/main/java/cc/winboll/studio/positions/MainActivity.java b/positions/src/main/java/cc/winboll/studio/positions/MainActivity.java
index 635b8353..8d489c26 100644
--- a/positions/src/main/java/cc/winboll/studio/positions/MainActivity.java
+++ b/positions/src/main/java/cc/winboll/studio/positions/MainActivity.java
@@ -1,17 +1,17 @@
package cc.winboll.studio.positions;
+import android.content.Intent;
import android.os.Bundle;
+import android.view.View;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
+import cc.winboll.studio.libappbase.LogActivity;
import cc.winboll.studio.libappbase.LogView;
-import com.hjq.toast.ToastUtils;
-import android.view.View;
import cc.winboll.studio.positions.activities.LocationActivity;
-import android.content.Intent;
+import com.hjq.toast.ToastUtils;
public class MainActivity extends AppCompatActivity {
- LogView mLogView;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -20,19 +20,13 @@ public class MainActivity extends AppCompatActivity {
Toolbar toolbar=(Toolbar)findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
-
- mLogView = findViewById(R.id.logview);
-
- ToastUtils.show("onCreate");
}
- @Override
- protected void onResume() {
- super.onResume();
- mLogView.start();
- }
-
public void onPositions(View view) {
startActivity(new Intent(this, LocationActivity.class));
}
+
+ public void onLog(View view) {
+ LogActivity.startLogActivity(this);
+ }
}
diff --git a/positions/src/main/java/cc/winboll/studio/positions/activities/LocationActivity.java b/positions/src/main/java/cc/winboll/studio/positions/activities/LocationActivity.java
index ee634381..74cb5619 100644
--- a/positions/src/main/java/cc/winboll/studio/positions/activities/LocationActivity.java
+++ b/positions/src/main/java/cc/winboll/studio/positions/activities/LocationActivity.java
@@ -411,29 +411,28 @@ public class LocationActivity extends AppCompatActivity {
* 活动销毁:停止定位 + 停止距离刷新定时器(避免内存泄漏)
*/
@Override
- protected void onDestroy() {
- super.onDestroy();
+ protected void onDestroy() {
+ super.onDestroy();
- // 1. 停止定位监听(释放定位资源)
- if (fusedLocationClient != null && locationCallback != null) {
- fusedLocationClient.removeLocationUpdates(locationCallback);
- }
+ // 1. 停止定位监听(原逻辑不变)
+ if (fusedLocationClient != null && locationCallback != null) {
+ fusedLocationClient.removeLocationUpdates(locationCallback);
+ }
- // 2. 停止Adapter的距离刷新定时器(关键:避免Timer持有Context导致内存泄漏)
- if (positionAdapter != null) {
- positionAdapter.stopTimer();
- }
-
- // 3. 最后同步一次数据(确保所有修改保存)
- try {
- if (mPositionList != null && !mPositionList.isEmpty()) {
- PositionModel.saveBeanList(this, mPositionList, PositionModel.class);
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
+ // 2. 关键:调用Adapter的stopTimer(内部已实现服务解绑,避免内存泄漏)
+ if (positionAdapter != null) {
+ positionAdapter.stopTimer(); // 此方法已重构为:解绑服务+清理资源
+ }
+ // 3. 最后同步一次数据(原逻辑不变)
+ try {
+ if (mPositionList != null && !mPositionList.isEmpty()) {
+ PositionModel.saveBeanList(this, mPositionList, PositionModel.class);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
/**
* 辅助工具:dp转px(适配不同屏幕分辨率)
diff --git a/positions/src/main/java/cc/winboll/studio/positions/adapters/PositionAdapter.java b/positions/src/main/java/cc/winboll/studio/positions/adapters/PositionAdapter.java
index d0110fa6..986564a1 100644
--- a/positions/src/main/java/cc/winboll/studio/positions/adapters/PositionAdapter.java
+++ b/positions/src/main/java/cc/winboll/studio/positions/adapters/PositionAdapter.java
@@ -3,10 +3,13 @@ package cc.winboll.studio.positions.adapters;
/**
* @Author ZhanGSKen&豆包大模型
* @Date 2025/09/29 20:25
- * @Describe 位置数据适配器(修复定时器:仅刷新实时距离,不干扰编辑中的任务;优化任务触发通知)
+ * @Describe 位置数据适配器(移除定时器,通过绑定服务获取距离更新)
*/
+import android.content.ComponentName;
import android.content.Context;
-import android.os.Handler;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.IBinder;
import android.os.Looper;
import android.view.LayoutInflater;
import android.view.MenuInflater;
@@ -20,44 +23,82 @@ import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.TextView;
import android.widget.Toast;
+
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
+
+import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.positions.R;
import cc.winboll.studio.positions.models.PositionModel;
import cc.winboll.studio.positions.models.PositionTaskModel;
+import cc.winboll.studio.positions.services.DistanceRefreshService;
import cc.winboll.studio.positions.views.PositionTaskListView;
+
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
-import java.util.Timer;
-import java.util.TimerTask;
-import cc.winboll.studio.positions.utils.NotificationUtils;
-public class PositionAdapter extends RecyclerView.Adapter {
- // 常量定义(不变)
+/**
+ * 位置列表适配器:管理位置视图渲染、服务绑定、距离UI更新
+ * Java 7 适配:移除Lambda、Stream,使用匿名内部类+迭代器,明确泛型,删除Java8+API(如forEach)
+ */
+public class PositionAdapter extends RecyclerView.Adapter implements DistanceRefreshService.OnDistanceUpdateReceiver {
public static final String TAG = "PositionAdapter";
+ // 视图类型常量
private static final int VIEW_TYPE_SIMPLE = 1;
private static final int VIEW_TYPE_EDIT = 2;
- private static final long REFRESH_INTERVAL = 5000;
+ // 默认任务参数
private static final String DEFAULT_TASK_DESC = "新位置任务";
private static final int DEFAULT_TASK_DISTANCE = 100;
- // 核心成员变量(新增:1.位置ID→索引映射 2.主线程Handler 3.任务触发标记)
+ // 核心成员变量(Java7:明确泛型声明与初始化)
private final ArrayList mPositionList;
private final ArrayList mAllPositionTasks;
private final Context mContext;
private PositionModel mCurrentGpsPosition;
- private final Timer mDistanceTimer;
- private final Handler mMainHandler;
private final Map> mPositionTaskMap;
- private final Map mVisibleDistanceViews = new HashMap<>();
- // 新增1:存储“位置ID→列表索引”映射(快速定位位置项,避免遍历)
- private final Map mPositionIdToIndexMap = new HashMap<>();
- // 新增2:主线程Handler(确保UI更新在主线程,避免异常)
- private final Handler mUiHandler = new Handler(Looper.getMainLooper());
+ private final Map mVisibleDistanceViews = new HashMap();
+ private final Map mPositionIdToIndexMap = new HashMap();
+ private final android.os.Handler mUiHandler = new android.os.Handler(Looper.getMainLooper());
- // 接口回调(不变)
+ // 服务相关变量
+ private DistanceRefreshService mDistanceService;
+ private boolean isServiceBound = false;
+ // Java7:匿名内部类实现ServiceConnection(替代Lambda)
+ private final ServiceConnection mServiceConnection = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ DistanceRefreshService.DistanceBinder binder = (DistanceRefreshService.DistanceBinder) service;
+ mDistanceService = binder.getService();
+ isServiceBound = true;
+
+ // 绑定服务后初始化:设置接收器+同步数据
+ mDistanceService.setOnDistanceUpdateReceiver(PositionAdapter.this);
+ mDistanceService.syncPositionList(mPositionList);
+ mDistanceService.syncAllPositionTasks(mAllPositionTasks);
+ mDistanceService.syncCurrentGpsPosition(mCurrentGpsPosition);
+
+ // 同步已有的可见控件(解决“控件已添加但服务未收到”问题)
+ Iterator posIdIterator = mVisibleDistanceViews.keySet().iterator();
+ while (posIdIterator.hasNext()) {
+ String positionId = posIdIterator.next();
+ mDistanceService.addVisibleDistanceView(positionId);
+ LogUtils.d(TAG, "服务绑定后同步可见控件:位置ID=" + positionId);
+ }
+
+ LogUtils.d(TAG, "DistanceRefreshService 绑定成功");
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ mDistanceService = null;
+ isServiceBound = false;
+ LogUtils.d(TAG, "DistanceRefreshService 意外断开");
+ }
+ };
+
+ // 接口回调(外部监听)
public interface OnDeleteClickListener {
void onDeleteClick(int position);
}
@@ -74,39 +115,67 @@ public class PositionAdapter extends RecyclerView.Adapter positionList, ArrayList allPositionTasks) {
this.mContext = context;
+ // Java7:显式非空判断(避免空指针)
this.mPositionList = (positionList != null) ? positionList : new ArrayList();
this.mAllPositionTasks = (allPositionTasks != null) ? allPositionTasks : new ArrayList();
- this.mMainHandler = new Handler(Looper.getMainLooper());
- this.mDistanceTimer = new Timer();
this.mPositionTaskMap = new HashMap>();
- // 初始化任务映射表(不变)
- for (PositionModel model : mPositionList) {
+ // 初始化任务映射表(Java7:双迭代器遍历)
+ Iterator modelIterator = mPositionList.iterator();
+ while (modelIterator.hasNext()) {
+ PositionModel model = modelIterator.next();
String validPosId = model.getPositionId();
ArrayList matchedTasks = new ArrayList();
- for (PositionTaskModel task : this.mAllPositionTasks) {
+
+ Iterator taskIterator = this.mAllPositionTasks.iterator();
+ while (taskIterator.hasNext()) {
+ PositionTaskModel task = taskIterator.next();
if (task != null && validPosId.equals(task.getPositionId())) {
matchedTasks.add(task);
}
}
+
mPositionTaskMap.put(validPosId, matchedTasks);
- // 新增:初始化“位置ID→索引”映射(索引即列表中的位置)
mPositionIdToIndexMap.put(validPosId, mPositionList.indexOf(model));
}
- // 启动距离刷新定时器(不变)
- startDistanceRefreshTimer();
+ // 绑定服务
+ bindDistanceRefreshService();
}
- // 对外API:设置当前GPS位置(不变)
+ // ---------------------- 服务绑定/解绑 ----------------------
+ private void bindDistanceRefreshService() {
+ if (!isServiceBound && mContext != null) {
+ Intent serviceIntent = new Intent(mContext, DistanceRefreshService.class);
+ mContext.bindService(serviceIntent, mServiceConnection, Context.BIND_AUTO_CREATE);
+ }
+ }
+
+ public void unbindDistanceRefreshService() {
+ if (isServiceBound && mContext != null) {
+ if (mDistanceService != null) {
+ mDistanceService.clearVisibleDistanceViews();
+ mDistanceService.setOnDistanceUpdateReceiver(null);
+ }
+ mContext.unbindService(mServiceConnection);
+ isServiceBound = false;
+ mDistanceService = null;
+ LogUtils.d(TAG, "DistanceRefreshService 解绑成功");
+ }
+ }
+
+ // ---------------------- 对外API(数据更新) ----------------------
public void setCurrentGpsPosition(PositionModel currentGpsPosition) {
this.mCurrentGpsPosition = currentGpsPosition;
+ if (isServiceBound && mDistanceService != null) {
+ mDistanceService.syncCurrentGpsPosition(currentGpsPosition);
+ LogUtils.d(TAG, "Adapter同步GPS位置到服务:纬度=" + (currentGpsPosition != null ? currentGpsPosition.getLatitude() : 0.0f));
+ }
}
- // 对外API:设置监听器(不变)
public void setOnDeleteClickListener(OnDeleteClickListener listener) {
this.mOnDeleteClickListener = listener;
}
@@ -119,94 +188,88 @@ public class PositionAdapter extends RecyclerView.Adapter entry : mVisibleDistanceViews.entrySet()) {
- String positionId = entry.getKey();
- TextView distanceView = entry.getValue();
-
- PositionModel targetModel = null;
- for (PositionModel model : mPositionList) {
- if (positionId.equals(model.getPositionId())) {
- targetModel = model;
- break;
- }
- }
-
- if (targetModel != null) {
- bindRealDistance(distanceView, targetModel);
- }
- }
- for (PositionTaskModel task : mAllPositionTasks) {
- if (task.isBingo() && task.isEnable()) {
- NotificationUtils.show(mContext, task.getTaskId(), task.getPositionId(), task.getTaskDescription());
- }
- }
- }
- });
- }
- }, 0, REFRESH_INTERVAL);
+ // ---------------------- 服务消息接收(距离更新) ----------------------
+ @Override
+ public void onDistanceUpdate(final String positionId, final String distanceText, final int distanceColor) {
+ // 保障主线程更新(Java7:匿名内部类实现Runnable,替代Lambda)
+ if (Looper.myLooper() != Looper.getMainLooper()) {
+ mUiHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ updateDistanceView(positionId, distanceText, distanceColor);
+ }
+ });
+ } else {
+ updateDistanceView(positionId, distanceText, distanceColor);
+ }
}
- // ---------------------- 核心修复2:管理可见/不可见的距离TextView(不变) ----------------------
- @Override
- public void onViewAttachedToWindow(@NonNull RecyclerView.ViewHolder holder) {
- super.onViewAttachedToWindow(holder);
- int position = holder.getAdapterPosition();
- if (position < 0 || position >= mPositionList.size()) {
+ /**
+ * 精准更新距离TextView(Java7:显式非空判断,避免空指针)
+ */
+ private void updateDistanceView(String positionId, String distanceText, int distanceColor) {
+ // 1. 检查核心参数(避免无效更新)
+ if (positionId == null || distanceText == null || mVisibleDistanceViews.isEmpty()) {
+ LogUtils.w(TAG, "更新距离失败:参数无效(位置ID=" + positionId + ",距离文本=" + distanceText + ")");
return;
}
- PositionModel model = mPositionList.get(position);
- String positionId = model.getPositionId();
- // 同步更新位置ID→索引映射(防止列表项位置变化导致索引失效)
- mPositionIdToIndexMap.put(positionId, position);
-
- TextView distanceView = null;
- if (holder instanceof SimpleViewHolder) {
- distanceView = ((SimpleViewHolder) holder).tvSimpleRealDistance;
- } else if (holder instanceof EditViewHolder) {
- distanceView = ((EditViewHolder) holder).tvEditRealDistance;
- }
-
- if (distanceView != null) {
- mVisibleDistanceViews.put(positionId, distanceView);
+ // 2. 找到目标TextView并更新
+ TextView distanceView = mVisibleDistanceViews.get(positionId);
+ if (distanceView != null && distanceView.isShown()) { // 仅更新“显示中”的控件
+ LogUtils.d(TAG, "更新距离UI:位置ID=" + positionId + ",文本=" + distanceText);
+ distanceView.setText(distanceText);
+ distanceView.setTextColor(distanceColor);
+ // 同步更新任务状态
+ refreshTaskBingoStatus(positionId);
+ } else {
+ // 控件不存在/不可见时,移除映射+同步服务
+ if (mVisibleDistanceViews.containsKey(positionId)) {
+ mVisibleDistanceViews.remove(positionId);
+ LogUtils.w(TAG, "更新距离失败:位置ID=" + positionId + " 对应的TextView不存在/已移除");
+ }
+ if (isServiceBound && mDistanceService != null) {
+ mDistanceService.removeVisibleDistanceView(positionId);
+ }
}
}
@Override
- public void onViewDetachedFromWindow(@NonNull RecyclerView.ViewHolder holder) {
- super.onViewDetachedFromWindow(holder);
- int position = holder.getAdapterPosition();
- if (position < 0 || position >= mPositionList.size()) {
- return;
+ public int getColorRes(int resId) {
+ if (mContext != null) {
+ try {
+ // Java7:显式调用getResources(),避免空指针
+ return mContext.getResources().getColor(resId);
+ } catch (Exception e) {
+ LogUtils.d(TAG, "获取颜色资源失败(resId=" + resId + ")" + e.getMessage());
+ return mContext.getResources().getColor(R.color.colorGrayText); // 异常时返回默认灰色
+ }
}
+ return mContext.getResources().getColor(R.color.colorGrayText);
+ }
- PositionModel model = mPositionList.get(position);
- String positionId = model.getPositionId();
- if (mVisibleDistanceViews.containsKey(positionId)) {
- mVisibleDistanceViews.remove(positionId);
+ // ---------------------- 任务状态刷新 ----------------------
+ private void refreshTaskBingoStatus(String positionId) {
+ Integer targetPosIndex = mPositionIdToIndexMap.get(positionId);
+ if (targetPosIndex != null && targetPosIndex >= 0 && targetPosIndex < mPositionList.size()) {
+ notifyItemChanged(targetPosIndex);
+ LogUtils.d(TAG, "刷新任务状态:位置ID=" + positionId + ",列表索引=" + targetPosIndex);
}
}
- // ---------------------- RecyclerView 核心方法(不变) ----------------------
+ // ---------------------- RecyclerView 核心方法 ----------------------
@Override
public int getItemViewType(int position) {
+ // Java7:显式非空+范围判断(避免越界)
+ if (position < 0 || position >= mPositionList.size()) {
+ LogUtils.w(TAG, "getItemViewType:无效列表位置(position=" + position + ")");
+ return VIEW_TYPE_SIMPLE; // 异常时返回默认类型
+ }
PositionModel model = mPositionList.get(position);
return model.isSimpleView() ? VIEW_TYPE_SIMPLE : VIEW_TYPE_EDIT;
}
@@ -226,10 +289,15 @@ public class PositionAdapter extends RecyclerView.Adapter= mPositionList.size()) {
+ LogUtils.w(TAG, "onBindViewHolder:无效列表位置(position=" + position + ")");
+ return;
+ }
PositionModel model = mPositionList.get(position);
- // 同步更新位置ID→索引映射(确保索引与当前列表项位置一致)
mPositionIdToIndexMap.put(model.getPositionId(), position);
+ // 绑定对应视图
if (holder instanceof SimpleViewHolder) {
bindSimpleView((SimpleViewHolder) holder, model, position);
} else if (holder instanceof EditViewHolder) {
@@ -242,34 +310,109 @@ public class PositionAdapter extends RecyclerView.Adapter= mPositionList.size()) {
+ LogUtils.w(TAG, "onViewAttached:无效列表位置(position=" + position + ")");
+ return;
+ }
+
+ PositionModel model = mPositionList.get(position);
+ String positionId = model.getPositionId();
+ mPositionIdToIndexMap.put(positionId, position); // 同步索引
+
+ // 找到距离TextView(Java7:显式类型转换+非空判断)
+ TextView distanceView = null;
+ if (holder instanceof SimpleViewHolder) {
+ distanceView = ((SimpleViewHolder) holder).tvSimpleRealDistance;
+ } else if (holder instanceof EditViewHolder) {
+ distanceView = ((EditViewHolder) holder).tvEditRealDistance;
+ }
+
+ // 仅添加有效控件,避免重复映射
+ if (distanceView != null && !mVisibleDistanceViews.containsKey(positionId)) {
+ mVisibleDistanceViews.put(positionId, distanceView);
+ LogUtils.d(TAG, "视图附加:添加可见控件(位置ID=" + positionId + "),当前可见数量=" + mVisibleDistanceViews.size());
+ // 同步到服务
+ if (isServiceBound && mDistanceService != null) {
+ mDistanceService.addVisibleDistanceView(positionId);
+ } else {
+ LogUtils.w(TAG, "视图附加:服务未绑定,暂未同步控件(位置ID=" + positionId + ")");
+ }
+ }
+ }
+
+ @Override
+ public void onViewDetachedFromWindow(@NonNull RecyclerView.ViewHolder holder) {
+ super.onViewDetachedFromWindow(holder);
+ int position = holder.getAdapterPosition();
+ if (position < 0 || position >= mPositionList.size()) {
+ LogUtils.w(TAG, "onViewDetached:无效列表位置(position=" + position + ")");
+ return;
+ }
+
+ PositionModel model = mPositionList.get(position);
+ String positionId = model.getPositionId();
+ // 移除本地映射+同步服务
+ if (mVisibleDistanceViews.containsKey(positionId)) {
+ mVisibleDistanceViews.remove(positionId);
+ LogUtils.d(TAG, "视图分离:移除可见控件(位置ID=" + positionId + "),当前可见数量=" + mVisibleDistanceViews.size());
+ if (isServiceBound && mDistanceService != null) {
+ mDistanceService.removeVisibleDistanceView(positionId);
+ }
+ }
+ }
+
@Override
public void onViewRecycled(@NonNull RecyclerView.ViewHolder holder) {
super.onViewRecycled(holder);
int position = holder.getAdapterPosition();
if (position >= 0 && position < mPositionList.size()) {
String positionId = mPositionList.get(position).getPositionId();
+ // 清理本地映射
mVisibleDistanceViews.remove(positionId);
- // 移除已回收位置的索引映射(避免内存泄漏)
mPositionIdToIndexMap.remove(positionId);
+ LogUtils.d(TAG, "视图回收:清理控件映射(位置ID=" + positionId + ")");
+ // 同步服务
+ if (isServiceBound && mDistanceService != null) {
+ mDistanceService.removeVisibleDistanceView(positionId);
+ }
}
+ // 清理任务列表资源(Java7:显式非空判断)
if (holder instanceof SimpleViewHolder) {
- ((SimpleViewHolder) holder).ptlvSimpleTasks.clearData();
- ((SimpleViewHolder) holder).ptlvSimpleTasks.setOnTaskUpdatedListener(null);
+ SimpleViewHolder simpleHolder = (SimpleViewHolder) holder;
+ if (simpleHolder.ptlvSimpleTasks != null) {
+ simpleHolder.ptlvSimpleTasks.clearData();
+ simpleHolder.ptlvSimpleTasks.setOnTaskUpdatedListener(null);
+ }
} else if (holder instanceof EditViewHolder) {
- ((EditViewHolder) holder).ptlvEditTasks.clearData();
- ((EditViewHolder) holder).ptlvEditTasks.setOnTaskUpdatedListener(null);
+ EditViewHolder editHolder = (EditViewHolder) holder;
+ if (editHolder.ptlvEditTasks != null) {
+ editHolder.ptlvEditTasks.clearData();
+ editHolder.ptlvEditTasks.setOnTaskUpdatedListener(null);
+ }
}
}
- // 绑定简单视图(不变)
+ // ---------------------- 视图绑定(简单视图/编辑视图) ----------------------
+ /**
+ * 绑定简单视图(仅展示,无编辑功能)
+ */
private void bindSimpleView(final SimpleViewHolder holder, final PositionModel model, final int position) {
+ // 绑定基础数据(Java7:显式String.format,避免自动类型转换)
holder.tvSimpleLongitude.setText(String.format("经度:%.6f", model.getLongitude()));
holder.tvSimpleLatitude.setText(String.format("纬度:%.6f", model.getLatitude()));
holder.tvSimpleMemo.setText(String.format("备注:%s", model.getMemo()));
- bindRealDistance(holder.tvSimpleRealDistance, model);
+ // 初始默认文本(记录日志,便于排查未覆盖问题)
+ holder.tvSimpleRealDistance.setText("实时距离:加载中...");
+ holder.tvSimpleRealDistance.setTextColor(mContext.getResources().getColor(R.color.colorGrayText));
+ LogUtils.d(TAG, "绑定简单视图:初始化距离文本(位置ID=" + model.getPositionId() + ")");
+ // 长按编辑逻辑(Java7:匿名内部类实现OnLongClickListener+PopupMenu监听)
holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
@@ -277,6 +420,7 @@ public class PositionAdapter extends RecyclerView.Adapter matchedTasks = getSafeTasks(currentPosId);
- holder.ptlvSimpleTasks.clearData();
- holder.ptlvSimpleTasks.setViewStatus(PositionTaskListView.VIEW_MODE_SIMPLE);
- if (holder.ptlvSimpleTasks.getAllTasks().isEmpty()) {
- holder.ptlvSimpleTasks.init(matchedTasks, currentPosId);
+ if (holder.ptlvSimpleTasks != null) {
+ holder.ptlvSimpleTasks.clearData();
+ holder.ptlvSimpleTasks.setViewStatus(PositionTaskListView.VIEW_MODE_SIMPLE);
+ if (holder.ptlvSimpleTasks.getAllTasks().isEmpty()) {
+ holder.ptlvSimpleTasks.init(matchedTasks, currentPosId);
+ }
}
}
- // 绑定编辑视图(不变)
+ /**
+ * 绑定编辑视图(支持修改备注、开关距离、增删任务)
+ */
private void bindEditView(final EditViewHolder holder, final PositionModel model, final int position) {
+ // 绑定基础数据
holder.tvEditLongitude.setText(String.format("经度:%.6f", model.getLongitude()));
holder.tvEditLatitude.setText(String.format("纬度:%.6f", model.getLatitude()));
holder.etEditMemo.setText(model.getMemo());
holder.etEditMemo.setSelection(holder.etEditMemo.getText().length());
- bindRealDistance(holder.tvEditRealDistance, model);
+ // 初始默认文本
+ holder.tvEditRealDistance.setText("实时距离:加载中...");
+ holder.tvEditRealDistance.setTextColor(mContext.getResources().getColor(R.color.colorGrayText));
+ LogUtils.d(TAG, "绑定编辑视图:初始化距离文本(位置ID=" + model.getPositionId() + ")");
+ // 实时距离开关状态(Java7:显式判断设置选中)
if (model.isEnableRealPositionDistance()) {
holder.rgRealDistanceSwitch.check(R.id.rb_enable);
} else {
holder.rgRealDistanceSwitch.check(R.id.rb_disable);
}
+ // 删除按钮逻辑(Java7:匿名内部类实现OnClickListener)
holder.btnEditDelete.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mOnDeleteClickListener != null) {
mOnDeleteClickListener.onDeleteClick(position);
+ // 清理本地映射
mPositionTaskMap.remove(model.getPositionId());
- // 移除删除位置的索引映射
mPositionIdToIndexMap.remove(model.getPositionId());
+ // 同步服务
+ if (isServiceBound && mDistanceService != null) {
+ mDistanceService.removeVisibleDistanceView(model.getPositionId());
+ }
}
}
});
+ // 取消编辑逻辑
holder.btnEditCancel.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
@@ -338,25 +497,51 @@ public class PositionAdapter extends RecyclerView.Adapter allTasks = holder.ptlvEditTasks.getAllTasks();
+ ArrayList allTasks = (holder.ptlvEditTasks != null) ? holder.ptlvEditTasks.getAllTasks() : new ArrayList();
ArrayList boundTasks = new ArrayList();
- for (PositionTaskModel task : allTasks) {
+
+ // Java7:迭代器遍历任务列表,绑定位置ID
+ Iterator taskIterator = allTasks.iterator();
+ while (taskIterator.hasNext()) {
+ PositionTaskModel task = taskIterator.next();
if (task != null) {
task.setPositionId(currentPosId);
boundTasks.add(task);
}
}
- mPositionTaskMap.put(currentPosId, boundTasks);
+ // 更新本地任务映射
+ mPositionTaskMap.put(currentPosId, boundTasks);
+ // 清理旧任务+添加新任务(Java7:迭代器删除,避免ConcurrentModificationException)
+ Iterator globalTaskIter = mAllPositionTasks.iterator();
+ while (globalTaskIter.hasNext()) {
+ PositionTaskModel task = globalTaskIter.next();
+ if (task != null && currentPosId.equals(task.getPositionId())) {
+ globalTaskIter.remove(); // Java7:迭代器安全删除
+ }
+ }
+ mAllPositionTasks.addAll(boundTasks);
+
+ // 同步服务数据
+ if (isServiceBound && mDistanceService != null) {
+ mDistanceService.syncPositionList(mPositionList);
+ mDistanceService.syncAllPositionTasks(mAllPositionTasks);
+ LogUtils.d(TAG, "编辑视图保存:同步位置+任务数据到服务(位置ID=" + currentPosId + ")");
+ }
+
+ // 切换视图+回调通知
model.setIsSimpleView(true);
notifyItemChanged(position);
if (mOnSavePositionClickListener != null) {
@@ -366,46 +551,71 @@ public class PositionAdapter extends RecyclerView.Adapter matchedTasks = getSafeTasks(currentPosId);
- holder.ptlvEditTasks.clearData();
+ if (holder.ptlvEditTasks != null) {
+ holder.ptlvEditTasks.clearData();
- holder.ptlvEditTasks.setOnTaskUpdatedListener(new PositionTaskListView.OnTaskUpdatedListener() {
- @Override
- public void onTaskUpdated(String posId, ArrayList updatedTasks) {
- ArrayList boundTasks = new ArrayList();
- for (PositionTaskModel task : updatedTasks) {
- if (task != null) {
- task.setPositionId(currentPosId);
- boundTasks.add(task);
+ // 任务更新监听(Java7:匿名内部类实现)
+ holder.ptlvEditTasks.setOnTaskUpdatedListener(new PositionTaskListView.OnTaskUpdatedListener() {
+ @Override
+ public void onTaskUpdated(String posId, ArrayList updatedTasks) {
+ ArrayList boundTasks = new ArrayList();
+ // 绑定任务到当前位置
+ Iterator taskIterator = updatedTasks.iterator();
+ while (taskIterator.hasNext()) {
+ PositionTaskModel task = taskIterator.next();
+ if (task != null) {
+ task.setPositionId(currentPosId);
+ boundTasks.add(task);
+ }
}
- }
- mPositionTaskMap.put(currentPosId, boundTasks);
- Iterator taskIterator = mAllPositionTasks.iterator();
- while (taskIterator.hasNext()) {
- PositionTaskModel task = taskIterator.next();
- if (task != null && currentPosId.equals(task.getPositionId())) {
- taskIterator.remove();
+ // 更新本地映射
+ mPositionTaskMap.put(currentPosId, boundTasks);
+ // 清理旧任务+添加新任务
+ Iterator globalTaskIter = mAllPositionTasks.iterator();
+ while (globalTaskIter.hasNext()) {
+ PositionTaskModel task = globalTaskIter.next();
+ if (task != null && currentPosId.equals(task.getPositionId())) {
+ globalTaskIter.remove();
+ }
}
- }
- mAllPositionTasks.addAll(boundTasks);
+ mAllPositionTasks.addAll(boundTasks);
- if (mOnSavePositionTaskClickListener != null) {
- mOnSavePositionTaskClickListener.onSavePositionTaskClick();
- }
- Toast.makeText(mContext, "任务信息已保存", Toast.LENGTH_SHORT).show();
- }
- });
+ // 同步服务
+ if (isServiceBound && mDistanceService != null) {
+ mDistanceService.syncAllPositionTasks(mAllPositionTasks);
+ LogUtils.d(TAG, "任务更新:同步任务数据到服务(位置ID=" + currentPosId + ",任务数量=" + boundTasks.size() + ")");
+ }
- if (holder.ptlvEditTasks.getAllTasks().isEmpty()) {
- holder.ptlvEditTasks.init(matchedTasks, currentPosId);
+ // 回调通知
+ if (mOnSavePositionTaskClickListener != null) {
+ mOnSavePositionTaskClickListener.onSavePositionTaskClick();
+ }
+ Toast.makeText(mContext, "任务信息已保存", Toast.LENGTH_SHORT).show();
+ }
+ });
+
+ // 初始化任务列表
+ if (holder.ptlvEditTasks.getAllTasks().isEmpty()) {
+ holder.ptlvEditTasks.init(matchedTasks, currentPosId);
+ }
+ holder.ptlvEditTasks.setViewStatus(PositionTaskListView.VIEW_MODE_EDIT);
}
- holder.ptlvEditTasks.setViewStatus(PositionTaskListView.VIEW_MODE_EDIT);
+ // 添加任务逻辑(Java7:匿名内部类实现OnClickListener)
holder.btnAddTask.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
+ if (holder.ptlvEditTasks == null) {
+ LogUtils.e(TAG, "添加任务失败:任务列表视图为空(位置ID=" + currentPosId + ")");
+ Toast.makeText(mContext, "添加任务失败,请重试", Toast.LENGTH_SHORT).show();
+ return;
+ }
+
+ // 创建新任务(Java7:显式调用构造函数,避免Builder模式)
PositionTaskModel newTask = new PositionTaskModel(
PositionTaskModel.genTaskId(),
currentPosId,
@@ -415,11 +625,19 @@ public class PositionAdapter extends RecyclerView.Adapter currentTasks = getSafeTasks(currentPosId);
currentTasks.add(newTask);
mPositionTaskMap.put(currentPosId, new ArrayList(currentTasks));
mAllPositionTasks.add(newTask);
+ // 同步服务
+ if (isServiceBound && mDistanceService != null) {
+ mDistanceService.syncAllPositionTasks(mAllPositionTasks);
+ LogUtils.d(TAG, "新增任务:同步任务数据到服务(位置ID=" + currentPosId + ",新任务ID=" + newTask.getTaskId() + ")");
+ }
+
+ // 刷新任务列表视图
holder.ptlvEditTasks.clearData();
holder.ptlvEditTasks.init(currentTasks, currentPosId);
holder.ptlvEditTasks.setViewStatus(PositionTaskListView.VIEW_MODE_EDIT);
@@ -428,248 +646,259 @@ public class PositionAdapter extends RecyclerView.Adapter getSafeTasks(String positionId) {
+ if (positionId == null) {
+ LogUtils.w(TAG, "getSafeTasks:位置ID为空,返回空任务列表");
+ return new ArrayList();
+ }
+ if (!mPositionTaskMap.containsKey(positionId)) {
+ mPositionTaskMap.put(positionId, new ArrayList());
+ LogUtils.d(TAG, "getSafeTasks:位置ID=" + positionId + " 无任务列表,初始化空列表");
+ }
+ return mPositionTaskMap.get(positionId);
+ }
+
+ /**
+ * 新增位置(Java7:显式非空判断,迭代器同步索引)
+ */
+ public void addPosition(PositionModel model) {
+ if (model == null || model.getPositionId() == null) {
+ LogUtils.e(TAG, "新增位置失败:位置模型/位置ID为空");
+ return;
+ }
+ String validPosId = model.getPositionId();
+ mPositionList.add(model);
+ mPositionIdToIndexMap.put(validPosId, mPositionList.size() - 1);
+
+ // 初始化任务列表(若不存在)
+ if (!mPositionTaskMap.containsKey(validPosId)) {
+ mPositionTaskMap.put(validPosId, new ArrayList());
+ }
+
+ // 同步服务+刷新视图
+ if (isServiceBound && mDistanceService != null) {
+ mDistanceService.syncPositionList(mPositionList);
+ // 若控件已添加,立即同步服务
+ if (mVisibleDistanceViews.containsKey(validPosId)) {
+ mDistanceService.addVisibleDistanceView(validPosId);
+ LogUtils.d(TAG, "新增位置:同步服务并添加控件(位置ID=" + validPosId + ")");
+ }
+ }
+
+ notifyItemInserted(mPositionList.size() - 1);
+ LogUtils.d(TAG, "新增位置成功:位置ID=" + validPosId + ",当前列表总数=" + mPositionList.size());
+ }
+
+ /**
+ * 删除位置(Java7:迭代器安全删除,避免并发异常)
+ */
+ public void removePosition(int position) {
+ if (position < 0 || position >= mPositionList.size()) {
+ LogUtils.w(TAG, "删除位置失败:无效列表位置(position=" + position + ")");
+ return;
+ }
+ PositionModel removedModel = mPositionList.get(position);
+ String removedPosId = removedModel.getPositionId();
+ if (removedPosId == null) {
+ LogUtils.w(TAG, "删除位置失败:待删除位置ID为空(列表位置=" + position + ")");
return;
}
- if (mCurrentGpsPosition == null) {
- distanceView.setText("实时距离:等待GPS定位");
- distanceView.setTextColor(mContext.getResources().getColor(R.color.colorGrayText));
- return;
- }
+ // 1. 清理本地缓存和映射
+ mVisibleDistanceViews.remove(removedPosId);
+ mPositionIdToIndexMap.remove(removedPosId);
+ LogUtils.d(TAG, "删除位置:清理本地映射(位置ID=" + removedPosId + ")");
- try {
- double distanceM = PositionModel.calculatePositionDistance(mCurrentGpsPosition, model, false);
-// 设置任务列表触发状态(调用优化后的通知逻辑)
- bingoTask(model, (int) distanceM);
- String distanceText = (distanceM < 1000) ?
- String.format("实时距离:%.1f 米", distanceM) :
- String.format("实时距离:%.1f 千米", distanceM / 1000);
- distanceView.setText(distanceText);
- distanceView.setTextColor(mContext.getResources().getColor(R.color.colorEnableGreen));
- } catch (IllegalArgumentException e) {
- distanceView.setText("实时距离:数据无效");
- distanceView.setTextColor(mContext.getResources().getColor(R.color.colorRed));
- }
- }
+ // 2. 重新同步剩余位置的索引映射(Java7:迭代器遍历)
+ Iterator modelIterator = mPositionList.iterator();
+ int index = 0;
+ while (modelIterator.hasNext()) {
+ PositionModel remainingModel = modelIterator.next();
+ mPositionIdToIndexMap.put(remainingModel.getPositionId(), index);
+ index++;
+ }
-// ---------------------- 核心优化:bingoTask 任务触发通知逻辑(替换 notifyDataSetChanged()) ----------------------
- private void bingoTask(PositionModel model, int distanceM) {
-// 1. 标记是否有任务触发(避免无变化时无效更新)
- boolean hasTaskTriggered = false;
-// 2. 记录当前位置ID(后续精准定位列表项)
- final String targetPosId = model.getPositionId();
+ // 3. 清理全局任务列表和映射表(Java7:迭代器安全删除)
+ Iterator taskIterator = mAllPositionTasks.iterator();
+ while (taskIterator.hasNext()) {
+ PositionTaskModel task = taskIterator.next();
+ if (task != null && removedPosId.equals(task.getPositionId())) {
+ taskIterator.remove();
+ }
+ }
+ mPositionTaskMap.remove(removedPosId);
+ mPositionList.remove(position);
+ LogUtils.d(TAG, "删除位置:清理任务数据(位置ID=" + removedPosId + "),剩余列表总数=" + mPositionList.size());
-// 遍历当前位置的任务,判断是否触发
- for (PositionTaskModel task : mAllPositionTasks) {
- if (targetPosId.equals(task.getPositionId())) {
-// 暂存旧状态(避免重复触发更新)
- boolean oldBingoState = task.isBingo();
- boolean newBingoState = false;
+ // 4. 同步服务
+ if (isServiceBound && mDistanceService != null) {
+ mDistanceService.syncPositionList(mPositionList);
+ mDistanceService.syncAllPositionTasks(mAllPositionTasks);
+ mDistanceService.removeVisibleDistanceView(removedPosId);
+ LogUtils.d(TAG, "删除位置:同步服务数据(位置ID=" + removedPosId + ")");
+ }
-// 根据任务条件判断新触发状态
- if (task.isGreaterThan()) {
- newBingoState = task.isEnable() && distanceM > task.getDiscussDistance();
- } else if (task.isLessThan()) {
- newBingoState = task.isEnable() && distanceM < task.getDiscussDistance();
- } else {
- newBingoState = task.isEnable() && true;
- }
+ // 刷新视图
+ notifyItemRemoved(position);
+ notifyItemRangeChanged(position, mPositionList.size());
+ }
-// 仅当状态变化时,才更新任务状态并标记需要通知
- if (newBingoState != oldBingoState) {
- task.setIsBingo(newBingoState);
- hasTaskTriggered = true;
- }
- }
- }
+ /**
+ * 更新所有位置(Java7:双迭代器清理无效数据)
+ */
+ public void updateAllPositions(ArrayList newPositionList) {
+ if (newPositionList == null) {
+ LogUtils.e(TAG, "更新所有位置失败:新位置列表为空");
+ return;
+ }
+ mPositionList.clear();
+ mPositionList.addAll(newPositionList);
+ LogUtils.d(TAG, "更新所有位置:接收新列表(数量=" + newPositionList.size() + ")");
-// 3. 仅当有任务触发且状态变化时,才执行精准更新(避免全局刷新)
- if (hasTaskTriggered) {
-// 切换到主线程更新UI(解决子线程更新异常)
- mUiHandler.post(new Runnable() {
- @Override
- public void run() {
-// 从映射表快速获取位置索引(避免遍历列表,提升效率)
- Integer targetPosIndex = mPositionIdToIndexMap.get(targetPosId);
- if (targetPosIndex != null && targetPosIndex >= 0 && targetPosIndex < mPositionList.size()) {
-// 精准更新单个位置项(仅刷新该位置的任务列表,不干扰其他项)
- notifyItemChanged(targetPosIndex);
- }
- }
- });
- }
- }
+ // 1. 清理本地无效可见控件(Java7:迭代器遍历+判断存在性)
+ Iterator distanceViewIter = mVisibleDistanceViews.keySet().iterator();
+ while (distanceViewIter.hasNext()) {
+ String posId = distanceViewIter.next();
+ boolean isPosExist = false;
-// 工具方法:安全获取任务列表(不变)
- private ArrayList getSafeTasks(String positionId) {
- if (!mPositionTaskMap.containsKey(positionId)) {
- mPositionTaskMap.put(positionId, new ArrayList());
- }
- return mPositionTaskMap.get(positionId);
- }
+ Iterator modelIter = newPositionList.iterator();
+ while (modelIter.hasNext()) {
+ PositionModel model = modelIter.next();
+ if (posId.equals(model.getPositionId())) {
+ isPosExist = true;
+ break;
+ }
+ }
-// 对外API:新增位置(补充索引映射更新)
- public void addPosition(PositionModel model) {
- if (model == null) return;
- String validPosId = model.getPositionId();
- mPositionList.add(model);
-// 新增:添加新位置的索引映射(索引为列表最后一位)
- mPositionIdToIndexMap.put(validPosId, mPositionList.size() - 1);
+ if (!isPosExist) {
+ distanceViewIter.remove();
+ LogUtils.d(TAG, "更新所有位置:清理无效可见控件(位置ID=" + posId + ")");
+ }
+ }
- if (!mPositionTaskMap.containsKey(validPosId)) {
- mPositionTaskMap.put(validPosId, new ArrayList());
- }
- notifyItemInserted(mPositionList.size() - 1);
- }
+ // 2. 清理任务映射表无效项(Java7:迭代器遍历)
+ Iterator taskMapKeyIter = mPositionTaskMap.keySet().iterator();
+ while (taskMapKeyIter.hasNext()) {
+ String posId = taskMapKeyIter.next();
+ boolean isPosExist = false;
-// 对外API:删除位置(补充索引映射清理)
- public void removePosition(int position) {
- if (position < 0 || position >= mPositionList.size()) return;
- PositionModel removedModel = mPositionList.get(position);
- String removedPosId = removedModel.getPositionId();
+ Iterator modelIter = newPositionList.iterator();
+ while (modelIter.hasNext()) {
+ PositionModel model = modelIter.next();
+ if (posId.equals(model.getPositionId())) {
+ isPosExist = true;
+ break;
+ }
+ }
-// 1. 清理距离缓存和索引映射(新增)
- mVisibleDistanceViews.remove(removedPosId);
- mPositionIdToIndexMap.remove(removedPosId);
+ if (!isPosExist) {
+ taskMapKeyIter.remove();
+ LogUtils.d(TAG, "更新所有位置:清理无效任务映射(位置ID=" + posId + ")");
+ }
+ }
-// 2. 重新同步剩余位置的索引映射(避免删除后索引错位)
- for (int i = position; i < mPositionList.size(); i++) {
- PositionModel remainingModel = mPositionList.get(i);
- mPositionIdToIndexMap.put(remainingModel.getPositionId(), i);
- }
+ // 3. 重新初始化索引映射(Java7:普通for循环,避免迭代器索引问题)
+ mPositionIdToIndexMap.clear();
+ for (int i = 0; i < newPositionList.size(); i++) {
+ PositionModel model = newPositionList.get(i);
+ String posId = model.getPositionId();
+ mPositionIdToIndexMap.put(posId, i);
+ // 初始化新位置的任务列表(若不存在)
+ if (!mPositionTaskMap.containsKey(posId)) {
+ mPositionTaskMap.put(posId, new ArrayList());
+ LogUtils.d(TAG, "更新所有位置:初始化新位置任务列表(位置ID=" + posId + ")");
+ }
+ }
-// 3. 清理全局任务列表和映射表(不变)
- Iterator taskIterator = mAllPositionTasks.iterator();
- while (taskIterator.hasNext()) {
- PositionTaskModel task = (PositionTaskModel)taskIterator.next();
- if (task != null && removedPosId.equals(task.getPositionId())) {
- taskIterator.remove();
- }
- }
- mPositionTaskMap.remove(removedPosId);
- mPositionList.remove(position);
+ // 4. 同步服务
+ if (isServiceBound && mDistanceService != null) {
+ mDistanceService.syncPositionList(mPositionList);
+ mDistanceService.clearVisibleDistanceViews();
+ // 同步有效可见控件(Java7:迭代器遍历)
+ Iterator validPosIter = mVisibleDistanceViews.keySet().iterator();
+ while (validPosIter.hasNext()) {
+ mDistanceService.addVisibleDistanceView(validPosIter.next());
+ }
+ LogUtils.d(TAG, "更新所有位置:同步服务数据(可见控件数量=" + mVisibleDistanceViews.size() + ")");
+ }
- notifyItemRemoved(position);
- notifyItemRangeChanged(position, mPositionList.size());
- }
+ // 刷新视图
+ notifyDataSetChanged();
+ }
-// 对外API:更新所有位置(补充索引映射同步)
- public void updateAllPositions(ArrayList newPositionList) {
- if (newPositionList == null) return;
- mPositionList.clear();
- mPositionList.addAll(newPositionList);
+ /**
+ * 批量切换简单视图(Java7:迭代器遍历)
+ */
+ public void switchAllToSimpleView() {
+ Iterator modelIterator = mPositionList.iterator();
+ while (modelIterator.hasNext()) {
+ PositionModel model = modelIterator.next();
+ model.setIsSimpleView(true);
+ }
+ notifyDataSetChanged();
+ LogUtils.d(TAG, "批量切换简单视图:已切换所有位置(总数=" + mPositionList.size() + ")");
+ }
-// 1. 清理距离缓存中无效的位置ID(不变)
- Iterator distanceViewIter = mVisibleDistanceViews.keySet().iterator();
- while (distanceViewIter.hasNext()) {
- String posId = (String)distanceViewIter.next();
- boolean isPosExist = false;
- for (PositionModel model : newPositionList) {
- if (posId.equals(model.getPositionId())) {
- isPosExist = true;
- break;
- }
- }
- if (!isPosExist) {
- distanceViewIter.remove();
- }
- }
+ /**
+ * 获取位置列表(Java7:返回新列表,避免外部修改)
+ */
+ public ArrayList getPositionList() {
+ return new ArrayList(mPositionList);
+ }
-// 2. 清理任务映射表(不变)
- Iterator taskMapKeyIter = mPositionTaskMap.keySet().iterator();
- while (taskMapKeyIter.hasNext()) {
- String posId = (String)taskMapKeyIter.next();
- boolean isPosExist = false;
- for (PositionModel model : newPositionList) {
- if (posId.equals(model.getPositionId())) {
- isPosExist = true;
- break;
- }
- }
- if (!isPosExist) {
- taskMapKeyIter.remove();
- }
- }
+ // ---------------------- ViewHolder 定义(完全兼容Java7) ----------------------
+ public static class SimpleViewHolder extends RecyclerView.ViewHolder {
+ TextView tvSimpleLongitude;
+ TextView tvSimpleLatitude;
+ TextView tvSimpleMemo;
+ TextView tvSimpleRealDistance;
+ PositionTaskListView ptlvSimpleTasks;
-// 3. 重新初始化索引映射和任务列表(新增索引同步)
- mPositionIdToIndexMap.clear();
- for (int i = 0; i < newPositionList.size(); i++) {
- PositionModel model = (PositionModel)newPositionList.get(i);
- String posId = model.getPositionId();
- mPositionIdToIndexMap.put(posId, i); // 同步新列表的索引
- if (!mPositionTaskMap.containsKey(posId)) {
- mPositionTaskMap.put(posId, new ArrayList());
- }
- }
+ public SimpleViewHolder(@NonNull View itemView) {
+ super(itemView);
+ // Java7:显式 findViewById + 类型转换
+ tvSimpleLongitude = (TextView) itemView.findViewById(R.id.tv_simple_longitude);
+ tvSimpleLatitude = (TextView) itemView.findViewById(R.id.tv_simple_latitude);
+ tvSimpleMemo = (TextView) itemView.findViewById(R.id.tv_simple_memo);
+ tvSimpleRealDistance = (TextView) itemView.findViewById(R.id.tv_simple_real_distance);
+ ptlvSimpleTasks = (PositionTaskListView) itemView.findViewById(R.id.ptlv_simple_tasks);
+ }
+ }
- notifyDataSetChanged();
- }
+ public static class EditViewHolder extends RecyclerView.ViewHolder {
+ TextView tvEditLongitude;
+ TextView tvEditLatitude;
+ EditText etEditMemo;
+ RadioGroup rgRealDistanceSwitch;
+ RadioButton rbDisable;
+ RadioButton rbEnable;
+ Button btnEditDelete;
+ Button btnEditCancel;
+ Button btnEditConfirm;
+ PositionTaskListView ptlvEditTasks;
+ Button btnAddTask;
+ TextView tvEditRealDistance;
-// 对外API:批量切换简单视图(不变)
- public void switchAllToSimpleView() {
- for (PositionModel model : mPositionList) {
- model.setIsSimpleView(true);
- }
- notifyDataSetChanged();
- }
-
-// 对外API:获取位置列表(不变)
- public ArrayList getPositionList() {
- return new ArrayList(mPositionList);
- }
-
-// ---------------------- ViewHolder 定义(不变) ----------------------
- public static class SimpleViewHolder extends RecyclerView.ViewHolder {
- TextView tvSimpleLongitude;
- TextView tvSimpleLatitude;
- TextView tvSimpleMemo;
- TextView tvSimpleRealDistance;
- PositionTaskListView ptlvSimpleTasks;
-
- public SimpleViewHolder(@NonNull View itemView) {
- super(itemView);
- tvSimpleLongitude = (TextView) itemView.findViewById(R.id.tv_simple_longitude);
- tvSimpleLatitude = (TextView) itemView.findViewById(R.id.tv_simple_latitude);
- tvSimpleMemo = (TextView) itemView.findViewById(R.id.tv_simple_memo);
- tvSimpleRealDistance = (TextView) itemView.findViewById(R.id.tv_simple_real_distance);
- ptlvSimpleTasks = (PositionTaskListView) itemView.findViewById(R.id.ptlv_simple_tasks);
- }
- }
-
- public static class EditViewHolder extends RecyclerView.ViewHolder {
- TextView tvEditLongitude;
- TextView tvEditLatitude;
- EditText etEditMemo;
- RadioGroup rgRealDistanceSwitch;
- RadioButton rbDisable;
- RadioButton rbEnable;
- Button btnEditDelete;
- Button btnEditCancel;
- Button btnEditConfirm;
- PositionTaskListView ptlvEditTasks;
- Button btnAddTask;
- TextView tvEditRealDistance;
-
- public EditViewHolder(@NonNull View itemView) {
- super(itemView);
- tvEditLongitude = (TextView) itemView.findViewById(R.id.tv_edit_longitude);
- tvEditLatitude = (TextView) itemView.findViewById(R.id.tv_edit_latitude);
- etEditMemo = (EditText) itemView.findViewById(R.id.et_edit_memo);
- rgRealDistanceSwitch = (RadioGroup) itemView.findViewById(R.id.rg_real_distance_switch);
- rbDisable = (RadioButton) itemView.findViewById(R.id.rb_disable);
- rbEnable = (RadioButton) itemView.findViewById(R.id.rb_enable);
- btnEditDelete = (Button) itemView.findViewById(R.id.btn_edit_delete);
- btnEditCancel = (Button) itemView.findViewById(R.id.btn_edit_cancel);
- btnEditConfirm = (Button) itemView.findViewById(R.id.btn_edit_confirm);
- ptlvEditTasks = (PositionTaskListView) itemView.findViewById(R.id.ptlv_edit_tasks);
- btnAddTask = (Button) itemView.findViewById(R.id.btn_add_task);
- tvEditRealDistance = (TextView) itemView.findViewById(R.id.tv_edit_real_distance);
- }
- }
+ public EditViewHolder(@NonNull View itemView) {
+ super(itemView);
+ // Java7:显式 findViewById + 类型转换
+ tvEditLongitude = (TextView) itemView.findViewById(R.id.tv_edit_longitude);
+ tvEditLatitude = (TextView) itemView.findViewById(R.id.tv_edit_latitude);
+ etEditMemo = (EditText) itemView.findViewById(R.id.et_edit_memo);
+ rgRealDistanceSwitch = (RadioGroup) itemView.findViewById(R.id.rg_real_distance_switch);
+ rbDisable = (RadioButton) itemView.findViewById(R.id.rb_disable);
+ rbEnable = (RadioButton) itemView.findViewById(R.id.rb_enable);
+ btnEditDelete = (Button) itemView.findViewById(R.id.btn_edit_delete);
+ btnEditCancel = (Button) itemView.findViewById(R.id.btn_edit_cancel);
+ btnEditConfirm = (Button) itemView.findViewById(R.id.btn_edit_confirm);
+ ptlvEditTasks = (PositionTaskListView) itemView.findViewById(R.id.ptlv_edit_tasks);
+ btnAddTask = (Button) itemView.findViewById(R.id.btn_add_task);
+ tvEditRealDistance = (TextView) itemView.findViewById(R.id.tv_edit_real_distance);
+ }
+ }
}
diff --git a/positions/src/main/java/cc/winboll/studio/positions/models/PositionModel.java b/positions/src/main/java/cc/winboll/studio/positions/models/PositionModel.java
index ea05bef2..c5a0154d 100644
--- a/positions/src/main/java/cc/winboll/studio/positions/models/PositionModel.java
+++ b/positions/src/main/java/cc/winboll/studio/positions/models/PositionModel.java
@@ -22,6 +22,8 @@ public class PositionModel extends BaseBean {
double latitude;
// 位置备注(空值时显示“无备注”)
String memo;
+ // 定位点与指定点实时距离大小
+ double realPositionDistance;
// 是否启用实时距离计算
boolean isEnableRealPositionDistance;
// 是否显示简单视图(true=简单视图,false=编辑视图)
@@ -45,6 +47,14 @@ public class PositionModel extends BaseBean {
this.isEnableRealPositionDistance = false;
}
+ public void setRealPositionDistance(double realPositionDistance) {
+ this.realPositionDistance = realPositionDistance;
+ }
+
+ public double getRealPositionDistance() {
+ return realPositionDistance;
+ }
+
// ---------------------- Getter/Setter(确保字段有效性) ----------------------
public void setPositionId(String positionId) {
this.positionId = (positionId == null || positionId.trim().isEmpty()) ? genPositionId() : positionId;
diff --git a/positions/src/main/java/cc/winboll/studio/positions/services/DistanceRefreshService.java b/positions/src/main/java/cc/winboll/studio/positions/services/DistanceRefreshService.java
new file mode 100644
index 00000000..3a730398
--- /dev/null
+++ b/positions/src/main/java/cc/winboll/studio/positions/services/DistanceRefreshService.java
@@ -0,0 +1,340 @@
+package cc.winboll.studio.positions.services;
+
+/**
+ * @Author ZhanGSKen&豆包大模型
+ * @Date 2025/09/30 19:53
+ * @Describe DistanceRefreshService
+ */
+import android.app.Service;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.util.Log;
+
+import cc.winboll.studio.libappbase.LogUtils;
+import cc.winboll.studio.positions.R;
+import cc.winboll.studio.positions.models.PositionModel;
+import cc.winboll.studio.positions.models.PositionTaskModel;
+import cc.winboll.studio.positions.utils.NotificationUtils;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Timer;
+import java.util.TimerTask;
+
+/**
+ * 距离刷新服务:独立管理定时器,负责实时距离计算、任务触发判断、发送UI更新消息
+ * 特性:1. 自启动(绑定后自动启动定时器) 2. 数据与Activity/Adapter同步 3. 本地消息通知UI更新
+ * Java 7 适配:移除Lambda、Stream,使用匿名内部类+迭代器,明确泛型声明
+ */
+public class DistanceRefreshService extends Service {
+ // 常量定义
+ public static final String TAG = "DistanceRefreshService";
+ public static final long REFRESH_INTERVAL = 5000; // 5秒刷新一次
+ public static final int MSG_UPDATE_DISTANCE = 1001;
+ public static final String KEY_POSITION_ID = "key_position_id";
+ public static final String KEY_DISTANCE_TEXT = "key_distance_text";
+ public static final String KEY_DISTANCE_COLOR = "key_distance_color";
+
+ // 核心成员变量(Java7:明确泛型初始化)
+ private Timer mDistanceTimer;
+ private Handler mMainHandler;
+ private PositionModel mCurrentGpsPosition;
+ private ArrayList mPositionList;
+ private ArrayList mAllPositionTasks;
+ private Map mVisibleDistanceViewTags = new HashMap();
+ private OnDistanceUpdateReceiver mUpdateReceiver;
+
+ // 数据同步与消息接收接口(Activity/Adapter实现)
+ public interface OnDistanceUpdateReceiver {
+ void onDistanceUpdate(String positionId, String distanceText, int distanceColor);
+ int getColorRes(int resId);
+ }
+
+ // 服务绑定器(用于外部获取服务实例)
+ public class DistanceBinder extends Binder {
+ public DistanceRefreshService getService() {
+ return DistanceRefreshService.this;
+ }
+ }
+
+ private final IBinder mBinder = new DistanceBinder();
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ Log.d(TAG, "DistanceRefreshService onCreate");
+ // 初始化主线程Handler(Java7:匿名内部类实现handleMessage)
+ mMainHandler = new Handler(Looper.getMainLooper()) {
+ @Override
+ public void handleMessage(Message msg) {
+ super.handleMessage(msg);
+ if (msg.what == MSG_UPDATE_DISTANCE && mUpdateReceiver != null) {
+ // 解析消息(Java7:显式调用getData(),避免链式调用)
+ String positionId = msg.getData().getString(KEY_POSITION_ID);
+ String distanceText = msg.getData().getString(KEY_DISTANCE_TEXT);
+ int distanceColor = msg.getData().getInt(KEY_DISTANCE_COLOR);
+ LogUtils.d(TAG, "接收消息→转发更新:位置ID=" + positionId + ",距离文本=" + distanceText);
+
+ mUpdateReceiver.onDistanceUpdate(positionId, distanceText, distanceColor);
+ }
+ }
+ };
+ // 初始化数据集(Java7:明确泛型类型)
+ mPositionList = new ArrayList();
+ mAllPositionTasks = new ArrayList();
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ // 绑定服务时启动定时器(确保仅启动一次)
+ if (mDistanceTimer == null) {
+ startDistanceTimer();
+ Log.d(TAG, "DistanceRefreshService onBind - 定时器首次启动");
+ } else {
+ Log.d(TAG, "DistanceRefreshService onBind - 定时器已在运行,无需重复启动");
+ }
+ return mBinder;
+ }
+
+ /**
+ * 启动定时器:核心逻辑(距离计算、任务触发、发送UI更新消息)
+ * Java7:使用匿名内部类实现TimerTask,显式调用cancel+purge
+ */
+ private void startDistanceTimer() {
+ // 先停止旧定时器(避免残留任务)
+ if (mDistanceTimer != null) {
+ mDistanceTimer.cancel();
+ mDistanceTimer.purge(); // 清空已取消的任务,释放资源
+ }
+ mDistanceTimer = new Timer();
+ // Java7:匿名内部类实现TimerTask的run方法(替代Lambda)
+ mDistanceTimer.scheduleAtFixedRate(new TimerTask() {
+ @Override
+ public void run() {
+ LogUtils.d(TAG, "定时器触发→开始计算距离,可见位置数量=" + mVisibleDistanceViewTags.size());
+ calculateAndSendDistanceUpdates();
+ checkAndTriggerTasks();
+ }
+ }, 0, REFRESH_INTERVAL); // 立即执行,之后每5秒执行一次
+ }
+
+ /**
+ * 计算距离并发送UI更新消息
+ * Java7:使用迭代器遍历Map(替代forEach Lambda),显式空判断
+ */
+ private void calculateAndSendDistanceUpdates() {
+ // 无可见控件/数据/接收器时,直接返回
+ if (mVisibleDistanceViewTags.isEmpty()) {
+ LogUtils.d(TAG, "无需要更新的可见控件,跳过计算");
+ return;
+ }
+ if (mPositionList.isEmpty()) {
+ LogUtils.d(TAG, "位置列表为空,无法计算距离");
+ return;
+ }
+ if (mUpdateReceiver == null) {
+ LogUtils.d(TAG, "未设置消息接收器(Adapter未绑定),无法发送更新");
+ return;
+ }
+
+ // Java7:使用Iterator遍历Map.Entry(替代forEach Lambda)
+ Iterator> entryIterator = mVisibleDistanceViewTags.entrySet().iterator();
+ while (entryIterator.hasNext()) {
+ Map.Entry entry = entryIterator.next();
+ String positionId = entry.getKey();
+ PositionModel targetModel = findPositionModelById(positionId);
+
+ if (targetModel == null) {
+ // 位置模型不存在时,发送“无效位置”消息
+ sendDistanceUpdateMessage(
+ positionId,
+ "实时距离:位置无效",
+ mUpdateReceiver.getColorRes(R.color.colorRed)
+ );
+ LogUtils.d(TAG, "位置ID=" + positionId + " 未找到对应模型,发送无效提示");
+ continue;
+ }
+
+ // 距离计算与文本生成
+ String distanceText;
+ int distanceColor;
+ if (!targetModel.isEnableRealPositionDistance()) {
+ distanceText = "实时距离:未启用";
+ distanceColor = mUpdateReceiver.getColorRes(R.color.colorGrayText);
+ } else if (mCurrentGpsPosition == null) {
+ // 无GPS时持续发送“等待定位”(避免默认文本回落)
+ distanceText = "实时距离:等待GPS定位";
+ distanceColor = mUpdateReceiver.getColorRes(R.color.colorGrayText);
+ LogUtils.d(TAG, "位置ID=" + positionId + " 无GPS数据,发送等待提示");
+ } else {
+ try {
+ // 计算距离(复用PositionModel静态方法)
+ double distanceM = PositionModel.calculatePositionDistance(mCurrentGpsPosition, targetModel, false);
+ judgeTaskBingoStatus(targetModel, (int) distanceM);
+ // 格式化距离文本(Java7:显式类型转换,避免自动拆箱问题)
+ if (distanceM < 1000) {
+ distanceText = String.format("实时距离:%.1f 米", distanceM);
+ } else {
+ distanceText = String.format("实时距离:%.1f 千米", distanceM / 1000);
+ }
+ distanceColor = mUpdateReceiver.getColorRes(R.color.colorEnableGreen);
+ LogUtils.d(TAG, "位置ID=" + positionId + " 计算完成:" + distanceText);
+ } catch (IllegalArgumentException e) {
+ distanceText = "实时距离:数据无效";
+ distanceColor = mUpdateReceiver.getColorRes(R.color.colorRed);
+ LogUtils.e(TAG, "位置ID=" + positionId + " 计算异常:" + e.getMessage());
+ }
+ }
+
+ // 发送消息到UI线程
+ sendDistanceUpdateMessage(positionId, distanceText, distanceColor);
+ }
+ }
+
+ /**
+ * 检查任务触发状态并发送通知
+ * Java7:使用迭代器遍历任务列表(替代forEach Lambda)
+ */
+ private void checkAndTriggerTasks() {
+ if (mAllPositionTasks.isEmpty()) {
+ return;
+ }
+ // Java7:Iterator遍历ArrayList(替代forEach Lambda)
+ Iterator taskIterator = mAllPositionTasks.iterator();
+ while (taskIterator.hasNext()) {
+ PositionTaskModel task = taskIterator.next();
+ if (task.isBingo() && task.isEnable()) {
+ NotificationUtils.show(getApplicationContext(), task.getTaskId(), task.getPositionId(), task.getTaskDescription());
+ }
+ }
+ }
+
+ /**
+ * 判断任务触发状态(仅修改数据,UI更新由Adapter处理)
+ * Java7:迭代器遍历任务列表,显式状态判断
+ */
+ private void judgeTaskBingoStatus(PositionModel model, int distanceM) {
+ String targetPosId = model.getPositionId();
+ // Java7:Iterator遍历任务列表
+ Iterator taskIterator = mAllPositionTasks.iterator();
+ while (taskIterator.hasNext()) {
+ PositionTaskModel task = taskIterator.next();
+ if (targetPosId.equals(task.getPositionId())) {
+ boolean oldBingoState = task.isBingo();
+ boolean newBingoState = false;
+
+ // 根据任务条件判断新状态(Java7:if-else替代三元表达式,增强可读性)
+ if (task.isGreaterThan()) {
+ newBingoState = task.isEnable() && distanceM > task.getDiscussDistance();
+ } else if (task.isLessThan()) {
+ newBingoState = task.isEnable() && distanceM < task.getDiscussDistance();
+ } else {
+ newBingoState = task.isEnable();
+ }
+
+ // 更新任务状态(仅状态变化时更新)
+ if (newBingoState != oldBingoState) {
+ task.setIsBingo(newBingoState);
+ }
+ }
+ }
+ }
+
+ /**
+ * 发送距离更新消息(通过Handler发送到UI线程)
+ * Java7:显式创建Message,避免obtainMessage链式调用
+ */
+ private void sendDistanceUpdateMessage(String positionId, String distanceText, int distanceColor) {
+ Message msg = mMainHandler.obtainMessage(MSG_UPDATE_DISTANCE);
+ // Java7:显式调用put方法(替代链式put)
+ msg.getData().putString(KEY_POSITION_ID, positionId);
+ msg.getData().putString(KEY_DISTANCE_TEXT, distanceText);
+ msg.getData().putInt(KEY_DISTANCE_COLOR, distanceColor);
+ mMainHandler.sendMessage(msg);
+ LogUtils.d(TAG, "发送消息→位置ID=" + positionId + ",距离文本=" + distanceText);
+ }
+
+ /**
+ * 根据位置ID查找位置模型(工具方法)
+ * Java7:迭代器遍历位置列表(替代stream().filter())
+ */
+ private PositionModel findPositionModelById(String positionId) {
+ // Java7:Iterator遍历ArrayList
+ Iterator modelIterator = mPositionList.iterator();
+ while (modelIterator.hasNext()) {
+ PositionModel model = modelIterator.next();
+ if (positionId.equals(model.getPositionId())) {
+ return model;
+ }
+ }
+ return null;
+ }
+
+ // ---------------------- 对外API(供Activity/Adapter调用) ----------------------
+ public void setOnDistanceUpdateReceiver(OnDistanceUpdateReceiver receiver) {
+ this.mUpdateReceiver = receiver;
+ }
+
+ public void syncCurrentGpsPosition(PositionModel currentGpsPosition) {
+ this.mCurrentGpsPosition = currentGpsPosition;
+ LogUtils.d(TAG, "同步GPS位置:纬度=" + (currentGpsPosition != null ? currentGpsPosition.getLatitude() : 0.0f));
+ }
+
+ public void syncPositionList(ArrayList positionList) {
+ if (positionList != null) {
+ this.mPositionList.clear();
+ this.mPositionList.addAll(positionList);
+ LogUtils.d(TAG, "同步位置列表:数量=" + positionList.size());
+ }
+ }
+
+ public void syncAllPositionTasks(ArrayList allPositionTasks) {
+ if (allPositionTasks != null) {
+ this.mAllPositionTasks.clear();
+ this.mAllPositionTasks.addAll(allPositionTasks);
+ }
+ }
+
+ public void addVisibleDistanceView(String positionId) {
+ if (positionId != null && !mVisibleDistanceViewTags.containsKey(positionId)) {
+ mVisibleDistanceViewTags.put(positionId, 0); // 用0占位,仅标记存在
+ LogUtils.d(TAG, "添加可见位置ID:" + positionId + ",当前可见数量=" + mVisibleDistanceViewTags.size());
+ }
+ }
+
+ public void removeVisibleDistanceView(String positionId) {
+ if (positionId != null) {
+ mVisibleDistanceViewTags.remove(positionId);
+ LogUtils.d(TAG, "移除可见位置ID:" + positionId + ",当前可见数量=" + mVisibleDistanceViewTags.size());
+ }
+ }
+
+ public void clearVisibleDistanceViews() {
+ mVisibleDistanceViewTags.clear();
+ LogUtils.d(TAG, "清空所有可见位置ID");
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ Log.d(TAG, "DistanceRefreshService onDestroy - 定时器销毁");
+ // 销毁定时器,避免内存泄漏(Java7:显式判断非空)
+ if (mDistanceTimer != null) {
+ mDistanceTimer.cancel();
+ mDistanceTimer.purge();
+ }
+ // 清空数据,解除引用(避免内存泄漏)
+ mCurrentGpsPosition = null;
+ mPositionList.clear();
+ mAllPositionTasks.clear();
+ mVisibleDistanceViewTags.clear();
+ mUpdateReceiver = null;
+ }
+}
+
diff --git a/positions/src/main/res/layout/activity_main.xml b/positions/src/main/res/layout/activity_main.xml
index 97e0df26..09f11dcd 100644
--- a/positions/src/main/res/layout/activity_main.xml
+++ b/positions/src/main/res/layout/activity_main.xml
@@ -32,18 +32,11 @@
android:text="Positions"
android:onClick="onPositions"/>
-
-
-
-
-
+