diff --git a/positions/build.properties b/positions/build.properties index 93bf19e..9287ca4 100644 --- a/positions/build.properties +++ b/positions/build.properties @@ -1,8 +1,8 @@ #Created by .winboll/winboll_app_build.gradle -#Tue Mar 31 05:26:19 GMT 2026 +#Tue Mar 31 05:48:03 GMT 2026 stageCount=15 libraryProject= baseVersion=15.12 publishVersion=15.12.14 -buildCount=4 +buildCount=11 baseBetaVersion=15.12.15 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 f437a8b..94a51c9 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 @@ -230,7 +230,7 @@ public class LocationActivity extends WinBoLLActivity implements IWinBoLLActivit mPositionAdapter.setOnDeleteClickListener(new PositionAdapter.OnDeleteClickListener() { @Override public void onDeleteClick(final int position) { - YesNoAlertDialog.show(LocationActivity.this, "删除位置提示", "是否删除位置?", new YesNoAlertDialog.OnDialogResultListener(){ + YesNoAlertDialog.show(LocationActivity.this, "删除位置提示", "是否删除此项锚点位置?", new YesNoAlertDialog.OnDialogResultListener(){ @Override public void onNo() { 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 d324e87..1f98eac 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 @@ -1,19 +1,12 @@ package cc.winboll.studio.positions.adapters; /** - * @Author ZhanGSKen&豆包大模型 - * @Date 2025/09/29 20:25 * @Describe 位置数据适配器(修复视图复用资源加载,支持滚动后重新绑定数据,Java 7语法适配) + * @Author 豆包&ZhanGSKen + * @CreateTime 2025-09-29 20:25:00 + * @EditTime 2026-03-31 23:14:55 */ - -/** - * Java 7 语法严格适配 + 视图复用资源加载修复: - * 1. 保留无Lambda/弱引用/线程安全集合等原有适配 - * 2. 修复核心问题:移除 onViewDetachedFromWindow 中关键资源释放,改为 onBind 时重新绑定 - * 3. 强化资源复用:任务视图/距离控件在复用后自动从服务同步最新数据,确保滚动后数据不丢失 - * 4. 优化缓存逻辑:仅清理脱离屏幕且无效的控件缓存,有效控件保留引用供局部更新(如距离) - */ import android.content.Context; import android.text.TextUtils; import android.view.LayoutInflater; @@ -36,32 +29,32 @@ import java.util.ArrayList; import java.util.Iterator; import java.util.concurrent.ConcurrentHashMap; -public class PositionAdapter extends RecyclerView.Adapter implements MainService.TaskUpdateListener { +public class PositionAdapter extends RecyclerView.Adapter +implements MainService.TaskUpdateListener { + public static final String TAG = "PositionAdapter"; - // 视图类型常量(静态常量统一管理) + // 视图类型常量 private static final int VIEW_TYPE_SIMPLE = 0; private static final int VIEW_TYPE_EDIT = 1; - // 默认配置常量(避免魔法值) + // 默认配置常量 private static final String DEFAULT_MEMO = "无备注"; private static final String DEFAULT_TASK_DESC = "新任务"; - private static final int DEFAULT_TASK_DISTANCE = 50; // 单位:米 + private static final int DEFAULT_TASK_DISTANCE = 50; private static final String DISTANCE_FORMAT = "实时距离:%.1f 米"; private static final String DISTANCE_DISABLED = "实时距离:未启用"; private static final String DISTANCE_ERROR = "实时距离:计算失败"; - // 核心依赖(弱引用+线程安全集合,适配Java 7) + // 核心成员变量 private final Context mContext; - private final ArrayList mCachedPositionList; // 位置缓存(与MainService同步) - private final WeakReference mMainServiceRef; // 弱引用MainService,防内存泄漏 - // 控件缓存:位置ID → 对应任务列表视图(分别缓存简单/编辑模式,支持复用后快速同步) + private final ArrayList mCachedPositionList; + private final WeakReference mMainServiceRef; private final ConcurrentHashMap mSimpleTaskViewMap; private final ConcurrentHashMap mEditTaskViewMap; - // 距离控件缓存(用于局部更新距离UI,保留有效引用避免复用后更新失效) private final ConcurrentHashMap mPosDistanceViewMap; - // 回调接口(仅处理位置逻辑,任务逻辑通过PositionTaskListView+MainService完成) + // 回调接口 public interface OnDeleteClickListener { void onDeleteClick(int position); } @@ -74,32 +67,32 @@ public class PositionAdapter extends RecyclerView.Adapter cachedPositionList, MainService mainService) { + LogUtils.d(TAG, "PositionAdapter 构造函数开始,context=" + context + + ",cachedPositionList=" + (cachedPositionList != null ? cachedPositionList.size() : 0) + + ",mainService=" + mainService); + this.mContext = context; - // 容错处理:避免传入null导致空指针 this.mCachedPositionList = (cachedPositionList != null) ? cachedPositionList : new ArrayList(); - // 弱引用MainService:防止Adapter持有服务导致内存泄漏(Java 7 弱引用语法) this.mMainServiceRef = new WeakReference(mainService); - // 初始化控件缓存(线程安全集合,适配多线程更新场景) this.mSimpleTaskViewMap = new ConcurrentHashMap(); this.mEditTaskViewMap = new ConcurrentHashMap(); this.mPosDistanceViewMap = new ConcurrentHashMap(); - // 注册MainService任务监听:服务任务变化时同步刷新任务列表视图 if (mainService != null) { mainService.registerTaskUpdateListener(this); - LogUtils.d(TAG, "已注册MainService任务监听,确保任务数据与服务同步"); + LogUtils.d(TAG, "已注册 MainService 任务监听"); } else { - LogUtils.w(TAG, "构造函数:MainService为空,PositionTaskListView无法初始化"); + LogUtils.w(TAG, "构造函数:MainService 为空,无法初始化任务视图"); } - LogUtils.d(TAG, "Adapter初始化完成:位置数量=" + mCachedPositionList.size()); + LogUtils.d(TAG, "PositionAdapter 初始化完成,位置数量=" + mCachedPositionList.size()); } // ========================================================================= - // RecyclerView 核心方法(强化视图复用逻辑,复用后自动重新绑定资源) + // RecyclerView 核心方法 // ========================================================================= @Override public int getItemViewType(int position) { @@ -109,6 +102,7 @@ public class PositionAdapter extends RecyclerView.Adapter500米) - } - } - - /** - - - 根据索引获取位置模型(容错处理,避免越界/空指针——适配复用后索引变化场景) - */ - private PositionModel getPositionByIndex(int index) { - if (mCachedPositionList == null || index < 0 || index >= mCachedPositionList.size()) { - LogUtils.w(TAG, "获取位置模型失败:无效索引(" + index + ")或缓存为空"); - return null; - } - return mCachedPositionList.get(index); - } - - /** - - - 根据位置ID获取列表索引(用于精准刷新视图——适配复用后视图位置变化) - */ - private int getPositionIndexById(String positionId) { - if (TextUtils.isEmpty(positionId) || mCachedPositionList == null || mCachedPositionList.isEmpty()) { - LogUtils.w(TAG, "获取位置索引失败:参数无效(ID/缓存为空)"); - return -1; - }// Java 7 增强for循环遍历(替代Lambda,适配语法) - for (int i = 0; i < mCachedPositionList.size(); i++) { - PositionModel pos = mCachedPositionList.get(i); - if (positionId.equals(pos.getPositionId())) { - return i; - } - } - LogUtils.w(TAG, "获取位置索引失败:未找到ID=" + positionId); - return -1; - } - - /** - - - 局部更新距离UI(仅更新指定位置的距离,避免全量刷新卡顿——适配复用后控件缓存有效场景) - */ - public void updateSinglePositionDistance(String positionId) { - if (TextUtils.isEmpty(positionId) || mPosDistanceViewMap.isEmpty()) { - LogUtils.w(TAG, "局部更新距离失败:ID无效或无控件缓存(ID=" + positionId + ")"); - return; - } - // 1. 获取服务端最新位置数据(带重试,避免服务临时回收) - MainService mainService = getMainServiceWithRetry(2); - if (mainService == null) { - LogUtils.e(TAG, "局部更新距离失败:无法获取MainService"); - return; - } - PositionModel latestPos = null; - try { - ArrayList servicePosList = mainService.getPositionList(); - if (servicePosList != null && !servicePosList.isEmpty()) { - // Java 7 迭代器遍历,避免ConcurrentModificationException - Iterator iter = servicePosList.iterator(); - while (iter.hasNext()) { - PositionModel pos = (PositionModel)iter.next(); - if (positionId.equals(pos.getPositionId())) { - latestPos = pos; - break; - } + taskView.setOnTaskUpdatedListener(new PositionTaskListView.OnTaskUpdatedListener() { + @Override + public void onTaskUpdated(String positionId, ArrayList updatedTasks) { + LogUtils.d(TAG, "编辑模式任务更新 posId=" + positionId + " 任务数=" + updatedTasks.size()); } - } - } catch (Exception e) { - LogUtils.d(TAG, "获取最新位置数据失败(ID=" + positionId + ")" + e); - return; - }if (latestPos == null) { - LogUtils.w(TAG, "局部更新距离失败:未找到位置ID=" + positionId); - return; - } - // 2. 更新距离控件(确保主线程操作,避免跨线程异常——适配复用后控件已重新绑定) - final TextView distanceView = mPosDistanceViewMap.get(positionId); - if (distanceView != null && distanceView.isAttachedToWindow()) { - final PositionModel finalLatestPos = latestPos; - distanceView.post(new Runnable() { + }); + } + + // ========================================================================= + // 工具方法 + // ========================================================================= + private void updateDistanceDisplay(TextView distanceView, PositionModel posModel) { + if (distanceView == null || posModel == null) { + LogUtils.w(TAG, "updateDistanceDisplay:参数为空"); + return; + } + if (!posModel.isEnableRealPositionDistance()) { + distanceView.setText(DISTANCE_DISABLED); + distanceView.setTextColor(mContext.getResources().getColor(R.color.gray)); + return; + } + double distance = posModel.getRealPositionDistance(); + if (distance < 0) { + distanceView.setText(DISTANCE_ERROR); + distanceView.setTextColor(mContext.getResources().getColor(R.color.red)); + return; + } + distanceView.setText(String.format(DISTANCE_FORMAT, distance)); + if (distance <= 100) { + distanceView.setTextColor(mContext.getResources().getColor(R.color.green)); + } else if (distance <= 500) { + distanceView.setTextColor(mContext.getResources().getColor(R.color.yellow)); + } else { + distanceView.setTextColor(mContext.getResources().getColor(R.color.red)); + } + } + + private PositionModel getPositionByIndex(int index) { + if (mCachedPositionList == null || index < 0 || index >= mCachedPositionList.size()) { + LogUtils.w(TAG, "getPositionByIndex:无效索引 index=" + index); + return null; + } + return mCachedPositionList.get(index); + } + + private int getPositionIndexById(String positionId) { + if (TextUtils.isEmpty(positionId) || mCachedPositionList == null || mCachedPositionList.isEmpty()) { + LogUtils.w(TAG, "getPositionIndexById:参数无效"); + return -1; + } + for (int i = 0; i < mCachedPositionList.size(); i++) { + PositionModel pos = mCachedPositionList.get(i); + if (positionId.equals(pos.getPositionId())) { + return i; + } + } + LogUtils.w(TAG, "未找到位置ID=" + positionId); + return -1; + } + + public void updateSinglePositionDistance(String positionId) { + LogUtils.d(TAG, "updateSinglePositionDistance posId=" + positionId); + if (TextUtils.isEmpty(positionId) || mPosDistanceViewMap.isEmpty()) { + return; + } + MainService mainService = getMainServiceWithRetry(2); + if (mainService == null) { + LogUtils.e(TAG, "无法获取 MainService"); + return; + } + PositionModel latestPos = null; + try { + ArrayList servicePosList = mainService.getPositionList(); + if (servicePosList != null && !servicePosList.isEmpty()) { + Iterator iter = servicePosList.iterator(); + while (iter.hasNext()) { + PositionModel pos = (PositionModel) iter.next(); + if (positionId.equals(pos.getPositionId())) { + latestPos = pos; + break; + } + } + } + } catch (Exception e) { + LogUtils.e(TAG, "获取位置数据异常", e); + return; + } + if (latestPos == null) { + LogUtils.w(TAG, "未找到位置 posId=" + positionId); + return; + } + final TextView distanceView = mPosDistanceViewMap.get(positionId); + if (distanceView != null && distanceView.isAttachedToWindow()) { + final PositionModel finalLatestPos = latestPos; + distanceView.post(new Runnable() { @Override public void run() { updateDistanceDisplay(distanceView, finalLatestPos); } }); - LogUtils.d(TAG, "局部更新距离完成:位置ID=" + positionId + ",距离=" + latestPos.getRealPositionDistance() + "米"); - } else { - mPosDistanceViewMap.remove(positionId); // 移除无效控件缓存(如视图已被销毁) - LogUtils.w(TAG, "局部更新距离失败:控件已回收/脱离视图树(ID=" + positionId + ")"); - } - } + } else { + mPosDistanceViewMap.remove(positionId); + } + } - /** - - 全量更新位置数据(从服务同步最新数据,过滤无效/重复项——适配复用后全量刷新场景) - */ - public void updateAllPositionData(ArrayList newPosList) { - if (newPosList == null) { - LogUtils.w(TAG, "全量更新位置数据失败:新列表为空"); - return; - } - // 1. 过滤无效位置(校验核心字段:ID/经纬度合法) - ArrayList validPosList = new ArrayList(); - for (PositionModel pos : newPosList) { - if (TextUtils.isEmpty(pos.getPositionId()) + public void updateAllPositionData(ArrayList newPosList) { + LogUtils.d(TAG, "updateAllPositionData 新数据数量=" + (newPosList != null ? newPosList.size() : 0)); + if (newPosList == null) { + return; + } + ArrayList validPosList = new ArrayList(); + for (PositionModel pos : newPosList) { + if (TextUtils.isEmpty(pos.getPositionId()) || pos.getLongitude() < -180 || pos.getLongitude() > 180 || pos.getLatitude() < -90 || pos.getLatitude() > 90) { - LogUtils.w(TAG, "过滤无效位置:ID=" + pos.getPositionId() + "(经纬度/ID非法)"); - continue; - } - validPosList.add(pos); - } - // 2. 去重(按位置ID去重,保留服务端最新数据) - ConcurrentHashMap uniquePosMap = new ConcurrentHashMap(); - for (PositionModel pos : validPosList) { - uniquePosMap.put(pos.getPositionId(), pos); // 相同ID覆盖,保留最新 - } - ArrayList uniquePosList = new ArrayList(uniquePosMap.values());// 3. 同步到本地缓存+刷新UI(刷新后触发onBind,自动重新绑定资源) - this.mCachedPositionList.clear(); - this.mCachedPositionList.addAll(uniquePosList); - // 清空旧控件缓存(避免引用失效数据,刷新后重新缓存新控件) - mPosDistanceViewMap.clear(); - mSimpleTaskViewMap.clear(); - mEditTaskViewMap.clear(); - notifyDataSetChanged(); - LogUtils.d(TAG, "全量更新位置数据完成:原数量=" + newPosList.size() + ",过滤后=" + uniquePosList.size()); - } + continue; + } + validPosList.add(pos); + } + ConcurrentHashMap uniquePosMap = new ConcurrentHashMap(); + for (PositionModel pos : validPosList) { + uniquePosMap.put(pos.getPositionId(), pos); + } + ArrayList uniquePosList = new ArrayList(uniquePosMap.values()); - /** - - 隐藏软键盘(编辑完成后调用,提升用户体验——适配复用后软键盘残留问题) - */ - private void hideSoftKeyboard(View view) { - if (mContext == null || view == null) { - LogUtils.w(TAG, "隐藏软键盘失败:参数为空(上下文/视图)"); - return; - }InputMethodManager imm = (InputMethodManager) mContext.getSystemService(Context.INPUT_METHOD_SERVICE); - if (imm != null) { - imm.hideSoftInputFromWindow(view.getWindowToken(), 0); // 强制隐藏 - } - } + mCachedPositionList.clear(); + mCachedPositionList.addAll(uniquePosList); + mPosDistanceViewMap.clear(); + mSimpleTaskViewMap.clear(); + mEditTaskViewMap.clear(); + notifyDataSetChanged(); - /** - - 带重试的服务获取(解决弱引用临时回收问题,最多重试2次——适配复用后服务引用失效场景) - */ - private MainService getMainServiceWithRetry(int retryCount) { - MainService mainService = mMainServiceRef.get(); - if (mainService != null) { - return mainService; - } - // 重试逻辑:每次间隔100ms,避免频繁重试 - for (int i = 0; i < retryCount; i++) { - try { - Thread.sleep(100); - mainService = mMainServiceRef.get(); - if (mainService != null) { - LogUtils.d(TAG, "重试获取MainService成功(第" + (i + 1) + "次)"); - return mainService; - } - } catch (InterruptedException e) { - LogUtils.d(TAG, "重试获取服务时线程被中断" + e); - Thread.currentThread().interrupt(); // 恢复中断状态 - break; - } - }LogUtils.e(TAG, "重试" + retryCount + "次后仍未获取到MainService"); - return null; - } + LogUtils.d(TAG, "全量更新完成,有效数量=" + uniquePosList.size()); + } - // ========================================================================= - // 实现 MainService.TaskUpdateListener 接口(服务任务变化时回调——适配复用后任务同步) - // ========================================================================= - @Override - public void onTaskUpdated() { - LogUtils.d(TAG, "收到MainService任务更新通知,同步所有任务列表视图(含复用视图)"); + private void hideSoftKeyboard(View view) { + if (mContext == null || view == null) { + return; + } + InputMethodManager imm = (InputMethodManager) mContext.getSystemService(Context.INPUT_METHOD_SERVICE); + if (imm != null) { + imm.hideSoftInputFromWindow(view.getWindowToken(), 0); + } + } - // 1. 同步简单模式任务视图(仅显示已启用任务——适配复用后视图已重新缓存) - if (!mSimpleTaskViewMap.isEmpty()) { - Iterator> iter = mSimpleTaskViewMap.entrySet().iterator(); - while (iter.hasNext()) { - ConcurrentHashMap.Entry entry = iter.next(); - PositionTaskListView taskView = entry.getValue(); - if (taskView != null && taskView.isAttachedToWindow()) { - taskView.syncTasksFromMainService(); // 从服务同步最新任务(复用后也能更新) - } else { - iter.remove(); // 移除无效视图缓存(如视图已脱离屏幕且未复用) - } - } - } + private MainService getMainServiceWithRetry(int retryCount) { + MainService mainService = mMainServiceRef.get(); + if (mainService != null) { + return mainService; + } + for (int i = 0; i < retryCount; i++) { + try { + Thread.sleep(100); + mainService = mMainServiceRef.get(); + if (mainService != null) { + LogUtils.d(TAG, "重试获取 MainService 成功,第" + (i + 1) + "次"); + return mainService; + } + } catch (InterruptedException e) { + LogUtils.e(TAG, "重试被中断", e); + Thread.currentThread().interrupt(); + break; + } + } + LogUtils.e(TAG, "重试" + retryCount + "次仍未获取到 MainService"); + return null; + } - // 2. 同步编辑模式任务视图(支持编辑的全量任务——适配复用后视图已重新缓存) - if (!mEditTaskViewMap.isEmpty()) { - Iterator> iter = mEditTaskViewMap.entrySet().iterator(); - while (iter.hasNext()) { - ConcurrentHashMap.Entry entry = iter.next(); - PositionTaskListView taskView = entry.getValue(); - if (taskView != null && taskView.isAttachedToWindow()) { - taskView.syncTasksFromMainService(); // 从服务同步最新任务(复用后也能更新) - } else { - iter.remove(); // 移除无效视图缓存 - } - } - } - } + // ========================================================================= + // 任务更新监听 + // ========================================================================= + @Override + public void onTaskUpdated() { + LogUtils.d(TAG, "onTaskUpdated:收到服务任务更新"); + if (!mSimpleTaskViewMap.isEmpty()) { + Iterator> iter = mSimpleTaskViewMap.entrySet().iterator(); + while (iter.hasNext()) { + ConcurrentHashMap.Entry entry = iter.next(); + PositionTaskListView taskView = entry.getValue(); + if (taskView != null && taskView.isAttachedToWindow()) { + taskView.syncTasksFromMainService(); + } else { + iter.remove(); + } + } + } + if (!mEditTaskViewMap.isEmpty()) { + Iterator> iter = mEditTaskViewMap.entrySet().iterator(); + while (iter.hasNext()) { + ConcurrentHashMap.Entry entry = iter.next(); + PositionTaskListView taskView = entry.getValue(); + if (taskView != null && taskView.isAttachedToWindow()) { + taskView.syncTasksFromMainService(); + } else { + iter.remove(); + } + } + } + } - // ========================================================================= - // 回调设置方法(供Activity绑定交互逻辑——适配复用后回调不失效) - // ========================================================================= - public void setOnDeleteClickListener(OnDeleteClickListener listener) { - this.mOnDeleteListener = listener; - } + // ========================================================================= + // 外部回调设置 + // ========================================================================= + public void setOnDeleteClickListener(OnDeleteClickListener listener) { + LogUtils.d(TAG, "setOnDeleteClickListener listener=" + listener); + this.mOnDeleteListener = listener; + } - public void setOnSavePositionClickListener(OnSavePositionClickListener listener) { - this.mOnSavePosListener = listener; - } + public void setOnSavePositionClickListener(OnSavePositionClickListener listener) { + LogUtils.d(TAG, "setOnSavePositionClickListener listener=" + listener); + this.mOnSavePosListener = listener; + } - // ========================================================================= - // 资源释放(Activity销毁时调用,彻底释放引用,避免内存泄漏——保留原有安全逻辑) - // ========================================================================= - public void release() { - // 1. 反注册MainService任务监听(解除服务绑定) - MainService mainService = mMainServiceRef.get(); - if (mainService != null) { - mainService.unregisterTaskUpdateListener(this); - LogUtils.d(TAG, "已反注册MainService任务监听"); - } + // ========================================================================= + // 资源释放 + // ========================================================================= + public void release() { + LogUtils.d(TAG, "release:开始释放 Adapter 资源"); + MainService mainService = mMainServiceRef.get(); + if (mainService != null) { + mainService.unregisterTaskUpdateListener(this); + LogUtils.d(TAG, "已反注册任务监听"); + } - // 2. 释放简单模式任务视图资源(清空数据+解绑监听——仅在Activity销毁时执行,不复用场景) - if (!mSimpleTaskViewMap.isEmpty()) { - Iterator> iter = mSimpleTaskViewMap.entrySet().iterator(); - while (iter.hasNext()) { - PositionTaskListView taskView = iter.next().getValue(); - if (taskView != null) { - taskView.clearData(); - taskView.setOnTaskUpdatedListener(null); - } - iter.remove(); - } - } + if (!mSimpleTaskViewMap.isEmpty()) { + Iterator> iter = mSimpleTaskViewMap.entrySet().iterator(); + while (iter.hasNext()) { + PositionTaskListView taskView = iter.next().getValue(); + if (taskView != null) { + taskView.clearData(); + taskView.setOnTaskUpdatedListener(null); + } + iter.remove(); + } + } - // 3. 释放编辑模式任务视图资源(清空数据+解绑监听——仅在Activity销毁时执行) - if (!mEditTaskViewMap.isEmpty()) { - Iterator> iter = mEditTaskViewMap.entrySet().iterator(); - while (iter.hasNext()) { - PositionTaskListView taskView = iter.next().getValue(); - if (taskView != null) { - taskView.clearData(); - taskView.setOnTaskUpdatedListener(null); - } - iter.remove(); - } - } + if (!mEditTaskViewMap.isEmpty()) { + Iterator> iter = mEditTaskViewMap.entrySet().iterator(); + while (iter.hasNext()) { + PositionTaskListView taskView = iter.next().getValue(); + if (taskView != null) { + taskView.clearData(); + taskView.setOnTaskUpdatedListener(null); + } + iter.remove(); + } + } - // 4. 清空其他缓存+置空引用(彻底释放,避免内存泄漏) - mPosDistanceViewMap.clear(); - if (mCachedPositionList != null) { - mCachedPositionList.clear(); - } - mOnDeleteListener = null; - mOnSavePosListener = null; + mPosDistanceViewMap.clear(); + if (mCachedPositionList != null) { + mCachedPositionList.clear(); + } + mOnDeleteListener = null; + mOnSavePosListener = null; - LogUtils.d(TAG, "Adapter资源已完全释放(任务视图资源释放+缓存清空+引用置空)"); - } + LogUtils.d(TAG, "release:资源释放完成"); + } - // ========================================================================= - // 静态内部类:ViewHolder(与布局严格对应,避免外部引用导致内存泄漏) - // ========================================================================= - /** - - 简单模式ViewHolder(对应 item_position_simple.xml,含简单模式任务列表视图) - */ - public static class SimpleViewHolder extends RecyclerView.ViewHolder { - TextView tvSimpleLon; // 经度显示 - TextView tvSimpleLat; // 纬度显示 - TextView tvSimpleMemo; // 备注显示 - TextView tvSimpleDistance; // 距离显示 - PositionTaskListView ptlvSimpleTasks; // 简单模式任务列表视图 - public SimpleViewHolder(View itemView) { - super(itemView); - // 绑定布局控件(与XML中ID严格一致,避免运行时空指针) - tvSimpleLon = (TextView) itemView.findViewById(R.id.tv_simple_longitude); - tvSimpleLat = (TextView) itemView.findViewById(R.id.tv_simple_latitude); - tvSimpleMemo = (TextView) itemView.findViewById(R.id.tv_simple_memo); - tvSimpleDistance = (TextView) itemView.findViewById(R.id.tv_simple_distance); - ptlvSimpleTasks = (PositionTaskListView) itemView.findViewById(R.id.ptlv_simple_tasks); - } - } + // ========================================================================= + // ViewHolder + // ========================================================================= + public static class SimpleViewHolder extends RecyclerView.ViewHolder { + TextView tvSimpleLon; + TextView tvSimpleLat; + TextView tvSimpleMemo; + TextView tvSimpleDistance; + PositionTaskListView ptlvSimpleTasks; - /** - - 编辑模式ViewHolder(对应 item_position_edit.xml,含编辑模式任务列表视图) - */ - public static class EditViewHolder extends RecyclerView.ViewHolder { - TextView tvEditLon; // 经度显示(不可编辑) - TextView tvEditLat; // 纬度显示(不可编辑) - EditText etEditMemo; // 备注编辑 - TextView tvEditDistance; // 距离显示 - RadioGroup rgDistanceSwitch; // 距离启用/禁用开关 - Button btnCancel; // 取消编辑按钮 - Button btnDelete; // 删除位置按钮 - Button btnSave; // 保存位置按钮 - Button btnAddTask; // 新增任务按钮 - TextView tvTaskCount; // 任务数量显示(兼容旧布局,可隐藏) - PositionTaskListView ptlvEditTasks; // 编辑模式任务列表视图 - public EditViewHolder(View itemView) { - super(itemView); - // 绑定布局控件(与XML中ID严格一致,避免运行时空指针) - tvEditLon = (TextView) itemView.findViewById(R.id.tv_edit_longitude); - tvEditLat = (TextView) itemView.findViewById(R.id.tv_edit_latitude); - etEditMemo = (EditText) itemView.findViewById(R.id.et_edit_memo); - tvEditDistance = (TextView) itemView.findViewById(R.id.tv_edit_distance); - rgDistanceSwitch = (RadioGroup) itemView.findViewById(R.id.rg_distance_switch); - btnCancel = (Button) itemView.findViewById(R.id.btn_edit_cancel); - btnDelete = (Button) itemView.findViewById(R.id.btn_edit_delete); - btnSave = (Button) itemView.findViewById(R.id.btn_edit_save); - btnAddTask = (Button) itemView.findViewById(R.id.btn_add_task); - tvTaskCount = (TextView) itemView.findViewById(R.id.tv_task_count); - ptlvEditTasks = (PositionTaskListView) itemView.findViewById(R.id.ptlv_edit_tasks); - } - } + public SimpleViewHolder(View itemView) { + super(itemView); + tvSimpleLon = (TextView) itemView.findViewById(R.id.tv_simple_longitude); + tvSimpleLat = (TextView) itemView.findViewById(R.id.tv_simple_latitude); + tvSimpleMemo = (TextView) itemView.findViewById(R.id.tv_simple_memo); + tvSimpleDistance = (TextView) itemView.findViewById(R.id.tv_simple_distance); + ptlvSimpleTasks = (PositionTaskListView) itemView.findViewById(R.id.ptlv_simple_tasks); + } + } - // ========================================================================= - // 补充:PositionTaskListView 必要方法适配(确保视图类已实现以下基础方法) - // (若视图类未实现,需在 PositionTaskListView 中添加对应逻辑,否则复用后功能异常) - // ========================================================================= - public static class PositionTaskListViewRequiredMethods { - /** - 1. - init 方法:初始化任务视图(绑定服务+位置ID——复用后重新绑定,确保服务引用有效) - - 需在 PositionTaskListView 中添加: - - public void init(MainService mainService, String positionId) { - - this.mMainServiceRef = new WeakReference(mainService); // 弱引用服务,防泄漏 - - this.mPositionId = positionId; // 绑定当前位置ID(复用后更新为当前位置ID) - - } - */ + public static class EditViewHolder extends RecyclerView.ViewHolder { + TextView tvEditLon; + TextView tvEditLat; + EditText etEditMemo; + TextView tvEditDistance; + RadioGroup rgDistanceSwitch; + Button btnCancel; + Button btnDelete; + Button btnSave; + Button btnAddTask; + TextView tvTaskCount; + PositionTaskListView ptlvEditTasks; - /** - 2. - setViewStatus 方法:设置视图模式(简单/编辑——复用后恢复对应模式UI) - - 需在 PositionTaskListView 中添加: - - public static final int VIEW_MODE_SIMPLE = 0; // 仅显示(隐藏编辑/删除按钮) - - public static final int VIEW_MODE_EDIT = 1; // 可编辑(显示编辑/删除按钮) - - private int mViewMode; - - public void setViewStatus(int viewMode) { - - this.mViewMode = viewMode; - - // 根据模式控制按钮显示:简单模式隐藏编辑按钮,编辑模式显示 - - if (mEditBtn != null) mEditBtn.setVisibility(viewMode == VIEW_MODE_EDIT ? View.VISIBLE : View.GONE); - - if (mDeleteBtn != null) mDeleteBtn.setVisibility(viewMode == VIEW_MODE_EDIT ? View.VISIBLE : View.GONE); - - } - */ + public EditViewHolder(View itemView) { + super(itemView); + tvEditLon = (TextView) itemView.findViewById(R.id.tv_edit_longitude); + tvEditLat = (TextView) itemView.findViewById(R.id.tv_edit_latitude); + etEditMemo = (EditText) itemView.findViewById(R.id.et_edit_memo); + tvEditDistance = (TextView) itemView.findViewById(R.id.tv_edit_distance); + rgDistanceSwitch = (RadioGroup) itemView.findViewById(R.id.rg_distance_switch); + btnCancel = (Button) itemView.findViewById(R.id.btn_edit_cancel); + btnDelete = (Button) itemView.findViewById(R.id.btn_edit_delete); + btnSave = (Button) itemView.findViewById(R.id.btn_edit_save); + btnAddTask = (Button) itemView.findViewById(R.id.btn_add_task); + tvTaskCount = (TextView) itemView.findViewById(R.id.tv_task_count); + ptlvEditTasks = (PositionTaskListView) itemView.findViewById(R.id.ptlv_edit_tasks); + } + } - /** - 3. - syncTasksFromMainService 方法:从服务同步任务数据(复用后核心方法,避免旧数据) - - 需在 PositionTaskListView 中添加: - - public void syncTasksFromMainService() { - - MainService mainService = mMainServiceRef.get(); - - if (mainService == null || TextUtils.isEmpty(mPositionId)) return; - - // 根据视图模式拉取对应任务:简单模式仅拉已启用,编辑模式拉全部 - - ArrayList tasks = (mViewMode == VIEW_MODE_SIMPLE) - - ? mainService.getEnabledTasksByPositionId(mPositionId) - - : mainService.getTasksByPositionId(mPositionId); - - // 更新列表数据(刷新UI,确保复用后显示最新任务) - - if (mTaskAdapter != null) { - - mTaskAdapter.setTaskList(tasks); - - mTaskAdapter.notifyDataSetChanged(); - - } - - // 通知外部任务更新(如Adapter需要联动) - - if (mTaskUpdateListener != null) mTaskUpdateListener.onTaskUpdated(mPositionId, tasks); - - } - */ - - /** - 4. - addNewTask 方法:新增任务(同步服务+刷新列表——复用后功能正常) - - 需在 PositionTaskListView 中添加: - - public void addNewTask(PositionTaskModel newTask) { - - if (newTask == null || TextUtils.isEmpty(newTask.getPositionId())) return; - - MainService mainService = mMainServiceRef.get(); - - if (mainService != null) { - - mainService.addTask(newTask); // 同步到服务(确保数据持久化) - - syncTasksFromMainService(); // 新增后立即同步,刷新列表 - - Toast.makeText(getContext(), "任务新增成功", Toast.LENGTH_SHORT).show(); - - } else { - - Toast.makeText(getContext(), "服务未就绪,新增失败", Toast.LENGTH_SHORT).show(); - - } - - } - */ - - /** - 5. - clearData 方法:清空任务数据(仅在Activity销毁时调用,不复用场景) - - 需在 PositionTaskListView 中添加: - - public void clearData() { - - if (mTaskList != null) mTaskList.clear(); // 清空本地任务列表 - - if (mTaskAdapter != null) mTaskAdapter.notifyDataSetChanged(); // 刷新空UI - - } - */ - - /** - 6. - setOnTaskUpdatedListener 方法:设置任务更新回调(复用后重新绑定,避免回调失效) - - 需在 PositionTaskListView 中添加: - - public interface OnTaskUpdatedListener { - - void onTaskUpdated(String positionId, ArrayList updatedTasks); - - } - - private OnTaskUpdatedListener mTaskUpdateListener; - - public void setOnTaskUpdatedListener(OnTaskUpdatedListener listener) { - - this.mTaskUpdateListener = listener; - - } - */ - } + // ========================================================================= + // 方法说明类(保留不动) + // ========================================================================= + public static class PositionTaskListViewRequiredMethods { + } } + diff --git a/positions/src/main/java/cc/winboll/studio/positions/services/MainService.java b/positions/src/main/java/cc/winboll/studio/positions/services/MainService.java index 59b4bc0..9d39fb0 100644 --- a/positions/src/main/java/cc/winboll/studio/positions/services/MainService.java +++ b/positions/src/main/java/cc/winboll/studio/positions/services/MainService.java @@ -230,24 +230,24 @@ public class MainService extends Service { * 删除任务(Adapter调用,通过迭代器安全删除,避免并发异常) * @param taskId 待删除任务的ID */ - public void deleteTask(String taskId) { + public void deleteTask(final String taskId) { if (TextUtils.isEmpty(taskId) || mAllTasks.isEmpty()) { LogUtils.w(TAG, "deletePositionTask:任务ID为空或列表为空,删除失败"); return; } - // 迭代器删除(Java 7 唯一安全删除集合元素的方式) - Iterator taskIter = mAllTasks.iterator(); - while (taskIter.hasNext()) { - PositionTaskModel task = taskIter.next(); - if (taskId.equals(task.getTaskId())) { - taskIter.remove(); // 迭代器安全删除,无ConcurrentModificationException - saveAllTasks(); - notifyTaskUpdated(); - LogUtils.d(TAG, "deletePositionTask:成功(任务ID=" + taskId + ")"); - break; - } - } + // 迭代器删除(Java 7 唯一安全删除集合元素的方式) + Iterator taskIter = mAllTasks.iterator(); + while (taskIter.hasNext()) { + PositionTaskModel task = taskIter.next(); + if (taskId.equals(task.getTaskId())) { + taskIter.remove(); // 迭代器安全删除,无ConcurrentModificationException + saveAllTasks(); + notifyTaskUpdated(); + LogUtils.d(TAG, "deletePositionTask:成功(任务ID=" + taskId + ")"); + break; + } + } } /** diff --git a/positions/src/main/java/cc/winboll/studio/positions/views/PositionTaskListView.java b/positions/src/main/java/cc/winboll/studio/positions/views/PositionTaskListView.java index 2345b6b..dec2102 100644 --- a/positions/src/main/java/cc/winboll/studio/positions/views/PositionTaskListView.java +++ b/positions/src/main/java/cc/winboll/studio/positions/views/PositionTaskListView.java @@ -23,6 +23,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; +import cc.winboll.studio.libaes.dialogs.YesNoAlertDialog; import cc.winboll.studio.libappbase.LogUtils; import cc.winboll.studio.positions.R; import cc.winboll.studio.positions.models.PositionTaskModel; @@ -354,28 +355,38 @@ public class PositionTaskListView extends LinearLayout { holder.btnDeleteTask.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - LogUtils.d(TAG, "删除按钮点击 position=" + position + " taskId=" + task.getTaskId()); - if (mMainService == null) { - showToast("删除失败:服务未就绪"); - LogUtils.e(TAG, "删除失败:MainService为空"); - return; - } - try { - mMainService.deleteTask(task.getTaskId()); - LogUtils.d(TAG, "服务删除任务成功 taskId=" + task.getTaskId()); - notifyItemRemoved(position); - notifyItemRangeChanged(position, mAdapterData.size()); + YesNoAlertDialog.show(getContext(), "删除任务提示", "是否删除此项位置任务?", new YesNoAlertDialog.OnDialogResultListener(){ + @Override + public void onNo() { + } - if (mOnTaskUpdatedListener != null && mBindPositionId != null) { - mOnTaskUpdatedListener.onTaskUpdated(mBindPositionId, new ArrayList<>(mAdapterData)); - } - showToast("任务已删除"); + @Override + public void onYes() { + LogUtils.d(TAG, "删除按钮点击 position=" + position + " taskId=" + task.getTaskId()); + if (mMainService == null) { + showToast("删除失败:服务未就绪"); + LogUtils.e(TAG, "删除失败:MainService为空"); + return; + } + try { + mMainService.deleteTask(task.getTaskId()); + LogUtils.d(TAG, "服务删除任务成功 taskId=" + task.getTaskId()); + notifyItemRemoved(position); + notifyItemRangeChanged(position, mAdapterData.size()); - } catch (Exception e) { - LogUtils.e(TAG, "删除异常: " + e.getMessage(), e); - showToast("删除失败,请重试"); - syncTasksFromMainService(); - } + if (mOnTaskUpdatedListener != null && mBindPositionId != null) { + mOnTaskUpdatedListener.onTaskUpdated(mBindPositionId, new ArrayList<>(mAdapterData)); + } + showToast("任务已删除"); + + } catch (Exception e) { + LogUtils.e(TAG, "删除异常: " + e.getMessage(), e); + showToast("删除失败,请重试"); + syncTasksFromMainService(); + } + } + + }); } });