任务触发响应模块已基本完成

This commit is contained in:
ZhanGSKen
2025-09-30 16:03:16 +08:00
parent 12f4fd3f45
commit edaf7007d6
9 changed files with 282 additions and 289 deletions

View File

@@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle #Created by .winboll/winboll_app_build.gradle
#Tue Sep 30 06:43:35 GMT 2025 #Tue Sep 30 08:01:34 GMT 2025
stageCount=3 stageCount=3
libraryProject= libraryProject=
baseVersion=15.0 baseVersion=15.0
publishVersion=15.0.2 publishVersion=15.0.2
buildCount=39 buildCount=47
baseBetaVersion=15.0.3 baseBetaVersion=15.0.3

View File

@@ -3,7 +3,7 @@ package cc.winboll.studio.positions.adapters;
/** /**
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com> * @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
* @Date 2025/09/29 20:25 * @Date 2025/09/29 20:25
* @Describe 位置数据适配器(修复定时器:仅刷新实时距离,不干扰编辑中的任务) * @Describe 位置数据适配器(修复定时器:仅刷新实时距离,不干扰编辑中的任务;优化任务触发通知
*/ */
import android.content.Context; import android.content.Context;
import android.os.Handler; import android.os.Handler;
@@ -42,7 +42,7 @@ public class PositionAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
private static final String DEFAULT_TASK_DESC = "新位置任务"; private static final String DEFAULT_TASK_DESC = "新位置任务";
private static final int DEFAULT_TASK_DISTANCE = 100; private static final int DEFAULT_TASK_DISTANCE = 100;
// 核心成员变量(新增:用于存储可见的距离TextView避免遍历所有ViewHolder // 核心成员变量(新增:1.位置ID→索引映射 2.主线程Handler 3.任务触发标记
private final ArrayList<PositionModel> mPositionList; private final ArrayList<PositionModel> mPositionList;
private final ArrayList<PositionTaskModel> mAllPositionTasks; private final ArrayList<PositionTaskModel> mAllPositionTasks;
private final Context mContext; private final Context mContext;
@@ -50,8 +50,11 @@ public class PositionAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
private final Timer mDistanceTimer; private final Timer mDistanceTimer;
private final Handler mMainHandler; private final Handler mMainHandler;
private final Map<String, ArrayList<PositionTaskModel>> mPositionTaskMap; private final Map<String, ArrayList<PositionTaskModel>> mPositionTaskMap;
// 关键新增存储“位置ID → 距离TextView”的映射仅包含当前可见的列表项
private final Map<String, TextView> mVisibleDistanceViews = new HashMap<>(); private final Map<String, TextView> mVisibleDistanceViews = new HashMap<>();
// 新增1存储“位置ID→列表索引”映射快速定位位置项避免遍历
private final Map<String, Integer> mPositionIdToIndexMap = new HashMap<>();
// 新增2主线程Handler确保UI更新在主线程避免异常
private final Handler mUiHandler = new Handler(Looper.getMainLooper());
// 接口回调(不变) // 接口回调(不变)
public interface OnDeleteClickListener { public interface OnDeleteClickListener {
@@ -70,7 +73,7 @@ public class PositionAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
private OnSavePositionClickListener mOnSavePositionClickListener; private OnSavePositionClickListener mOnSavePositionClickListener;
private OnSavePositionTaskClickListener mOnSavePositionTaskClickListener; private OnSavePositionTaskClickListener mOnSavePositionTaskClickListener;
// 构造函数(不变,仅定时器初始化逻辑修改 // 构造函数(新增初始化位置ID→索引映射
public PositionAdapter(Context context, ArrayList<PositionModel> positionList, ArrayList<PositionTaskModel> allPositionTasks) { public PositionAdapter(Context context, ArrayList<PositionModel> positionList, ArrayList<PositionTaskModel> allPositionTasks) {
this.mContext = context; this.mContext = context;
this.mPositionList = (positionList != null) ? positionList : new ArrayList<PositionModel>(); this.mPositionList = (positionList != null) ? positionList : new ArrayList<PositionModel>();
@@ -89,9 +92,11 @@ public class PositionAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
} }
} }
mPositionTaskMap.put(validPosId, matchedTasks); mPositionTaskMap.put(validPosId, matchedTasks);
// 新增初始化“位置ID→索引”映射索引即列表中的位置
mPositionIdToIndexMap.put(validPosId, mPositionList.indexOf(model));
} }
// 启动距离刷新定时器(核心修改:调用新的刷新逻辑 // 启动距离刷新定时器(不变
startDistanceRefreshTimer(); startDistanceRefreshTimer();
} }
@@ -119,28 +124,21 @@ public class PositionAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
mDistanceTimer.cancel(); mDistanceTimer.cancel();
mDistanceTimer.purge(); mDistanceTimer.purge();
} }
// 清空可见距离视图缓存,避免内存泄漏
mVisibleDistanceViews.clear(); mVisibleDistanceViews.clear();
} }
// ---------------------- 核心修复1定时器逻辑仅刷新可见的距离TextView ---------------------- // ---------------------- 核心修复1定时器逻辑仅刷新可见的距离TextView(不变) ----------------------
/**
* 启动实时距离刷新定时器(仅更新可见的距离文本,不刷新整个列表项)
*/
private void startDistanceRefreshTimer() { private void startDistanceRefreshTimer() {
mDistanceTimer.scheduleAtFixedRate(new TimerTask() { mDistanceTimer.scheduleAtFixedRate(new TimerTask() {
@Override @Override
public void run() { public void run() {
// 切换到主线程UI更新必须在主线程
mMainHandler.post(new Runnable() { mMainHandler.post(new Runnable() {
@Override @Override
public void run() { public void run() {
// 遍历所有可见的距离TextView针对性更新不触发列表项重新绑定
for (Map.Entry<String, TextView> entry : mVisibleDistanceViews.entrySet()) { for (Map.Entry<String, TextView> entry : mVisibleDistanceViews.entrySet()) {
String positionId = entry.getKey(); // 当前可见项的位置ID String positionId = entry.getKey();
TextView distanceView = entry.getValue(); // 要更新的距离文本控件 TextView distanceView = entry.getValue();
// 根据位置ID找到对应的PositionModel
PositionModel targetModel = null; PositionModel targetModel = null;
for (PositionModel model : mPositionList) { for (PositionModel model : mPositionList) {
if (positionId.equals(model.getPositionId())) { if (positionId.equals(model.getPositionId())) {
@@ -149,7 +147,6 @@ public class PositionAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
} }
} }
// 若找到对应模型直接更新距离文本复用原有bindRealDistance逻辑
if (targetModel != null) { if (targetModel != null) {
bindRealDistance(distanceView, targetModel); bindRealDistance(distanceView, targetModel);
} }
@@ -157,13 +154,10 @@ public class PositionAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
} }
}); });
} }
}, 0, REFRESH_INTERVAL); // 0延迟启动每5秒执行一次 }, 0, REFRESH_INTERVAL);
} }
// ---------------------- 核心修复2管理可见/不可见的距离TextView避免内存泄漏+精准刷新 ---------------------- // ---------------------- 核心修复2管理可见/不可见的距离TextView不变 ----------------------
/**
* 列表项变为“可见”时将距离TextView加入缓存供定时器刷新
*/
@Override @Override
public void onViewAttachedToWindow(@NonNull RecyclerView.ViewHolder holder) { public void onViewAttachedToWindow(@NonNull RecyclerView.ViewHolder holder) {
super.onViewAttachedToWindow(holder); super.onViewAttachedToWindow(holder);
@@ -174,25 +168,21 @@ public class PositionAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
PositionModel model = mPositionList.get(position); PositionModel model = mPositionList.get(position);
String positionId = model.getPositionId(); String positionId = model.getPositionId();
TextView distanceView = null; // 同步更新位置ID→索引映射防止列表项位置变化导致索引失效
mPositionIdToIndexMap.put(positionId, position);
// 根据视图类型找到对应的距离TextView TextView distanceView = null;
if (holder instanceof SimpleViewHolder) { if (holder instanceof SimpleViewHolder) {
distanceView = ((SimpleViewHolder) holder).tvSimpleRealDistance; distanceView = ((SimpleViewHolder) holder).tvSimpleRealDistance;
} else if (holder instanceof EditViewHolder) { } else if (holder instanceof EditViewHolder) {
// 编辑视图若也显示距离(根据需求,若不显示可删除此分支) distanceView = ((EditViewHolder) holder).tvEditRealDistance;
distanceView = ((EditViewHolder) holder).tvEditRealDistance;
} }
// 若距离TextView存在加入缓存key=位置ID确保唯一
if (distanceView != null) { if (distanceView != null) {
mVisibleDistanceViews.put(positionId, distanceView); mVisibleDistanceViews.put(positionId, distanceView);
} }
} }
/**
* 列表项变为“不可见”时将距离TextView从缓存移除避免内存泄漏+无效刷新)
*/
@Override @Override
public void onViewDetachedFromWindow(@NonNull RecyclerView.ViewHolder holder) { public void onViewDetachedFromWindow(@NonNull RecyclerView.ViewHolder holder) {
super.onViewDetachedFromWindow(holder); super.onViewDetachedFromWindow(holder);
@@ -203,14 +193,12 @@ public class PositionAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
PositionModel model = mPositionList.get(position); PositionModel model = mPositionList.get(position);
String positionId = model.getPositionId(); String positionId = model.getPositionId();
// 从缓存移除不可见项的距离TextView
if (mVisibleDistanceViews.containsKey(positionId)) { if (mVisibleDistanceViews.containsKey(positionId)) {
mVisibleDistanceViews.remove(positionId); mVisibleDistanceViews.remove(positionId);
} }
} }
// ---------------------- RecyclerView 核心方法(仅补充编辑视图的距离TextView绑定其他不变) ---------------------- // ---------------------- RecyclerView 核心方法(不变) ----------------------
@Override @Override
public int getItemViewType(int position) { public int getItemViewType(int position) {
PositionModel model = mPositionList.get(position); PositionModel model = mPositionList.get(position);
@@ -233,6 +221,9 @@ public class PositionAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
@Override @Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
PositionModel model = mPositionList.get(position); PositionModel model = mPositionList.get(position);
// 同步更新位置ID→索引映射确保索引与当前列表项位置一致
mPositionIdToIndexMap.put(model.getPositionId(), position);
if (holder instanceof SimpleViewHolder) { if (holder instanceof SimpleViewHolder) {
bindSimpleView((SimpleViewHolder) holder, model, position); bindSimpleView((SimpleViewHolder) holder, model, position);
} else if (holder instanceof EditViewHolder) { } else if (holder instanceof EditViewHolder) {
@@ -245,18 +236,18 @@ public class PositionAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
return mPositionList.size(); return mPositionList.size();
} }
// 回收ViewHolder时清理资源不变,补充距离缓存移除 // 回收ViewHolder时清理资源不变
@Override @Override
public void onViewRecycled(@NonNull RecyclerView.ViewHolder holder) { public void onViewRecycled(@NonNull RecyclerView.ViewHolder holder) {
super.onViewRecycled(holder); super.onViewRecycled(holder);
// 移除缓存中已回收项的距离TextView
int position = holder.getAdapterPosition(); int position = holder.getAdapterPosition();
if (position >= 0 && position < mPositionList.size()) { if (position >= 0 && position < mPositionList.size()) {
String positionId = mPositionList.get(position).getPositionId(); String positionId = mPositionList.get(position).getPositionId();
mVisibleDistanceViews.remove(positionId); mVisibleDistanceViews.remove(positionId);
// 移除已回收位置的索引映射(避免内存泄漏)
mPositionIdToIndexMap.remove(positionId);
} }
// 清理任务列表资源(不变)
if (holder instanceof SimpleViewHolder) { if (holder instanceof SimpleViewHolder) {
((SimpleViewHolder) holder).ptlvSimpleTasks.clearData(); ((SimpleViewHolder) holder).ptlvSimpleTasks.clearData();
((SimpleViewHolder) holder).ptlvSimpleTasks.setOnTaskUpdatedListener(null); ((SimpleViewHolder) holder).ptlvSimpleTasks.setOnTaskUpdatedListener(null);
@@ -266,17 +257,13 @@ public class PositionAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
} }
} }
// 绑定简单视图(不变仅确保距离TextView正确初始化 // 绑定简单视图(不变)
private void bindSimpleView(final SimpleViewHolder holder, final PositionModel model, final int position) { private void bindSimpleView(final SimpleViewHolder holder, final PositionModel model, final int position) {
// 1. 绑定位置基础信息(不变)
holder.tvSimpleLongitude.setText(String.format("经度:%.6f", model.getLongitude())); holder.tvSimpleLongitude.setText(String.format("经度:%.6f", model.getLongitude()));
holder.tvSimpleLatitude.setText(String.format("纬度:%.6f", model.getLatitude())); holder.tvSimpleLatitude.setText(String.format("纬度:%.6f", model.getLatitude()));
holder.tvSimpleMemo.setText(String.format("备注:%s", model.getMemo())); holder.tvSimpleMemo.setText(String.format("备注:%s", model.getMemo()));
// 2. 绑定实时距离(首次初始化时调用,后续由定时器刷新)
bindRealDistance(holder.tvSimpleRealDistance, model); bindRealDistance(holder.tvSimpleRealDistance, model);
// 3. 长按切换编辑视图(不变)
holder.itemView.setOnLongClickListener(new View.OnLongClickListener() { holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
@Override @Override
public boolean onLongClick(View v) { public boolean onLongClick(View v) {
@@ -289,7 +276,7 @@ public class PositionAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
public boolean onMenuItemClick(MenuItem item) { public boolean onMenuItemClick(MenuItem item) {
if (item.getItemId() == R.id.menu_edit) { if (item.getItemId() == R.id.menu_edit) {
model.setIsSimpleView(false); model.setIsSimpleView(false);
notifyItemChanged(position); // 仅切换视图时刷新,不影响定时器 notifyItemChanged(position);
return true; return true;
} }
return false; return false;
@@ -301,7 +288,6 @@ public class PositionAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
} }
}); });
// 4. 绑定任务列表(不变,确保编辑不被干扰)
String currentPosId = model.getPositionId(); String currentPosId = model.getPositionId();
ArrayList<PositionTaskModel> matchedTasks = getSafeTasks(currentPosId); ArrayList<PositionTaskModel> matchedTasks = getSafeTasks(currentPosId);
holder.ptlvSimpleTasks.clearData(); holder.ptlvSimpleTasks.clearData();
@@ -311,30 +297,28 @@ public class PositionAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
} }
} }
// 绑定编辑视图(补充距离TextView绑定其他不变) // 绑定编辑视图(不变)
private void bindEditView(final EditViewHolder holder, final PositionModel model, final int position) { private void bindEditView(final EditViewHolder holder, final PositionModel model, final int position) {
// 1. 绑定位置基础信息新增编辑视图的距离TextView初始化
holder.tvEditLongitude.setText(String.format("经度:%.6f", model.getLongitude())); holder.tvEditLongitude.setText(String.format("经度:%.6f", model.getLongitude()));
holder.tvEditLatitude.setText(String.format("纬度:%.6f", model.getLatitude())); holder.tvEditLatitude.setText(String.format("纬度:%.6f", model.getLatitude()));
holder.etEditMemo.setText(model.getMemo()); holder.etEditMemo.setText(model.getMemo());
holder.etEditMemo.setSelection(holder.etEditMemo.getText().length()); holder.etEditMemo.setSelection(holder.etEditMemo.getText().length());
// 新增:首次绑定编辑视图时,初始化距离文本(后续由定时器刷新)
bindRealDistance(holder.tvEditRealDistance, model); bindRealDistance(holder.tvEditRealDistance, model);
// 2. 绑定实时距离开关(不变)
if (model.isEnableRealPositionDistance()) { if (model.isEnableRealPositionDistance()) {
holder.rgRealDistanceSwitch.check(R.id.rb_enable); holder.rgRealDistanceSwitch.check(R.id.rb_enable);
} else { } else {
holder.rgRealDistanceSwitch.check(R.id.rb_disable); holder.rgRealDistanceSwitch.check(R.id.rb_disable);
} }
// 3. 绑定删除/取消/确定按钮(不变,确保任务编辑逻辑正常)
holder.btnEditDelete.setOnClickListener(new View.OnClickListener() { holder.btnEditDelete.setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
if (mOnDeleteClickListener != null) { if (mOnDeleteClickListener != null) {
mOnDeleteClickListener.onDeleteClick(position); mOnDeleteClickListener.onDeleteClick(position);
mPositionTaskMap.remove(model.getPositionId()); mPositionTaskMap.remove(model.getPositionId());
// 移除删除位置的索引映射
mPositionIdToIndexMap.remove(model.getPositionId());
} }
} }
}); });
@@ -376,7 +360,6 @@ public class PositionAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
} }
}); });
// 4. 绑定任务列表(不变,确保编辑中的任务不被刷新覆盖)
final String currentPosId = model.getPositionId(); final String currentPosId = model.getPositionId();
ArrayList<PositionTaskModel> matchedTasks = getSafeTasks(currentPosId); ArrayList<PositionTaskModel> matchedTasks = getSafeTasks(currentPosId);
holder.ptlvEditTasks.clearData(); holder.ptlvEditTasks.clearData();
@@ -403,52 +386,51 @@ public class PositionAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
mAllPositionTasks.addAll(boundTasks); mAllPositionTasks.addAll(boundTasks);
if (mOnSavePositionTaskClickListener != null) { if (mOnSavePositionTaskClickListener != null) {
mOnSavePositionTaskClickListener.onSavePositionTaskClick(); mOnSavePositionTaskClickListener.onSavePositionTaskClick();
} }
Toast.makeText(mContext, "任务信息已保存", Toast.LENGTH_SHORT).show(); Toast.makeText(mContext, "任务信息已保存", Toast.LENGTH_SHORT).show();
} }
}); });
if (holder.ptlvEditTasks.getAllTasks().isEmpty()) { if (holder.ptlvEditTasks.getAllTasks().isEmpty()) {
holder.ptlvEditTasks.init(matchedTasks, currentPosId); holder.ptlvEditTasks.init(matchedTasks, currentPosId);
} }
holder.ptlvEditTasks.setViewStatus(PositionTaskListView.VIEW_MODE_EDIT); holder.ptlvEditTasks.setViewStatus(PositionTaskListView.VIEW_MODE_EDIT);
// 5. 绑定新增任务按钮(不变) holder.btnAddTask.setOnClickListener(new View.OnClickListener() {
holder.btnAddTask.setOnClickListener(new View.OnClickListener() { @Override
@Override public void onClick(View v) {
public void onClick(View v) { PositionTaskModel newTask = new PositionTaskModel(
PositionTaskModel newTask = new PositionTaskModel( PositionTaskModel.genTaskId(),
PositionTaskModel.genTaskId(), currentPosId,
currentPosId, DEFAULT_TASK_DESC,
DEFAULT_TASK_DESC, true,
true, DEFAULT_TASK_DISTANCE,
DEFAULT_TASK_DISTANCE, true
true );
);
ArrayList<PositionTaskModel> currentTasks = getSafeTasks(currentPosId); ArrayList<PositionTaskModel> currentTasks = getSafeTasks(currentPosId);
currentTasks.add(newTask); currentTasks.add(newTask);
mPositionTaskMap.put(currentPosId, new ArrayList<PositionTaskModel>(currentTasks)); mPositionTaskMap.put(currentPosId, new ArrayList<PositionTaskModel>(currentTasks));
mAllPositionTasks.add(newTask); mAllPositionTasks.add(newTask);
holder.ptlvEditTasks.clearData(); holder.ptlvEditTasks.clearData();
holder.ptlvEditTasks.init(currentTasks, currentPosId); holder.ptlvEditTasks.init(currentTasks, currentPosId);
holder.ptlvEditTasks.setViewStatus(PositionTaskListView.VIEW_MODE_EDIT); holder.ptlvEditTasks.setViewStatus(PositionTaskListView.VIEW_MODE_EDIT);
Toast.makeText(mContext, "已新增1个任务绑定当前位置", Toast.LENGTH_SHORT).show(); Toast.makeText(mContext, "已新增1个任务绑定当前位置", Toast.LENGTH_SHORT).show();
} }
}); });
} }
// 工具方法:绑定实时距离(逻辑不变,仅负责文本更新,不影响其他控件 // 工具方法:绑定实时距离(不变
private void bindRealDistance(TextView distanceView, PositionModel model) { private void bindRealDistance(TextView distanceView, PositionModel model) {
if (!model.isEnableRealPositionDistance()) { if (!model.isEnableRealPositionDistance()) {
distanceView.setText("实时距离:未启用"); distanceView.setText("实时距离:未启用");
distanceView.setTextColor(mContext.getResources().getColor(R.color.colorGrayText)); distanceView.setTextColor(mContext.getResources().getColor(R.color.colorGrayText));
return; return;
} }
if (mCurrentGpsPosition == null) { if (mCurrentGpsPosition == null) {
distanceView.setText("实时距离等待GPS定位"); distanceView.setText("实时距离等待GPS定位");
distanceView.setTextColor(mContext.getResources().getColor(R.color.colorGrayText)); distanceView.setTextColor(mContext.getResources().getColor(R.color.colorGrayText));
return; return;
@@ -456,6 +438,8 @@ public class PositionAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
try { try {
double distanceM = PositionModel.calculatePositionDistance(mCurrentGpsPosition, model, false); double distanceM = PositionModel.calculatePositionDistance(mCurrentGpsPosition, model, false);
// 设置任务列表触发状态(调用优化后的通知逻辑)
bingoTask(model, (int) distanceM);
String distanceText = (distanceM < 1000) ? String distanceText = (distanceM < 1000) ?
String.format("实时距离:%.1f 米", distanceM) : String.format("实时距离:%.1f 米", distanceM) :
String.format("实时距离:%.1f 千米", distanceM / 1000); String.format("实时距离:%.1f 千米", distanceM / 1000);
@@ -467,38 +451,96 @@ public class PositionAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
} }
} }
// 工具方法:安全获取任务列表(不变) // ---------------------- 核心优化bingoTask 任务触发通知逻辑(替换 notifyDataSetChanged() ----------------------
private ArrayList<PositionTaskModel> getSafeTasks(String positionId) { private void bingoTask(PositionModel model, int distanceM) {
// 1. 标记是否有任务触发(避免无变化时无效更新)
boolean hasTaskTriggered = false;
// 2. 记录当前位置ID后续精准定位列表项
final String targetPosId = model.getPositionId();
// 遍历当前位置的任务,判断是否触发
for (PositionTaskModel task : mAllPositionTasks) {
if (targetPosId.equals(task.getPositionId())) {
// 暂存旧状态(避免重复触发更新)
boolean oldBingoState = task.isBingo();
boolean newBingoState = false;
// 根据任务条件判断新触发状态
if (task.isGreaterThan()) {
newBingoState = distanceM > task.getDiscussDistance();
} else if (task.isLessThan()) {
newBingoState = distanceM < task.getDiscussDistance();
} else {
newBingoState = true;
}
// 仅当状态变化时,才更新任务状态并标记需要通知
if (newBingoState != oldBingoState) {
task.setIsBingo(newBingoState);
hasTaskTriggered = true;
}
}
}
// 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);
}
}
});
}
}
// 工具方法:安全获取任务列表(不变)
private ArrayList getSafeTasks(String positionId) {
if (!mPositionTaskMap.containsKey(positionId)) { if (!mPositionTaskMap.containsKey(positionId)) {
mPositionTaskMap.put(positionId, new ArrayList<PositionTaskModel>()); mPositionTaskMap.put(positionId, new ArrayList());
} }
return mPositionTaskMap.get(positionId); return mPositionTaskMap.get(positionId);
} }
// 对外API新增位置不变 // 对外API新增位置补充索引映射更新
public void addPosition(PositionModel model) { public void addPosition(PositionModel model) {
if (model == null) return; if (model == null) return;
String validPosId = model.getPositionId(); String validPosId = model.getPositionId();
mPositionList.add(model); mPositionList.add(model);
// 新增:添加新位置的索引映射(索引为列表最后一位)
mPositionIdToIndexMap.put(validPosId, mPositionList.size() - 1);
if (!mPositionTaskMap.containsKey(validPosId)) { if (!mPositionTaskMap.containsKey(validPosId)) {
mPositionTaskMap.put(validPosId, new ArrayList<PositionTaskModel>()); mPositionTaskMap.put(validPosId, new ArrayList());
} }
notifyItemInserted(mPositionList.size() - 1); notifyItemInserted(mPositionList.size() - 1);
} }
// 对外API删除位置不变,补充距离缓存清理) // 对外API删除位置补充索引映射清理)
public void removePosition(int position) { public void removePosition(int position) {
if (position < 0 || position >= mPositionList.size()) return; if (position < 0 || position >= mPositionList.size()) return;
PositionModel removedModel = mPositionList.get(position); PositionModel removedModel = mPositionList.get(position);
String removedPosId = removedModel.getPositionId(); String removedPosId = removedModel.getPositionId();
// 1. 清理距离缓存(新增移除已删除位置的距离TextView // 1. 清理距离缓存和索引映射(新增)
mVisibleDistanceViews.remove(removedPosId); mVisibleDistanceViews.remove(removedPosId);
mPositionIdToIndexMap.remove(removedPosId);
// 2. 清理全局任务列表和映射表(不变 // 2. 重新同步剩余位置的索引映射(避免删除后索引错位
Iterator<PositionTaskModel> taskIterator = mAllPositionTasks.iterator(); for (int i = position; i < mPositionList.size(); i++) {
PositionModel remainingModel = mPositionList.get(i);
mPositionIdToIndexMap.put(remainingModel.getPositionId(), i);
}
// 3. 清理全局任务列表和映射表(不变)
Iterator taskIterator = mAllPositionTasks.iterator();
while (taskIterator.hasNext()) { while (taskIterator.hasNext()) {
PositionTaskModel task = taskIterator.next(); PositionTaskModel task = (PositionTaskModel)taskIterator.next();
if (task != null && removedPosId.equals(task.getPositionId())) { if (task != null && removedPosId.equals(task.getPositionId())) {
taskIterator.remove(); taskIterator.remove();
} }
@@ -510,16 +552,16 @@ public class PositionAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
notifyItemRangeChanged(position, mPositionList.size()); notifyItemRangeChanged(position, mPositionList.size());
} }
// 对外API更新所有位置不变,补充距离缓存同步) // 对外API更新所有位置补充索引映射同步)
public void updateAllPositions(ArrayList<PositionModel> newPositionList) { public void updateAllPositions(ArrayList<PositionModel> newPositionList) {
if (newPositionList == null) return; if (newPositionList == null) return;
mPositionList.clear(); mPositionList.clear();
mPositionList.addAll(newPositionList); mPositionList.addAll(newPositionList);
// 1. 清理距离缓存中无效的位置ID新增:仅保留新列表中的位置 // 1. 清理距离缓存中无效的位置ID不变
Iterator<String> distanceViewIter = mVisibleDistanceViews.keySet().iterator(); Iterator distanceViewIter = mVisibleDistanceViews.keySet().iterator();
while (distanceViewIter.hasNext()) { while (distanceViewIter.hasNext()) {
String posId = distanceViewIter.next(); String posId = (String)distanceViewIter.next();
boolean isPosExist = false; boolean isPosExist = false;
for (PositionModel model : newPositionList) { for (PositionModel model : newPositionList) {
if (posId.equals(model.getPositionId())) { if (posId.equals(model.getPositionId())) {
@@ -532,10 +574,10 @@ public class PositionAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
} }
} }
// 2. 清理任务映射表(不变) // 2. 清理任务映射表(不变)
Iterator<String> taskMapKeyIter = mPositionTaskMap.keySet().iterator(); Iterator taskMapKeyIter = mPositionTaskMap.keySet().iterator();
while (taskMapKeyIter.hasNext()) { while (taskMapKeyIter.hasNext()) {
String posId = taskMapKeyIter.next(); String posId = (String)taskMapKeyIter.next();
boolean isPosExist = false; boolean isPosExist = false;
for (PositionModel model : newPositionList) { for (PositionModel model : newPositionList) {
if (posId.equals(model.getPositionId())) { if (posId.equals(model.getPositionId())) {
@@ -548,18 +590,21 @@ public class PositionAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
} }
} }
// 3. 为新位置初始化任务列表(不变 // 3. 重新初始化索引映射和任务列表(新增索引同步
for (PositionModel model : newPositionList) { mPositionIdToIndexMap.clear();
for (int i = 0; i < newPositionList.size(); i++) {
PositionModel model = (PositionModel)newPositionList.get(i);
String posId = model.getPositionId(); String posId = model.getPositionId();
mPositionIdToIndexMap.put(posId, i); // 同步新列表的索引
if (!mPositionTaskMap.containsKey(posId)) { if (!mPositionTaskMap.containsKey(posId)) {
mPositionTaskMap.put(posId, new ArrayList<PositionTaskModel>()); mPositionTaskMap.put(posId, new ArrayList());
} }
} }
notifyDataSetChanged(); notifyDataSetChanged();
} }
// 对外API批量切换简单视图不变 // 对外API批量切换简单视图不变
public void switchAllToSimpleView() { public void switchAllToSimpleView() {
for (PositionModel model : mPositionList) { for (PositionModel model : mPositionList) {
model.setIsSimpleView(true); model.setIsSimpleView(true);
@@ -567,18 +612,17 @@ public class PositionAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
notifyDataSetChanged(); notifyDataSetChanged();
} }
// 对外API获取位置列表不变 // 对外API获取位置列表不变
public ArrayList<PositionModel> getPositionList() { public ArrayList getPositionList() {
return new ArrayList<PositionModel>(mPositionList); return new ArrayList(mPositionList);
} }
// ---------------------- ViewHolder 定义(核心补充编辑视图的距离TextView ---------------------- // ---------------------- ViewHolder 定义(不变 ----------------------
// 简单视图Holder不变
public static class SimpleViewHolder extends RecyclerView.ViewHolder { public static class SimpleViewHolder extends RecyclerView.ViewHolder {
TextView tvSimpleLongitude; TextView tvSimpleLongitude;
TextView tvSimpleLatitude; TextView tvSimpleLatitude;
TextView tvSimpleMemo; TextView tvSimpleMemo;
TextView tvSimpleRealDistance; // 简单视图-距离文本 TextView tvSimpleRealDistance;
PositionTaskListView ptlvSimpleTasks; PositionTaskListView ptlvSimpleTasks;
public SimpleViewHolder(@NonNull View itemView) { public SimpleViewHolder(@NonNull View itemView) {
@@ -591,7 +635,6 @@ public class PositionAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
} }
} }
// 编辑视图Holder核心补充tvEditRealDistance 距离文本控件)
public static class EditViewHolder extends RecyclerView.ViewHolder { public static class EditViewHolder extends RecyclerView.ViewHolder {
TextView tvEditLongitude; TextView tvEditLongitude;
TextView tvEditLatitude; TextView tvEditLatitude;
@@ -604,7 +647,7 @@ public class PositionAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
Button btnEditConfirm; Button btnEditConfirm;
PositionTaskListView ptlvEditTasks; PositionTaskListView ptlvEditTasks;
Button btnAddTask; Button btnAddTask;
TextView tvEditRealDistance; // 新增:编辑视图-距离文本(与布局文件对应) TextView tvEditRealDistance;
public EditViewHolder(@NonNull View itemView) { public EditViewHolder(@NonNull View itemView) {
super(itemView); super(itemView);
@@ -619,9 +662,8 @@ public class PositionAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
btnEditConfirm = (Button) itemView.findViewById(R.id.btn_edit_confirm); btnEditConfirm = (Button) itemView.findViewById(R.id.btn_edit_confirm);
ptlvEditTasks = (PositionTaskListView) itemView.findViewById(R.id.ptlv_edit_tasks); ptlvEditTasks = (PositionTaskListView) itemView.findViewById(R.id.ptlv_edit_tasks);
btnAddTask = (Button) itemView.findViewById(R.id.btn_add_task); btnAddTask = (Button) itemView.findViewById(R.id.btn_add_task);
tvEditRealDistance = (TextView) itemView.findViewById(R.id.tv_edit_real_distance); // 绑定编辑视图的距离控件 tvEditRealDistance = (TextView) itemView.findViewById(R.id.tv_edit_real_distance);
} }
} }
} }

View File

@@ -26,6 +26,8 @@ public class PositionTaskModel extends BaseBean {
boolean isLessThan; boolean isLessThan;
// 任务条件距离(单位:米) // 任务条件距离(单位:米)
int discussDistance; int discussDistance;
// 任务是否已触发
boolean isBingo = false;
// 是否启用任务 // 是否启用任务
boolean isEnable; boolean isEnable;
@@ -51,6 +53,14 @@ public class PositionTaskModel extends BaseBean {
this.isEnable = true; this.isEnable = true;
} }
public void setIsBingo(boolean isBingo) {
this.isBingo = isBingo;
}
public boolean isBingo() {
return isBingo;
}
// ---------------------- Getter/Setter确保positionId不为空距离有效 ---------------------- // ---------------------- Getter/Setter确保positionId不为空距离有效 ----------------------
public void setTaskId(String taskId) { public void setTaskId(String taskId) {
this.taskId = (taskId == null || taskId.trim().isEmpty()) ? genTaskId() : taskId; this.taskId = (taskId == null || taskId.trim().isEmpty()) ? genTaskId() : taskId;

View File

@@ -3,7 +3,7 @@ package cc.winboll.studio.positions.views;
/** /**
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com> * @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
* @Date 2025/09/30 08:09 * @Date 2025/09/30 08:09
* @Describe 位置任务列表视图(支持简单/编辑模式,确保任务修改实时生效 * @Describe 位置任务列表视图(支持简单/编辑模式,含 isBingo 红点标识
*/ */
import android.content.Context; import android.content.Context;
import android.util.AttributeSet; import android.util.AttributeSet;
@@ -65,29 +65,21 @@ public class PositionTaskListView extends LinearLayout {
// 初始化视图(绑定控件+设置布局) // 初始化视图(绑定控件+设置布局)
private void initView(Context context) { private void initView(Context context) {
setOrientation(VERTICAL); setOrientation(VERTICAL);
// 加载根布局view_position_task_list.xml
LayoutInflater.from(context).inflate(R.layout.view_position_task_list, this, true); LayoutInflater.from(context).inflate(R.layout.view_position_task_list, this, true);
// 绑定RecyclerView并设置布局管理器
mRvTasks = (RecyclerView) findViewById(R.id.rv_position_tasks); mRvTasks = (RecyclerView) findViewById(R.id.rv_position_tasks);
mRvTasks.setLayoutManager(new LinearLayoutManager(context)); mRvTasks.setLayoutManager(new LinearLayoutManager(context));
// 初始化任务列表+适配器(强引用关联,确保数据同步)
mTaskList = new ArrayList<PositionTaskModel>(); mTaskList = new ArrayList<PositionTaskModel>();
mTaskAdapter = new TaskListAdapter(mTaskList); mTaskAdapter = new TaskListAdapter(mTaskList);
mRvTasks.setAdapter(mTaskAdapter); mRvTasks.setAdapter(mTaskAdapter);
// 默认简单模式
mCurrentViewMode = VIEW_MODE_SIMPLE; mCurrentViewMode = VIEW_MODE_SIMPLE;
} }
// ---------------------- 对外API ---------------------- // ---------------------- 对外API ----------------------
/**
* 初始化任务列表(仅首次空列表时加载,避免覆盖修改后数据)
*/
public void init(ArrayList<PositionTaskModel> taskList, String positionId) { public void init(ArrayList<PositionTaskModel> taskList, String positionId) {
this.mBindPositionId = positionId; this.mBindPositionId = positionId;
// 仅内部列表为空时加载外部数据
if (this.mTaskList.isEmpty()) { if (this.mTaskList.isEmpty()) {
ArrayList<PositionTaskModel> matchedTasks = new ArrayList<PositionTaskModel>(); ArrayList<PositionTaskModel> matchedTasks = new ArrayList<PositionTaskModel>();
if (taskList != null && !taskList.isEmpty()) { if (taskList != null && !taskList.isEmpty()) {
@@ -103,9 +95,6 @@ public class PositionTaskListView extends LinearLayout {
mTaskAdapter.notifyDataSetChanged(); mTaskAdapter.notifyDataSetChanged();
} }
/**
* 设置视图模式(简单/编辑)
*/
public void setViewStatus(int viewMode) { public void setViewStatus(int viewMode) {
if (viewMode != VIEW_MODE_SIMPLE && viewMode != VIEW_MODE_EDIT) { if (viewMode != VIEW_MODE_SIMPLE && viewMode != VIEW_MODE_EDIT) {
return; return;
@@ -114,23 +103,14 @@ public class PositionTaskListView extends LinearLayout {
mTaskAdapter.notifyDataSetChanged(); mTaskAdapter.notifyDataSetChanged();
} }
/**
* 设置任务修改回调
*/
public void setOnTaskUpdatedListener(OnTaskUpdatedListener listener) { public void setOnTaskUpdatedListener(OnTaskUpdatedListener listener) {
this.mOnTaskUpdatedListener = listener; this.mOnTaskUpdatedListener = listener;
} }
/**
* 获取当前任务列表(返回副本,避免外部修改污染)
*/
public ArrayList<PositionTaskModel> getAllTasks() { public ArrayList<PositionTaskModel> getAllTasks() {
return new ArrayList<PositionTaskModel>(mTaskList); return new ArrayList<PositionTaskModel>(mTaskList);
} }
/**
* 清空任务数据避免RecyclerView复用残留
*/
public void clearData() { public void clearData() {
mTaskList.clear(); mTaskList.clear();
if (mTaskAdapter != null && mTaskAdapter.mData != null) { if (mTaskAdapter != null && mTaskAdapter.mData != null) {
@@ -140,9 +120,6 @@ public class PositionTaskListView extends LinearLayout {
mBindPositionId = null; mBindPositionId = null;
} }
/**
* 主动触发任务同步(供外部强制同步数据)
*/
public void triggerTaskSync() { public void triggerTaskSync() {
if (mOnTaskUpdatedListener != null && mBindPositionId != null) { if (mOnTaskUpdatedListener != null && mBindPositionId != null) {
mOnTaskUpdatedListener.onTaskUpdated(mBindPositionId, new ArrayList<PositionTaskModel>(mTaskList)); mOnTaskUpdatedListener.onTaskUpdated(mBindPositionId, new ArrayList<PositionTaskModel>(mTaskList));
@@ -150,9 +127,6 @@ public class PositionTaskListView extends LinearLayout {
} }
// ---------------------- 内部工具方法 ---------------------- // ---------------------- 内部工具方法 ----------------------
/**
* 校验任务是否与当前位置匹配
*/
private boolean isTaskMatchedWithPosition(PositionTaskModel task) { private boolean isTaskMatchedWithPosition(PositionTaskModel task) {
if (task == null || mBindPositionId == null || mBindPositionId.trim().isEmpty()) { if (task == null || mBindPositionId == null || mBindPositionId.trim().isEmpty()) {
return false; return false;
@@ -160,53 +134,63 @@ public class PositionTaskListView extends LinearLayout {
return mBindPositionId.equals(task.getPositionId()); return mBindPositionId.equals(task.getPositionId());
} }
// ---------------------- 内部Adapter任务列表适配器(核心修改:确保修改生效 ---------------------- // ---------------------- 内部Adapter适配 isBingo 红点(核心调整 ----------------------
private class TaskListAdapter extends RecyclerView.Adapter<TaskListAdapter.TaskViewHolder> { private class TaskListAdapter extends RecyclerView.Adapter<TaskListAdapter.TaskViewHolder> {
// 适配器数据源与外部mTaskList强引用确保数据实时同步
private final List<PositionTaskModel> mData; private final List<PositionTaskModel> mData;
public TaskListAdapter(List<PositionTaskModel> data) { public TaskListAdapter(List<PositionTaskModel> data) {
this.mData = data; this.mData = data;
} }
// 获取列表项数量空列表显示1个“空提示”项
@Override @Override
public int getItemCount() { public int getItemCount() {
return mData.isEmpty() ? 1 : mData.size(); return mData.isEmpty() ? 1 : mData.size();
} }
// 区分视图类型0=空提示1=任务项 // 调整:根据“是否空列表”+“视图模式”区分视图类型(确保简单/编辑模式加载对应布局
@Override @Override
public int getItemViewType(int position) { public int getItemViewType(int position) {
return mData.isEmpty() ? 0 : 1; if (mData.isEmpty()) {
return 0; // 0=空提示
} else {
return mCurrentViewMode; // 1=简单模式2=编辑模式(复用视图模式常量)
}
} }
// 创建ViewHolder // 调整:按视图类型加载布局(简单模式加载带红点的布局,编辑模式加载原有布局)
@NonNull @NonNull
@Override @Override
public TaskViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { public TaskViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
Context context = parent.getContext(); Context context = parent.getContext();
LayoutInflater inflater = LayoutInflater.from(context); LayoutInflater inflater = LayoutInflater.from(context);
if (viewType == 0) { if (viewType == 0) {
// 空提示布局item_task_empty.xml // 空提示布局
View emptyView = inflater.inflate(R.layout.item_task_empty, parent, false); View emptyView = inflater.inflate(R.layout.item_task_empty, parent, false);
return new EmptyViewHolder(emptyView); return new EmptyViewHolder(emptyView);
} else if (viewType == VIEW_MODE_SIMPLE) {
// 简单模式布局(带 isBingo 红点)
View simpleTaskView = inflater.inflate(R.layout.item_position_task_simple, parent, false);
return new SimpleTaskViewHolder(simpleTaskView);
} else { } else {
// 任务项布局item_task_content.xml // 编辑模式布局(原有布局不变
View taskView = inflater.inflate(R.layout.item_task_content, parent, false); View editTaskView = inflater.inflate(R.layout.item_task_content, parent, false);
return new TaskContentViewHolder(taskView); return new TaskContentViewHolder(editTaskView);
} }
} }
// 绑定数据(核心:修复开关监听重复绑定+实时刷新 // 调整:按视图类型绑定数据(简单模式绑定红点+文本,编辑模式绑定原有逻辑
@Override @Override
public void onBindViewHolder(@NonNull TaskViewHolder holder, int position) { public void onBindViewHolder(@NonNull TaskViewHolder holder, int position) {
// 空提示处理(不变)
if (holder instanceof EmptyViewHolder) { if (holder instanceof EmptyViewHolder) {
// 空提示项无需绑定数据 EmptyViewHolder emptyHolder = (EmptyViewHolder) holder;
TextView tvEmptyTip = (TextView) emptyHolder.itemView.findViewById(R.id.tv_task_empty_tip);
tvEmptyTip.setText(mCurrentViewMode == VIEW_MODE_EDIT ? "暂无任务,点击\"添加新任务\"创建" : "暂无启用的任务");
return; return;
} }
// 校验位置有效性(避免越界 // 任务项有效性校验(不变
if (position >= mData.size()) { if (position >= mData.size()) {
return; return;
} }
@@ -215,16 +199,28 @@ public class PositionTaskListView extends LinearLayout {
return; return;
} }
// 绑定任务数据 // 简单模式绑定红点isBingo和文本数据
final TaskContentViewHolder contentHolder = (TaskContentViewHolder) holder; if (holder instanceof SimpleTaskViewHolder) {
bindTaskData(contentHolder, task, position); SimpleTaskViewHolder simpleHolder = (SimpleTaskViewHolder) holder;
// 绑定任务描述
simpleHolder.tvSimpleTaskDesc.setText(String.format("任务:%s", task.getTaskDescription()));
// 绑定距离条件
String distanceCond = task.isGreaterThan() ? "大于" : "小于";
simpleHolder.tvSimpleDistanceCond.setText(String.format("条件:距离 %s %d 米", distanceCond, task.getDiscussDistance()));
// 绑定启用状态
simpleHolder.tvSimpleIsEnable.setText(task.isEnable() ? "状态:已启用" : "状态:已禁用");
// 核心:根据 isBingo 控制红点显示true=显示false=隐藏)
simpleHolder.vBingoDot.setVisibility(task.isBingo() ? View.VISIBLE : View.GONE);
}
// 编辑模式:沿用原有绑定逻辑(不变)
else if (holder instanceof TaskContentViewHolder) {
TaskContentViewHolder contentHolder = (TaskContentViewHolder) holder;
bindTaskData(contentHolder, task, position);
}
} }
/** // ---------------------- 原有编辑模式绑定逻辑(完全不变) ----------------------
* 绑定任务具体数据(核心修复:开关监听去重+修改后实时刷新)
*/
private void bindTaskData(final TaskContentViewHolder holder, final PositionTaskModel task, final int position) { private void bindTaskData(final TaskContentViewHolder holder, final PositionTaskModel task, final int position) {
// 1. 绑定基础信息(描述、距离条件、启用状态)
String taskDesc = (task.getTaskDescription() == null) ? "未设置描述" : task.getTaskDescription(); String taskDesc = (task.getTaskDescription() == null) ? "未设置描述" : task.getTaskDescription();
holder.tvTaskDesc.setText(String.format("任务:%s", taskDesc)); holder.tvTaskDesc.setText(String.format("任务:%s", taskDesc));
@@ -232,21 +228,18 @@ public class PositionTaskListView extends LinearLayout {
holder.tvTaskDistance.setText(String.format("条件:%s %d 米", distanceCondition, task.getDiscussDistance())); holder.tvTaskDistance.setText(String.format("条件:%s %d 米", distanceCondition, task.getDiscussDistance()));
holder.cbTaskEnable.setChecked(task.isEnable()); holder.cbTaskEnable.setChecked(task.isEnable());
holder.cbTaskEnable.setEnabled(mCurrentViewMode == VIEW_MODE_EDIT); // 仅编辑模式可点击 holder.cbTaskEnable.setEnabled(mCurrentViewMode == VIEW_MODE_EDIT);
// 2. 编辑模式处理(核心:修复开关监听重复绑定+修改同步)
if (mCurrentViewMode == VIEW_MODE_EDIT) { if (mCurrentViewMode == VIEW_MODE_EDIT) {
holder.btnEditTask.setVisibility(View.VISIBLE); holder.btnEditTask.setVisibility(View.VISIBLE);
holder.btnDeleteTask.setVisibility(View.VISIBLE); holder.btnDeleteTask.setVisibility(View.VISIBLE);
// 1删除按钮逻辑
holder.btnDeleteTask.setOnClickListener(new View.OnClickListener() { holder.btnDeleteTask.setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
mData.remove(position); // 移除数据源中任务 mData.remove(position);
notifyItemRemoved(position); // 刷新列表(移除项) notifyItemRemoved(position);
notifyItemRangeChanged(position, mData.size()); // 刷新后续项索引 notifyItemRangeChanged(position, mData.size());
// 同步通知外部
if (mOnTaskUpdatedListener != null && mBindPositionId != null) { if (mOnTaskUpdatedListener != null && mBindPositionId != null) {
mOnTaskUpdatedListener.onTaskUpdated(mBindPositionId, new ArrayList<PositionTaskModel>(mData)); mOnTaskUpdatedListener.onTaskUpdated(mBindPositionId, new ArrayList<PositionTaskModel>(mData));
} }
@@ -254,7 +247,6 @@ public class PositionTaskListView extends LinearLayout {
} }
}); });
// 2修改按钮逻辑弹窗编辑
holder.btnEditTask.setOnClickListener(new View.OnClickListener() { holder.btnEditTask.setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
@@ -262,45 +254,37 @@ public class PositionTaskListView extends LinearLayout {
} }
}); });
// 核心修复1绑定新监听前先移除旧监听避免重复触发
holder.cbTaskEnable.setOnCheckedChangeListener(null); holder.cbTaskEnable.setOnCheckedChangeListener(null);
holder.cbTaskEnable.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { holder.cbTaskEnable.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override @Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
task.setIsEnable(isChecked); // 直接修改数据源中任务的启用状态 task.setIsEnable(isChecked);
notifyItemChanged(position); // 实时刷新当前项(显示最新状态) notifyItemChanged(position);
// 同步通知外部
if (mOnTaskUpdatedListener != null && mBindPositionId != null) { if (mOnTaskUpdatedListener != null && mBindPositionId != null) {
mOnTaskUpdatedListener.onTaskUpdated(mBindPositionId, new ArrayList<PositionTaskModel>(mData)); mOnTaskUpdatedListener.onTaskUpdated(mBindPositionId, new ArrayList<PositionTaskModel>(mData));
} }
} }
}); });
} else { } else {
// 简单模式:隐藏编辑按钮+移除监听
holder.btnEditTask.setVisibility(View.GONE); holder.btnEditTask.setVisibility(View.GONE);
holder.btnDeleteTask.setVisibility(View.GONE); holder.btnDeleteTask.setVisibility(View.GONE);
holder.cbTaskEnable.setOnCheckedChangeListener(null); holder.cbTaskEnable.setOnCheckedChangeListener(null);
} }
} }
/** // ---------------------- 原有编辑弹窗逻辑(完全不变) ----------------------
* 任务编辑弹窗核心修复2修改后实时刷新+同步数据)
*/
private void showTaskEditDialog(final PositionTaskModel task, final int position) { private void showTaskEditDialog(final PositionTaskModel task, final int position) {
final Context context = getContext(); final Context context = getContext();
// 加载弹窗布局dialog_edit_task.xml
View dialogView = LayoutInflater.from(context).inflate(R.layout.dialog_edit_task, null); View dialogView = LayoutInflater.from(context).inflate(R.layout.dialog_edit_task, null);
// 绑定弹窗控件
final EditText etEditDesc = (EditText) dialogView.findViewById(R.id.et_edit_task_desc); final EditText etEditDesc = (EditText) dialogView.findViewById(R.id.et_edit_task_desc);
final RadioGroup rgDistanceCondition = (RadioGroup) dialogView.findViewById(R.id.rg_distance_condition); final RadioGroup rgDistanceCondition = (RadioGroup) dialogView.findViewById(R.id.rg_distance_condition);
final EditText etEditDistance = (EditText) dialogView.findViewById(R.id.et_edit_distance); final EditText etEditDistance = (EditText) dialogView.findViewById(R.id.et_edit_distance);
Button btnCancel = (Button) dialogView.findViewById(R.id.btn_dialog_cancel); Button btnCancel = (Button) dialogView.findViewById(R.id.btn_dialog_cancel);
Button btnSave = (Button) dialogView.findViewById(R.id.btn_dialog_save); Button btnSave = (Button) dialogView.findViewById(R.id.btn_dialog_save);
// 初始化弹窗数据(显示当前任务信息)
etEditDesc.setText(task.getTaskDescription()); etEditDesc.setText(task.getTaskDescription());
etEditDesc.setSelection(etEditDesc.getText().length()); // 光标定位到末尾 etEditDesc.setSelection(etEditDesc.getText().length());
if (task.isGreaterThan()) { if (task.isGreaterThan()) {
rgDistanceCondition.check(R.id.rb_greater_than); rgDistanceCondition.check(R.id.rb_greater_than);
@@ -310,13 +294,11 @@ public class PositionTaskListView extends LinearLayout {
etEditDistance.setText(String.valueOf(task.getDiscussDistance())); etEditDistance.setText(String.valueOf(task.getDiscussDistance()));
// 创建并显示弹窗
final android.app.AlertDialog dialog = new android.app.AlertDialog.Builder(context) final android.app.AlertDialog dialog = new android.app.AlertDialog.Builder(context)
.setView(dialogView) .setView(dialogView)
.create(); .create();
dialog.show(); dialog.show();
// 取消按钮:关闭弹窗(不保存)
btnCancel.setOnClickListener(new View.OnClickListener() { btnCancel.setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
@@ -324,11 +306,9 @@ public class PositionTaskListView extends LinearLayout {
} }
}); });
// 保存按钮:校验+更新+同步(核心修复:实时刷新列表)
btnSave.setOnClickListener(new View.OnClickListener() { btnSave.setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
// 1. 输入校验
String newDesc = etEditDesc.getText().toString().trim(); String newDesc = etEditDesc.getText().toString().trim();
String distanceStr = etEditDistance.getText().toString().trim(); String distanceStr = etEditDistance.getText().toString().trim();
@@ -340,7 +320,7 @@ public class PositionTaskListView extends LinearLayout {
int newDistance; int newDistance;
try { try {
newDistance = Integer.parseInt(distanceStr); newDistance = Integer.parseInt(distanceStr);
if (newDistance < 1) { // 距离最小为1米避免无效值 if (newDistance < 1) {
Toast.makeText(context, "距离不能小于1米", Toast.LENGTH_SHORT).show(); Toast.makeText(context, "距离不能小于1米", Toast.LENGTH_SHORT).show();
return; return;
} }
@@ -349,69 +329,66 @@ public class PositionTaskListView extends LinearLayout {
return; return;
} }
// 2. 更新任务数据(直接修改数据源中的任务对象,强引用同步)
task.setTaskDescription(newDesc); task.setTaskDescription(newDesc);
task.setDiscussDistance(newDistance); task.setDiscussDistance(newDistance);
// 更新距离条件(大于/小于)
boolean isGreater = rgDistanceCondition.getCheckedRadioButtonId() == R.id.rb_greater_than; boolean isGreater = rgDistanceCondition.getCheckedRadioButtonId() == R.id.rb_greater_than;
task.setIsGreaterThan(isGreater); task.setIsGreaterThan(isGreater);
// 强制绑定当前位置ID避免任务串位
task.setPositionId(mBindPositionId); task.setPositionId(mBindPositionId);
// 3. 核心修复:实时刷新当前任务项,确保修改后立即显示
notifyItemChanged(position); notifyItemChanged(position);
// 4. 同步通知外部Adapter更新全局数据
if (mOnTaskUpdatedListener != null && mBindPositionId != null) { if (mOnTaskUpdatedListener != null && mBindPositionId != null) {
mOnTaskUpdatedListener.onTaskUpdated(mBindPositionId, new ArrayList<PositionTaskModel>(mData)); mOnTaskUpdatedListener.onTaskUpdated(mBindPositionId, new ArrayList<PositionTaskModel>(mData));
} }
// 5. 关闭弹窗并提示
dialog.dismiss(); dialog.dismiss();
Toast.makeText(context, "任务已更新", Toast.LENGTH_SHORT).show(); Toast.makeText(context, "任务已更新", Toast.LENGTH_SHORT).show();
} }
}); });
} }
// ---------------------- ViewHolder 定义(内部类,绑定布局控件 ---------------------- // ---------------------- ViewHolder 定义(新增简单模式 Holder适配红点 ----------------------
/** // 基础抽象 ViewHolder不变
* 基础ViewHolder抽象类统一父类型
*/
public abstract class TaskViewHolder extends RecyclerView.ViewHolder { public abstract class TaskViewHolder extends RecyclerView.ViewHolder {
public TaskViewHolder(@NonNull View itemView) { public TaskViewHolder(@NonNull View itemView) {
super(itemView); super(itemView);
} }
} }
/** // 空提示 Holder不变
* 空提示ViewHolder绑定“无任务”提示布局 item_task_empty.xml
*/
public class EmptyViewHolder extends TaskViewHolder { public class EmptyViewHolder extends TaskViewHolder {
public EmptyViewHolder(@NonNull View itemView) { public EmptyViewHolder(@NonNull View itemView) {
super(itemView); super(itemView);
// 根据当前视图模式修改提示文本
TextView tvEmptyTip = (TextView) itemView.findViewById(R.id.tv_task_empty_tip);
if (mCurrentViewMode == VIEW_MODE_EDIT) {
tvEmptyTip.setText("暂无任务,点击\"添加新任务\"创建");
} else {
tvEmptyTip.setText("暂无启用的任务");
}
} }
} }
/** // 新增:简单模式 Holder绑定带红点的布局控件
* 任务内容ViewHolder绑定任务项布局 item_task_content.xml public class SimpleTaskViewHolder extends TaskViewHolder {
*/ TextView tvSimpleTaskDesc; // 任务描述
TextView tvSimpleDistanceCond;// 距离条件
TextView tvSimpleIsEnable; // 启用状态
View vBingoDot; // isBingo 红点控件
public SimpleTaskViewHolder(@NonNull View itemView) {
super(itemView);
// 绑定简单模式布局中的控件(与 item_task_simple.xml 完全对应)
tvSimpleTaskDesc = itemView.findViewById(R.id.tv_simple_task_desc);
tvSimpleDistanceCond = itemView.findViewById(R.id.tv_simple_distance_cond);
tvSimpleIsEnable = itemView.findViewById(R.id.tv_simple_is_enable);
vBingoDot = itemView.findViewById(R.id.v_bingo_dot);
}
}
// 编辑模式 Holder原有逻辑完全不变
public class TaskContentViewHolder extends TaskViewHolder { public class TaskContentViewHolder extends TaskViewHolder {
TextView tvTaskDesc; // 任务描述 TextView tvTaskDesc;
TextView tvTaskDistance; // 距离条件(如“大于 100 米”) TextView tvTaskDistance;
CompoundButton cbTaskEnable; // 任务启用/禁用开关 CompoundButton cbTaskEnable;
Button btnEditTask; // 编辑任务按钮 Button btnEditTask;
Button btnDeleteTask; // 删除任务按钮 Button btnDeleteTask;
public TaskContentViewHolder(@NonNull View itemView) { public TaskContentViewHolder(@NonNull View itemView) {
super(itemView); super(itemView);
// 绑定布局控件(与 item_task_content.xml 中的ID严格对应
tvTaskDesc = (TextView) itemView.findViewById(R.id.tv_task_desc); tvTaskDesc = (TextView) itemView.findViewById(R.id.tv_task_desc);
tvTaskDistance = (TextView) itemView.findViewById(R.id.tv_task_distance); tvTaskDistance = (TextView) itemView.findViewById(R.id.tv_task_distance);
cbTaskEnable = (CompoundButton) itemView.findViewById(R.id.cb_task_enable); cbTaskEnable = (CompoundButton) itemView.findViewById(R.id.cb_task_enable);

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval"> <!-- 形状:圆形 -->
<solid android:color="#FF4444"/> <!-- 填充色:亮红色(可调整色值) -->
<stroke
android:width="1dp"
android:color="#FFFFFF"/>
</shape>

View File

@@ -1,60 +0,0 @@
<?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="wrap_content"
android:orientation="horizontal"
android:padding="12dp"
android:background="@drawable/item_position_bg"
android:layout_marginBottom="8dp"
android:gravity="center_vertical"> <!-- 内容与按钮垂直居中对齐 -->
<!-- 左侧:内容区(经纬度+备注,垂直排列,占满剩余宽度) -->
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical"
android:clickable="false"> <!-- 内容区禁止点击响应 -->
<TextView
android:id="@+id/tv_item_longitude"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="14sp"
android:textColor="#666666"/>
<TextView
android:id="@+id/tv_item_latitude"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="14sp"
android:textColor="#666666"
android:layout_marginTop="5dp"/>
<TextView
android:id="@+id/tv_item_memo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="16sp"
android:textColor="#000000"
android:textStyle="bold"
android:layout_marginTop="8dp"/>
</LinearLayout>
<!-- 右侧:删除按钮(红色图标/文字,仅按钮可点击) -->
<Button
android:id="@+id/btn_item_delete"
android:layout_width="wrap_content"
android:layout_height="30dp"
android:layout_marginStart="10dp"
android:background="@drawable/btn_delete_bg"
android:text="删除"
android:textColor="@android:color/white"
android:textSize="12sp"
android:paddingStart="12dp"
android:paddingEnd="12dp"/>
</LinearLayout>

View File

@@ -161,7 +161,7 @@
<cc.winboll.studio.positions.views.PositionTaskListView <cc.winboll.studio.positions.views.PositionTaskListView
android:id="@+id/ptlv_edit_tasks" android:id="@+id/ptlv_edit_tasks"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="400dp" android:layout_height="wrap_content"
android:layout_marginTop="6dp"/> android:layout_marginTop="6dp"/>
</LinearLayout> </LinearLayout>

View File

@@ -48,7 +48,7 @@
<cc.winboll.studio.positions.views.PositionTaskListView <cc.winboll.studio.positions.views.PositionTaskListView
android:id="@+id/ptlv_simple_tasks" android:id="@+id/ptlv_simple_tasks"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="200dp" android:layout_height="wrap_content"
android:layout_marginTop="4dp"/> android:layout_marginTop="4dp"/>
</LinearLayout> </LinearLayout>

View File

@@ -1,5 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <!-- 根布局改为 RelativeLayout支持红点右上角定位原 LinearLayout 无法便捷实现绝对位置) -->
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical" android:orientation="vertical"
@@ -7,7 +9,19 @@
android:background="@drawable/item_bg_simple" android:background="@drawable/item_bg_simple"
android:layout_marginBottom="8dp"> android:layout_marginBottom="8dp">
<!-- 任务描述 --> <!-- 1. 右上角小红点(仅 isBingo=true 时显示,绑定 isBingo 属性) -->
<View
android:id="@+id/v_bingo_dot"
android:layout_width="12dp"
android:layout_height="12dp"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:layout_marginRight="2dp"
android:layout_marginTop="2dp"
android:background="@drawable/bg_bingo_dot"
android:visibility="gone"/> <!-- 默认隐藏仅任务触发isBingo=true时显示 -->
<!-- 2. 任务描述(原控件不变,位置受红点不影响) -->
<TextView <TextView
android:id="@+id/tv_simple_task_desc" android:id="@+id/tv_simple_task_desc"
android:layout_width="wrap_content" android:layout_width="wrap_content"
@@ -16,7 +30,7 @@
android:textColor="#333333" android:textColor="#333333"
android:text="任务:无描述"/> android:text="任务:无描述"/>
<!-- 距离条件 --> <!-- 3. 距离条件(原控件不变) -->
<TextView <TextView
android:id="@+id/tv_simple_distance_cond" android:id="@+id/tv_simple_distance_cond"
android:layout_width="wrap_content" android:layout_width="wrap_content"
@@ -24,9 +38,10 @@
android:textSize="14sp" android:textSize="14sp"
android:textColor="#666666" android:textColor="#666666"
android:layout_marginTop="6dp" android:layout_marginTop="6dp"
android:layout_below="@id/tv_simple_task_desc"
android:text="条件:距离 > 0 米"/> android:text="条件:距离 > 0 米"/>
<!-- 启用状态(简单视图仅显示“已启用” --> <!-- 4. 启用状态(原控件不变 -->
<TextView <TextView
android:id="@+id/tv_simple_is_enable" android:id="@+id/tv_simple_is_enable"
android:layout_width="wrap_content" android:layout_width="wrap_content"
@@ -34,7 +49,8 @@
android:textSize="14sp" android:textSize="14sp"
android:textColor="#2E8B57" android:textColor="#2E8B57"
android:layout_marginTop="4dp" android:layout_marginTop="4dp"
android:layout_below="@id/tv_simple_distance_cond"
android:text="状态:已启用"/> android:text="状态:已启用"/>
</LinearLayout> </RelativeLayout>