位置任务参数数据管理模块完成
This commit is contained in:
@@ -32,6 +32,7 @@ import com.google.android.gms.location.LocationResult;
|
||||
import com.google.android.gms.location.LocationServices;
|
||||
import com.google.android.gms.tasks.OnSuccessListener;
|
||||
import java.util.ArrayList;
|
||||
import cc.winboll.studio.positions.models.PositionTaskModel;
|
||||
|
||||
/**
|
||||
* 实时定位活动窗口:
|
||||
@@ -59,6 +60,7 @@ public class LocationActivity extends AppCompatActivity {
|
||||
private RecyclerView rvPositionList; // 位置列表(RecyclerView)
|
||||
private PositionAdapter positionAdapter; // 列表Adapter(含实时距离逻辑)
|
||||
ArrayList<PositionModel> mPositionList = new ArrayList<PositionModel>(); // 位置数据集合
|
||||
ArrayList<PositionTaskModel> mPositionTasksList = new ArrayList<PositionTaskModel>(); // 位置数据集合
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
@@ -154,7 +156,9 @@ public class LocationActivity extends AppCompatActivity {
|
||||
rvPositionList.setLayoutManager(layoutManager);
|
||||
|
||||
// 2. 初始化Adapter(传入上下文和数据集合,兼容Java 7)
|
||||
positionAdapter = new PositionAdapter(this, mPositionList);
|
||||
PositionModel.loadBeanList(this, this.mPositionList, PositionModel.class);
|
||||
PositionTaskModel.loadBeanList(this, this.mPositionTasksList, PositionTaskModel.class);
|
||||
positionAdapter = new PositionAdapter(this, mPositionList, mPositionTasksList);
|
||||
rvPositionList.setAdapter(positionAdapter);
|
||||
|
||||
// 3. 设置Adapter删除监听(删除列表项并同步本地数据)
|
||||
@@ -166,11 +170,25 @@ public class LocationActivity extends AppCompatActivity {
|
||||
});
|
||||
|
||||
// 4. 设置Adapter保存监听(编辑备注后同步本地数据)
|
||||
positionAdapter.setOnSaveClickListener(new PositionAdapter.OnSaveClickListener() {
|
||||
positionAdapter.setOnSavePositionClickListener(new PositionAdapter.OnSavePositionClickListener() {
|
||||
@Override
|
||||
public void onSaveClick() {
|
||||
public void onSavePositionClick() {
|
||||
try {
|
||||
PositionModel.saveBeanList(LocationActivity.this, mPositionList, PositionModel.class);
|
||||
Toast.makeText(LocationActivity.this, "位置信息已保存", Toast.LENGTH_SHORT).show();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
Toast.makeText(LocationActivity.this, "数据保存失败", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
positionAdapter.setOnSavePositionTaskClickListener(new PositionAdapter.OnSavePositionTaskClickListener() {
|
||||
@Override
|
||||
public void onSavePositionTaskClick() {
|
||||
try {
|
||||
PositionTaskModel.saveBeanList(LocationActivity.this, mPositionTasksList, PositionTaskModel.class);
|
||||
Toast.makeText(LocationActivity.this, "任务信息已保存", Toast.LENGTH_SHORT).show();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
Toast.makeText(LocationActivity.this, "数据保存失败", Toast.LENGTH_SHORT).show();
|
||||
@@ -403,7 +421,7 @@ public class LocationActivity extends AppCompatActivity {
|
||||
|
||||
// 2. 停止Adapter的距离刷新定时器(关键:避免Timer持有Context导致内存泄漏)
|
||||
if (positionAdapter != null) {
|
||||
positionAdapter.stopDistanceRefreshTimer();
|
||||
positionAdapter.stopTimer();
|
||||
}
|
||||
|
||||
// 3. 最后同步一次数据(确保所有修改保存)
|
||||
|
||||
@@ -3,7 +3,7 @@ package cc.winboll.studio.positions.adapters;
|
||||
/**
|
||||
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
|
||||
* @Date 2025/09/29 20:25
|
||||
* @Describe 位置数据适配器(支持简单视图/编辑视图切换 + 实时距离开关控制)
|
||||
* @Describe 位置数据适配器(修复定时器:仅刷新实时距离,不干扰编辑中的任务)
|
||||
*/
|
||||
import android.content.Context;
|
||||
import android.os.Handler;
|
||||
@@ -24,120 +24,212 @@ import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
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.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;
|
||||
|
||||
public class PositionAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
|
||||
// 常量定义(不变)
|
||||
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; // 实时刷新间隔:5秒(5000毫秒)
|
||||
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;
|
||||
|
||||
private ArrayList<PositionModel> mPositionList;
|
||||
private Context mContext;
|
||||
private PositionModel mCurrentGpsPosition; // 存储当前GPS位置(由Activity传入)
|
||||
private Timer mDistanceTimer; // 定时计算距离的定时器
|
||||
private Handler mMainHandler; // 主线程Handler(更新UI必须在主线程)
|
||||
// 核心成员变量(新增:用于存储可见的距离TextView,避免遍历所有ViewHolder)
|
||||
private final ArrayList<PositionModel> mPositionList;
|
||||
private final ArrayList<PositionTaskModel> mAllPositionTasks;
|
||||
private final Context mContext;
|
||||
private PositionModel mCurrentGpsPosition;
|
||||
private final Timer mDistanceTimer;
|
||||
private final Handler mMainHandler;
|
||||
private final Map<String, ArrayList<PositionTaskModel>> mPositionTaskMap;
|
||||
// 关键新增:存储“位置ID → 距离TextView”的映射(仅包含当前可见的列表项)
|
||||
private final Map<String, TextView> mVisibleDistanceViews = new HashMap<>();
|
||||
|
||||
// 接口定义(不变)
|
||||
// 接口回调(不变)
|
||||
public interface OnDeleteClickListener {
|
||||
void onDeleteClick(int position);
|
||||
}
|
||||
|
||||
public interface OnSaveClickListener {
|
||||
void onSaveClick();
|
||||
public interface OnSavePositionClickListener {
|
||||
void onSavePositionClick();
|
||||
}
|
||||
|
||||
public interface OnSavePositionTaskClickListener {
|
||||
void onSavePositionTaskClick();
|
||||
}
|
||||
|
||||
private OnDeleteClickListener mOnDeleteClickListener;
|
||||
private OnSaveClickListener mOnSaveClickListener;
|
||||
private OnSavePositionClickListener mOnSavePositionClickListener;
|
||||
private OnSavePositionTaskClickListener mOnSavePositionTaskClickListener;
|
||||
|
||||
// 构造函数(新增:初始化主线程Handler)
|
||||
public PositionAdapter(Context context, ArrayList<PositionModel> positionList) {
|
||||
// 构造函数(不变,仅定时器初始化逻辑修改)
|
||||
public PositionAdapter(Context context, ArrayList<PositionModel> positionList, ArrayList<PositionTaskModel> allPositionTasks) {
|
||||
this.mContext = context;
|
||||
this.mPositionList = positionList;
|
||||
this.mMainHandler = new Handler(Looper.getMainLooper()); // 绑定主线程Looper
|
||||
this.mPositionList = (positionList != null) ? positionList : new ArrayList<PositionModel>();
|
||||
this.mAllPositionTasks = (allPositionTasks != null) ? allPositionTasks : new ArrayList<PositionTaskModel>();
|
||||
this.mMainHandler = new Handler(Looper.getMainLooper());
|
||||
this.mDistanceTimer = new Timer();
|
||||
this.mPositionTaskMap = new HashMap<String, ArrayList<PositionTaskModel>>();
|
||||
|
||||
// 初始化任务映射表(不变)
|
||||
for (PositionModel model : mPositionList) {
|
||||
String validPosId = model.getPositionId();
|
||||
ArrayList<PositionTaskModel> matchedTasks = new ArrayList<PositionTaskModel>();
|
||||
for (PositionTaskModel task : this.mAllPositionTasks) {
|
||||
if (task != null && validPosId.equals(task.getPositionId())) {
|
||||
matchedTasks.add(task);
|
||||
}
|
||||
}
|
||||
mPositionTaskMap.put(validPosId, matchedTasks);
|
||||
}
|
||||
|
||||
// 启动距离刷新定时器(核心修改:调用新的刷新逻辑)
|
||||
startDistanceRefreshTimer();
|
||||
}
|
||||
|
||||
// ---------------------- 新增:设置当前GPS位置(由Activity调用,传入实时GPS数据) ----------------------
|
||||
/**
|
||||
* 设置当前GPS位置(Activity通过定位API获取后,调用此方法传入Adapter)
|
||||
* @param currentGpsPosition 包含当前经度、纬度的PositionModel(memo可空,isEnable无需关注)
|
||||
*/
|
||||
// 对外API:设置当前GPS位置(不变)
|
||||
public void setCurrentGpsPosition(PositionModel currentGpsPosition) {
|
||||
this.mCurrentGpsPosition = currentGpsPosition;
|
||||
// 首次设置GPS位置时,启动定时器(避免重复启动)
|
||||
if (currentGpsPosition != null && mDistanceTimer == null) {
|
||||
startDistanceRefreshTimer();
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------- 新增:启动5秒定时刷新距离的定时器 ----------------------
|
||||
private void startDistanceRefreshTimer() {
|
||||
// 先停止原有定时器(防止重复创建)
|
||||
stopDistanceRefreshTimer();
|
||||
|
||||
mDistanceTimer = new Timer();
|
||||
mDistanceTimer.scheduleAtFixedRate(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
// 定时器在子线程执行,更新UI需切换到主线程
|
||||
mMainHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// 刷新所有列表项的距离显示(仅简单视图生效)
|
||||
notifyItemRangeChanged(0, getItemCount());
|
||||
}
|
||||
});
|
||||
}
|
||||
}, 0, REFRESH_INTERVAL); // 0延迟启动,每REFRESH_INTERVAL(5秒)执行一次
|
||||
}
|
||||
|
||||
// ---------------------- 新增:停止定时器(避免内存泄漏) ----------------------
|
||||
/**
|
||||
* 停止定时器(必须在Activity销毁/Adapter不再使用时调用,如Activity的onDestroy)
|
||||
*/
|
||||
public void stopDistanceRefreshTimer() {
|
||||
if (mDistanceTimer != null) {
|
||||
mDistanceTimer.cancel();
|
||||
mDistanceTimer.purge();
|
||||
mDistanceTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
// 设置监听(不变)
|
||||
// 对外API:设置监听器(不变)
|
||||
public void setOnDeleteClickListener(OnDeleteClickListener listener) {
|
||||
this.mOnDeleteClickListener = listener;
|
||||
}
|
||||
|
||||
public void setOnSaveClickListener(OnSaveClickListener listener) {
|
||||
this.mOnSaveClickListener = listener;
|
||||
public void setOnSavePositionClickListener(OnSavePositionClickListener listener) {
|
||||
this.mOnSavePositionClickListener = listener;
|
||||
}
|
||||
|
||||
// 视图类型判断(不变)
|
||||
public void setOnSavePositionTaskClickListener(OnSavePositionTaskClickListener listener) {
|
||||
this.mOnSavePositionTaskClickListener = listener;
|
||||
}
|
||||
|
||||
// 对外API:停止定时器(不变)
|
||||
public void stopTimer() {
|
||||
if (mDistanceTimer != null) {
|
||||
mDistanceTimer.cancel();
|
||||
mDistanceTimer.purge();
|
||||
}
|
||||
// 清空可见距离视图缓存,避免内存泄漏
|
||||
mVisibleDistanceViews.clear();
|
||||
}
|
||||
|
||||
// ---------------------- 核心修复1:定时器逻辑(仅刷新可见的距离TextView) ----------------------
|
||||
/**
|
||||
* 启动实时距离刷新定时器(仅更新可见的距离文本,不刷新整个列表项)
|
||||
*/
|
||||
private void startDistanceRefreshTimer() {
|
||||
mDistanceTimer.scheduleAtFixedRate(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
// 切换到主线程(UI更新必须在主线程)
|
||||
mMainHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// 遍历所有可见的距离TextView,针对性更新(不触发列表项重新绑定)
|
||||
for (Map.Entry<String, TextView> entry : mVisibleDistanceViews.entrySet()) {
|
||||
String positionId = entry.getKey(); // 当前可见项的位置ID
|
||||
TextView distanceView = entry.getValue(); // 要更新的距离文本控件
|
||||
|
||||
// 根据位置ID找到对应的PositionModel
|
||||
PositionModel targetModel = null;
|
||||
for (PositionModel model : mPositionList) {
|
||||
if (positionId.equals(model.getPositionId())) {
|
||||
targetModel = model;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 若找到对应模型,直接更新距离文本(复用原有bindRealDistance逻辑)
|
||||
if (targetModel != null) {
|
||||
bindRealDistance(distanceView, targetModel);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}, 0, REFRESH_INTERVAL); // 0延迟启动,每5秒执行一次
|
||||
}
|
||||
|
||||
// ---------------------- 核心修复2:管理可见/不可见的距离TextView(避免内存泄漏+精准刷新) ----------------------
|
||||
/**
|
||||
* 列表项变为“可见”时:将距离TextView加入缓存(供定时器刷新)
|
||||
*/
|
||||
@Override
|
||||
public void onViewAttachedToWindow(@NonNull RecyclerView.ViewHolder holder) {
|
||||
super.onViewAttachedToWindow(holder);
|
||||
int position = holder.getAdapterPosition();
|
||||
if (position < 0 || position >= mPositionList.size()) {
|
||||
return;
|
||||
}
|
||||
|
||||
PositionModel model = mPositionList.get(position);
|
||||
String positionId = model.getPositionId();
|
||||
TextView distanceView = null;
|
||||
|
||||
// 根据视图类型,找到对应的距离TextView
|
||||
if (holder instanceof SimpleViewHolder) {
|
||||
distanceView = ((SimpleViewHolder) holder).tvSimpleRealDistance;
|
||||
} else if (holder instanceof EditViewHolder) {
|
||||
// 编辑视图若也显示距离(根据需求,若不显示可删除此分支)
|
||||
distanceView = ((EditViewHolder) holder).tvEditRealDistance;
|
||||
}
|
||||
|
||||
// 若距离TextView存在,加入缓存(key=位置ID,确保唯一)
|
||||
if (distanceView != null) {
|
||||
mVisibleDistanceViews.put(positionId, distanceView);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 列表项变为“不可见”时:将距离TextView从缓存移除(避免内存泄漏+无效刷新)
|
||||
*/
|
||||
@Override
|
||||
public void onViewDetachedFromWindow(@NonNull RecyclerView.ViewHolder holder) {
|
||||
super.onViewDetachedFromWindow(holder);
|
||||
int position = holder.getAdapterPosition();
|
||||
if (position < 0 || position >= mPositionList.size()) {
|
||||
return;
|
||||
}
|
||||
|
||||
PositionModel model = mPositionList.get(position);
|
||||
String positionId = model.getPositionId();
|
||||
|
||||
// 从缓存移除不可见项的距离TextView
|
||||
if (mVisibleDistanceViews.containsKey(positionId)) {
|
||||
mVisibleDistanceViews.remove(positionId);
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------- RecyclerView 核心方法(仅补充编辑视图的距离TextView绑定,其他不变) ----------------------
|
||||
@Override
|
||||
public int getItemViewType(int position) {
|
||||
PositionModel model = mPositionList.get(position);
|
||||
return model.isSimpleView() ? VIEW_TYPE_SIMPLE : VIEW_TYPE_EDIT;
|
||||
}
|
||||
|
||||
// 创建ViewHolder(修改:EditViewHolder新增单选框组引用;SimpleViewHolder不变)
|
||||
@NonNull
|
||||
@Override
|
||||
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
LayoutInflater inflater = LayoutInflater.from(mContext);
|
||||
if (viewType == VIEW_TYPE_SIMPLE) {
|
||||
// 简单视图:绑定距离显示控件(不变)
|
||||
View view = inflater.inflate(R.layout.item_position_simple, parent, false);
|
||||
return new SimpleViewHolder(view);
|
||||
} else {
|
||||
// 编辑视图:绑定新增的单选框组(关键修改)
|
||||
View view = inflater.inflate(R.layout.item_position_edit, parent, false);
|
||||
return new EditViewHolder(view);
|
||||
}
|
||||
}
|
||||
|
||||
// 绑定数据(核心修改:编辑视图添加单选框状态绑定)
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
|
||||
PositionModel model = mPositionList.get(position);
|
||||
@@ -148,40 +240,43 @@ public class PositionAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------- 简单视图绑定(不变:实时距离显示逻辑保持原样) ----------------------
|
||||
private void bindSimpleView(SimpleViewHolder holder, final PositionModel model, final int position) {
|
||||
// 原有:绑定经纬度、备注
|
||||
holder.tvSimpleLongitude.setText(String.format("经度:%.6f", model.getLongitude()));
|
||||
holder.tvSimpleLatitude.setText(String.format("纬度:%.6f", model.getLatitude()));
|
||||
String memo = model.getMemo().trim().isEmpty() ? "无备注" : model.getMemo();
|
||||
holder.tvSimpleMemo.setText(String.format("备注:%s", memo));
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return mPositionList.size();
|
||||
}
|
||||
|
||||
// 实时距离显示逻辑(根据isEnableRealPositionDistance判断)
|
||||
if (model.isEnableRealPositionDistance()) {
|
||||
if (mCurrentGpsPosition != null) {
|
||||
try {
|
||||
// 调用PositionModel静态方法计算距离(米/千米自适应)
|
||||
double distanceM = PositionModel.calculatePositionDistance(
|
||||
mCurrentGpsPosition, model, false);
|
||||
String distanceText;
|
||||
if (distanceM < 1000) {
|
||||
distanceText = String.format("实时距离:%.1f 米", distanceM);
|
||||
} else {
|
||||
double distanceKm = distanceM / 1000;
|
||||
distanceText = String.format("实时距离:%.1f 千米", distanceKm);
|
||||
}
|
||||
holder.tvSimpleRealDistance.setText(distanceText);
|
||||
} catch (IllegalArgumentException e) {
|
||||
holder.tvSimpleRealDistance.setText("实时距离:数据无效");
|
||||
}
|
||||
} else {
|
||||
holder.tvSimpleRealDistance.setText("实时距离:等待GPS定位");
|
||||
}
|
||||
} else {
|
||||
holder.tvSimpleRealDistance.setText("实时距离未启用");
|
||||
// 回收ViewHolder时清理资源(不变,补充距离缓存移除)
|
||||
@Override
|
||||
public void onViewRecycled(@NonNull RecyclerView.ViewHolder holder) {
|
||||
super.onViewRecycled(holder);
|
||||
// 移除缓存中已回收项的距离TextView
|
||||
int position = holder.getAdapterPosition();
|
||||
if (position >= 0 && position < mPositionList.size()) {
|
||||
String positionId = mPositionList.get(position).getPositionId();
|
||||
mVisibleDistanceViews.remove(positionId);
|
||||
}
|
||||
|
||||
// 长按弹出编辑菜单(不变)
|
||||
// 清理任务列表资源(不变)
|
||||
if (holder instanceof SimpleViewHolder) {
|
||||
((SimpleViewHolder) holder).ptlvSimpleTasks.clearData();
|
||||
((SimpleViewHolder) holder).ptlvSimpleTasks.setOnTaskUpdatedListener(null);
|
||||
} else if (holder instanceof EditViewHolder) {
|
||||
((EditViewHolder) holder).ptlvEditTasks.clearData();
|
||||
((EditViewHolder) holder).ptlvEditTasks.setOnTaskUpdatedListener(null);
|
||||
}
|
||||
}
|
||||
|
||||
// 绑定简单视图(不变,仅确保距离TextView正确初始化)
|
||||
private void bindSimpleView(final SimpleViewHolder holder, final PositionModel model, final int position) {
|
||||
// 1. 绑定位置基础信息(不变)
|
||||
holder.tvSimpleLongitude.setText(String.format("经度:%.6f", model.getLongitude()));
|
||||
holder.tvSimpleLatitude.setText(String.format("纬度:%.6f", model.getLatitude()));
|
||||
holder.tvSimpleMemo.setText(String.format("备注:%s", model.getMemo()));
|
||||
|
||||
// 2. 绑定实时距离(首次初始化时调用,后续由定时器刷新)
|
||||
bindRealDistance(holder.tvSimpleRealDistance, model);
|
||||
|
||||
// 3. 长按切换编辑视图(不变)
|
||||
holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
|
||||
@Override
|
||||
public boolean onLongClick(View v) {
|
||||
@@ -194,47 +289,56 @@ public class PositionAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
|
||||
public boolean onMenuItemClick(MenuItem item) {
|
||||
if (item.getItemId() == R.id.menu_edit) {
|
||||
model.setIsSimpleView(false);
|
||||
notifyItemChanged(position);
|
||||
notifyItemChanged(position); // 仅切换视图时刷新,不影响定时器
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
popupMenu.show();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
// 4. 绑定任务列表(不变,确保编辑不被干扰)
|
||||
String currentPosId = model.getPositionId();
|
||||
ArrayList<PositionTaskModel> matchedTasks = getSafeTasks(currentPosId);
|
||||
holder.ptlvSimpleTasks.clearData();
|
||||
holder.ptlvSimpleTasks.setViewStatus(PositionTaskListView.VIEW_MODE_SIMPLE);
|
||||
if (holder.ptlvSimpleTasks.getAllTasks().isEmpty()) {
|
||||
holder.ptlvSimpleTasks.init(matchedTasks, currentPosId);
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------- 核心修改:编辑视图绑定(新增单选框状态同步+保存) ----------------------
|
||||
// 绑定编辑视图(补充距离TextView绑定,其他不变)
|
||||
private void bindEditView(final EditViewHolder holder, final PositionModel model, final int position) {
|
||||
// 1. 原有:绑定经纬度、备注(不变)
|
||||
// 1. 绑定位置基础信息(新增:编辑视图的距离TextView初始化)
|
||||
holder.tvEditLongitude.setText(String.format("经度:%.6f", model.getLongitude()));
|
||||
holder.tvEditLatitude.setText(String.format("纬度:%.6f", model.getLatitude()));
|
||||
holder.etEditMemo.setText(model.getMemo());
|
||||
holder.etEditMemo.setSelection(model.getMemo().length());
|
||||
holder.etEditMemo.setSelection(holder.etEditMemo.getText().length());
|
||||
// 新增:首次绑定编辑视图时,初始化距离文本(后续由定时器刷新)
|
||||
bindRealDistance(holder.tvEditRealDistance, model);
|
||||
|
||||
// 2. 新增:绑定实时距离开关状态(与model.isEnableRealPositionDistance同步)
|
||||
// 2. 绑定实时距离开关(不变)
|
||||
if (model.isEnableRealPositionDistance()) {
|
||||
// 启用实时距离→选中“启用”单选框
|
||||
holder.rgRealDistanceSwitch.check(R.id.rb_enable);
|
||||
} else {
|
||||
// 禁用实时距离→选中“禁用”单选框(默认状态)
|
||||
holder.rgRealDistanceSwitch.check(R.id.rb_disable);
|
||||
}
|
||||
|
||||
// 3. 按钮逻辑(不变+新增单选框保存)
|
||||
// ① 删除按钮(最左侧,逻辑不变)
|
||||
// 3. 绑定删除/取消/确定按钮(不变,确保任务编辑逻辑正常)
|
||||
holder.btnEditDelete.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (mOnDeleteClickListener != null) {
|
||||
mOnDeleteClickListener.onDeleteClick(position);
|
||||
mPositionTaskMap.remove(model.getPositionId());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// ② 取消按钮(中间,逻辑不变:不保存修改,切回简单视图)
|
||||
holder.btnEditCancel.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
@@ -244,132 +348,280 @@ public class PositionAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
|
||||
}
|
||||
});
|
||||
|
||||
// ③ 确定按钮(最右侧,核心修改:保存备注+保存单选框状态)
|
||||
holder.btnEditConfirm.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
// 原有:保存备注内容
|
||||
String newMemo = holder.etEditMemo.getText().toString().trim();
|
||||
model.setMemo(newMemo.isEmpty() ? "无备注" : newMemo);
|
||||
model.setMemo(newMemo);
|
||||
boolean isEnableDistance = holder.rgRealDistanceSwitch.getCheckedRadioButtonId() == R.id.rb_enable;
|
||||
model.setIsEnableRealPositionDistance(isEnableDistance);
|
||||
|
||||
// 新增:保存实时距离开关状态(根据单选框选中项更新model属性)
|
||||
int checkedRadioId = holder.rgRealDistanceSwitch.getCheckedRadioButtonId();
|
||||
if (checkedRadioId == R.id.rb_enable) {
|
||||
// 选中“启用”→设置isEnableRealPositionDistance为true
|
||||
model.setIsEnableRealPositionDistance(true);
|
||||
} else {
|
||||
// 选中“禁用”(含默认)→设置isEnableRealPositionDistance为false
|
||||
model.setIsEnableRealPositionDistance(false);
|
||||
String currentPosId = model.getPositionId();
|
||||
ArrayList<PositionTaskModel> allTasks = holder.ptlvEditTasks.getAllTasks();
|
||||
ArrayList<PositionTaskModel> boundTasks = new ArrayList<PositionTaskModel>();
|
||||
for (PositionTaskModel task : allTasks) {
|
||||
if (task != null) {
|
||||
task.setPositionId(currentPosId);
|
||||
boundTasks.add(task);
|
||||
}
|
||||
}
|
||||
mPositionTaskMap.put(currentPosId, boundTasks);
|
||||
|
||||
// 原有:切回简单视图+刷新列表+通知保存
|
||||
model.setIsSimpleView(true);
|
||||
notifyItemChanged(position); // 刷新当前项,实时更新距离显示状态
|
||||
|
||||
if (mOnSaveClickListener != null) {
|
||||
mOnSaveClickListener.onSaveClick(); // 通知Activity保存到本地
|
||||
notifyItemChanged(position);
|
||||
if (mOnSavePositionClickListener != null) {
|
||||
mOnSavePositionClickListener.onSavePositionClick();
|
||||
}
|
||||
Toast.makeText(mContext, "备注及实时距离设置已保存", Toast.LENGTH_SHORT).show();
|
||||
Toast.makeText(mContext, "位置信息已保存", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ---------------------- ViewHolder 定义(核心修改:EditViewHolder新增单选框组引用) ----------------------
|
||||
/**
|
||||
* 简单视图Holder(不变:含实时距离显示控件)
|
||||
*/
|
||||
public static class SimpleViewHolder extends RecyclerView.ViewHolder {
|
||||
TextView tvSimpleLongitude;
|
||||
TextView tvSimpleLatitude;
|
||||
TextView tvSimpleMemo;
|
||||
TextView tvSimpleRealDistance; // 实时距离显示控件
|
||||
// 4. 绑定任务列表(不变,确保编辑中的任务不被刷新覆盖)
|
||||
final String currentPosId = model.getPositionId();
|
||||
ArrayList<PositionTaskModel> matchedTasks = getSafeTasks(currentPosId);
|
||||
holder.ptlvEditTasks.clearData();
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
holder.ptlvEditTasks.setOnTaskUpdatedListener(new PositionTaskListView.OnTaskUpdatedListener() {
|
||||
@Override
|
||||
public void onTaskUpdated(String posId, ArrayList<PositionTaskModel> updatedTasks) {
|
||||
ArrayList<PositionTaskModel> boundTasks = new ArrayList<PositionTaskModel>();
|
||||
for (PositionTaskModel task : updatedTasks) {
|
||||
if (task != null) {
|
||||
task.setPositionId(currentPosId);
|
||||
boundTasks.add(task);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑视图Holder(核心修改:新增实时距离开关单选框组及子项引用)
|
||||
*/
|
||||
public static class EditViewHolder extends RecyclerView.ViewHolder {
|
||||
TextView tvEditLongitude;
|
||||
TextView tvEditLatitude;
|
||||
EditText etEditMemo;
|
||||
Button btnEditDelete;
|
||||
Button btnEditCancel;
|
||||
Button btnEditConfirm;
|
||||
// 新增:实时距离开关单选框组及子项(与item_position_edit.xml对应)
|
||||
RadioGroup rgRealDistanceSwitch; // 单选框组(控制二选一)
|
||||
RadioButton rbDisable; // “禁用”单选框(对应isEnable=false)
|
||||
RadioButton rbEnable; // “启用”单选框(对应isEnable=true)
|
||||
mPositionTaskMap.put(currentPosId, boundTasks);
|
||||
Iterator<PositionTaskModel> taskIterator = mAllPositionTasks.iterator();
|
||||
while (taskIterator.hasNext()) {
|
||||
PositionTaskModel task = taskIterator.next();
|
||||
if (task != null && currentPosId.equals(task.getPositionId())) {
|
||||
taskIterator.remove();
|
||||
}
|
||||
}
|
||||
mAllPositionTasks.addAll(boundTasks);
|
||||
|
||||
public EditViewHolder(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
// 绑定原有控件(不变,显式强转适配Java 7)
|
||||
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);
|
||||
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);
|
||||
// 新增:绑定单选框组及子项(显式强转适配Java 7,与布局ID严格对应)
|
||||
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);
|
||||
}
|
||||
}
|
||||
if (mOnSavePositionTaskClickListener != null) {
|
||||
mOnSavePositionTaskClickListener.onSavePositionTaskClick();
|
||||
}
|
||||
Toast.makeText(mContext, "任务信息已保存", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
|
||||
// 数据存取方法(不变)
|
||||
public void addPosition(PositionModel model) {
|
||||
if (mPositionList != null && model != null) {
|
||||
model.setIsSimpleView(true);
|
||||
mPositionList.add(model);
|
||||
notifyItemInserted(mPositionList.size() - 1);
|
||||
}
|
||||
}
|
||||
if (holder.ptlvEditTasks.getAllTasks().isEmpty()) {
|
||||
holder.ptlvEditTasks.init(matchedTasks, currentPosId);
|
||||
}
|
||||
holder.ptlvEditTasks.setViewStatus(PositionTaskListView.VIEW_MODE_EDIT);
|
||||
|
||||
public void removePosition(int position) {
|
||||
if (mPositionList != null && position >= 0 && position < mPositionList.size()) {
|
||||
mPositionList.remove(position);
|
||||
notifyItemRemoved(position);
|
||||
notifyItemRangeChanged(position, mPositionList.size());
|
||||
}
|
||||
}
|
||||
// 5. 绑定新增任务按钮(不变)
|
||||
holder.btnAddTask.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
PositionTaskModel newTask = new PositionTaskModel(
|
||||
PositionTaskModel.genTaskId(),
|
||||
currentPosId,
|
||||
DEFAULT_TASK_DESC,
|
||||
true,
|
||||
DEFAULT_TASK_DISTANCE,
|
||||
true
|
||||
);
|
||||
|
||||
public void updateAllPositions(ArrayList<PositionModel> newList) {
|
||||
if (newList != null) {
|
||||
mPositionList.clear();
|
||||
for (PositionModel model : newList) {
|
||||
model.setIsSimpleView(true);
|
||||
}
|
||||
mPositionList.addAll(newList);
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
ArrayList<PositionTaskModel> currentTasks = getSafeTasks(currentPosId);
|
||||
currentTasks.add(newTask);
|
||||
mPositionTaskMap.put(currentPosId, new ArrayList<PositionTaskModel>(currentTasks));
|
||||
mAllPositionTasks.add(newTask);
|
||||
|
||||
public ArrayList<PositionModel> getPositionList() {
|
||||
return mPositionList;
|
||||
}
|
||||
holder.ptlvEditTasks.clearData();
|
||||
holder.ptlvEditTasks.init(currentTasks, currentPosId);
|
||||
holder.ptlvEditTasks.setViewStatus(PositionTaskListView.VIEW_MODE_EDIT);
|
||||
Toast.makeText(mContext, "已新增1个任务(绑定当前位置)", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void switchAllToSimpleView() {
|
||||
if (mPositionList != null) {
|
||||
for (PositionModel model : mPositionList) {
|
||||
model.setIsSimpleView(true);
|
||||
}
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
// 工具方法:绑定实时距离(逻辑不变,仅负责文本更新,不影响其他控件)
|
||||
private void bindRealDistance(TextView distanceView, PositionModel model) {
|
||||
if (!model.isEnableRealPositionDistance()) {
|
||||
distanceView.setText("实时距离:未启用");
|
||||
distanceView.setTextColor(mContext.getResources().getColor(R.color.colorGrayText));
|
||||
return;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return mPositionList == null ? 0 : mPositionList.size();
|
||||
}
|
||||
if (mCurrentGpsPosition == null) {
|
||||
distanceView.setText("实时距离:等待GPS定位");
|
||||
distanceView.setTextColor(mContext.getResources().getColor(R.color.colorGrayText));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
double distanceM = PositionModel.calculatePositionDistance(mCurrentGpsPosition, model, false);
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
// 工具方法:安全获取任务列表(不变)
|
||||
private ArrayList<PositionTaskModel> getSafeTasks(String positionId) {
|
||||
if (!mPositionTaskMap.containsKey(positionId)) {
|
||||
mPositionTaskMap.put(positionId, new ArrayList<PositionTaskModel>());
|
||||
}
|
||||
return mPositionTaskMap.get(positionId);
|
||||
}
|
||||
|
||||
// 对外API:新增位置(不变)
|
||||
public void addPosition(PositionModel model) {
|
||||
if (model == null) return;
|
||||
String validPosId = model.getPositionId();
|
||||
mPositionList.add(model);
|
||||
if (!mPositionTaskMap.containsKey(validPosId)) {
|
||||
mPositionTaskMap.put(validPosId, new ArrayList<PositionTaskModel>());
|
||||
}
|
||||
notifyItemInserted(mPositionList.size() - 1);
|
||||
}
|
||||
|
||||
// 对外API:删除位置(不变,补充距离缓存清理)
|
||||
public void removePosition(int position) {
|
||||
if (position < 0 || position >= mPositionList.size()) return;
|
||||
PositionModel removedModel = mPositionList.get(position);
|
||||
String removedPosId = removedModel.getPositionId();
|
||||
|
||||
// 1. 清理距离缓存(新增:移除已删除位置的距离TextView)
|
||||
mVisibleDistanceViews.remove(removedPosId);
|
||||
|
||||
// 2. 清理全局任务列表和映射表(不变)
|
||||
Iterator<PositionTaskModel> 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);
|
||||
|
||||
notifyItemRemoved(position);
|
||||
notifyItemRangeChanged(position, mPositionList.size());
|
||||
}
|
||||
|
||||
// 对外API:更新所有位置(不变,补充距离缓存同步)
|
||||
public void updateAllPositions(ArrayList<PositionModel> newPositionList) {
|
||||
if (newPositionList == null) return;
|
||||
mPositionList.clear();
|
||||
mPositionList.addAll(newPositionList);
|
||||
|
||||
// 1. 清理距离缓存中无效的位置ID(新增:仅保留新列表中的位置)
|
||||
Iterator<String> distanceViewIter = mVisibleDistanceViews.keySet().iterator();
|
||||
while (distanceViewIter.hasNext()) {
|
||||
String posId = distanceViewIter.next();
|
||||
boolean isPosExist = false;
|
||||
for (PositionModel model : newPositionList) {
|
||||
if (posId.equals(model.getPositionId())) {
|
||||
isPosExist = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!isPosExist) {
|
||||
distanceViewIter.remove();
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 清理任务映射表(不变)
|
||||
Iterator<String> taskMapKeyIter = mPositionTaskMap.keySet().iterator();
|
||||
while (taskMapKeyIter.hasNext()) {
|
||||
String posId = taskMapKeyIter.next();
|
||||
boolean isPosExist = false;
|
||||
for (PositionModel model : newPositionList) {
|
||||
if (posId.equals(model.getPositionId())) {
|
||||
isPosExist = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!isPosExist) {
|
||||
taskMapKeyIter.remove();
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 为新位置初始化任务列表(不变)
|
||||
for (PositionModel model : newPositionList) {
|
||||
String posId = model.getPositionId();
|
||||
if (!mPositionTaskMap.containsKey(posId)) {
|
||||
mPositionTaskMap.put(posId, new ArrayList<PositionTaskModel>());
|
||||
}
|
||||
}
|
||||
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
// 对外API:批量切换简单视图(不变)
|
||||
public void switchAllToSimpleView() {
|
||||
for (PositionModel model : mPositionList) {
|
||||
model.setIsSimpleView(true);
|
||||
}
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
// 对外API:获取位置列表(不变)
|
||||
public ArrayList<PositionModel> getPositionList() {
|
||||
return new ArrayList<PositionModel>(mPositionList);
|
||||
}
|
||||
|
||||
// ---------------------- ViewHolder 定义(核心补充:编辑视图的距离TextView) ----------------------
|
||||
// 简单视图Holder(不变)
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// 编辑视图Holder(核心补充:tvEditRealDistance 距离文本控件)
|
||||
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); // 绑定编辑视图的距离控件
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -12,39 +12,42 @@ import java.io.IOException;
|
||||
import java.util.UUID;
|
||||
|
||||
public class PositionModel extends BaseBean {
|
||||
|
||||
|
||||
public static final String TAG = "PositionModel";
|
||||
// 位置标识符
|
||||
// 位置唯一标识符(与任务的positionId绑定)
|
||||
String positionId;
|
||||
// 经度
|
||||
// 经度(范围:-180~180)
|
||||
double longitude;
|
||||
// 纬度
|
||||
// 纬度(范围:-90~90)
|
||||
double latitude;
|
||||
// 位置信息备注
|
||||
// 位置备注(空值时显示“无备注”)
|
||||
String memo;
|
||||
// 是否启用实时距离计算
|
||||
boolean isEnableRealPositionDistance;
|
||||
// 是否是简单视图
|
||||
// 是否显示简单视图(true=简单视图,false=编辑视图)
|
||||
boolean isSimpleView = true;
|
||||
|
||||
// 带参构造(强制初始化位置ID和经纬度)
|
||||
public PositionModel(String positionId, double longitude, double latitude, String memo, boolean isEnableRealPositionDistance) {
|
||||
this.positionId = positionId;
|
||||
this.longitude = longitude;
|
||||
this.latitude = latitude;
|
||||
this.memo = memo;
|
||||
this.positionId = (positionId == null || positionId.trim().isEmpty()) ? genPositionId() : positionId;
|
||||
this.longitude = Math.max(-180, Math.min(180, longitude)); // 经度范围限制
|
||||
this.latitude = Math.max(-90, Math.min(90, latitude)); // 纬度范围限制
|
||||
this.memo = (memo == null || memo.trim().isEmpty()) ? "无备注" : memo;
|
||||
this.isEnableRealPositionDistance = isEnableRealPositionDistance;
|
||||
}
|
||||
|
||||
|
||||
// 无参构造(默认值初始化,避免空指针)
|
||||
public PositionModel() {
|
||||
this.positionId = "";
|
||||
this.longitude = 0.0f;
|
||||
this.latitude = 0.0f;
|
||||
this.memo = "";
|
||||
this.positionId = genPositionId();
|
||||
this.longitude = 0.0;
|
||||
this.latitude = 0.0;
|
||||
this.memo = "无备注";
|
||||
this.isEnableRealPositionDistance = false;
|
||||
}
|
||||
|
||||
// ---------------------- Getter/Setter(确保字段有效性) ----------------------
|
||||
public void setPositionId(String positionId) {
|
||||
this.positionId = positionId;
|
||||
this.positionId = (positionId == null || positionId.trim().isEmpty()) ? genPositionId() : positionId;
|
||||
}
|
||||
|
||||
public String getPositionId() {
|
||||
@@ -68,7 +71,7 @@ public class PositionModel extends BaseBean {
|
||||
}
|
||||
|
||||
public void setMemo(String memo) {
|
||||
this.memo = memo;
|
||||
this.memo = (memo == null || memo.trim().isEmpty()) ? "无备注" : memo;
|
||||
}
|
||||
|
||||
public String getMemo() {
|
||||
@@ -76,7 +79,7 @@ public class PositionModel extends BaseBean {
|
||||
}
|
||||
|
||||
public void setLongitude(double longitude) {
|
||||
this.longitude = longitude;
|
||||
this.longitude = Math.max(-180, Math.min(180, longitude)); // 限制经度范围
|
||||
}
|
||||
|
||||
public double getLongitude() {
|
||||
@@ -84,79 +87,84 @@ public class PositionModel extends BaseBean {
|
||||
}
|
||||
|
||||
public void setLatitude(double latitude) {
|
||||
this.latitude = latitude;
|
||||
this.latitude = Math.max(-90, Math.min(90, latitude)); // 限制纬度范围
|
||||
}
|
||||
|
||||
public double getLatitude() {
|
||||
return latitude;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return PositionModel.class.getName();
|
||||
}
|
||||
|
||||
// ---------------------- 父类方法重写 ----------------------
|
||||
@Override
|
||||
public String getName() {
|
||||
return PositionModel.class.getName();
|
||||
}
|
||||
|
||||
// 生成唯一位置ID(与任务ID格式一致,确保关联匹配)
|
||||
public static String genPositionId() {
|
||||
// 生成唯一UUID(版本4,随机型)
|
||||
UUID uniqueUuid = UUID.randomUUID();
|
||||
// 转成字符串(标准格式,含横杠,共36位)
|
||||
String uuidStr = uniqueUuid.toString();
|
||||
return uuidStr;
|
||||
return uniqueUuid.toString(); // 36位标准UUID(含横杠,确保与任务ID格式统一)
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException {
|
||||
super.writeThisToJsonWriter(jsonWriter);
|
||||
jsonWriter.name("positionId").value(getPositionId());
|
||||
jsonWriter.name("longitude").value(getLongitude());
|
||||
jsonWriter.name("latitude").value(getLatitude());
|
||||
jsonWriter.name("memo").value(getMemo());
|
||||
jsonWriter.name("isEnableRealPositionDistance").value(isEnableRealPositionDistance());
|
||||
}
|
||||
// JSON序列化(保存位置数据)
|
||||
@Override
|
||||
public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException {
|
||||
super.writeThisToJsonWriter(jsonWriter);
|
||||
jsonWriter.name("positionId").value(getPositionId());
|
||||
jsonWriter.name("longitude").value(getLongitude());
|
||||
jsonWriter.name("latitude").value(getLatitude());
|
||||
jsonWriter.name("memo").value(getMemo());
|
||||
jsonWriter.name("isEnableRealPositionDistance").value(isEnableRealPositionDistance());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean initObjectsFromJsonReader(JsonReader jsonReader, String name) throws IOException {
|
||||
if (super.initObjectsFromJsonReader(jsonReader, name)) { return true; } else {
|
||||
if (name.equals("positionId")) {
|
||||
setPositionId(jsonReader.nextString());
|
||||
} else if (name.equals("longitude")) {
|
||||
setLongitude(jsonReader.nextDouble());
|
||||
} else if (name.equals("latitude")) {
|
||||
setLatitude(jsonReader.nextDouble());
|
||||
} else if (name.equals("memo")) {
|
||||
setMemo(jsonReader.nextString());
|
||||
} else if (name.equals("isEnableRealPositionDistance")) {
|
||||
setIsEnableRealPositionDistance(jsonReader.nextBoolean());
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// JSON反序列化(加载位置数据,校验字段)
|
||||
@Override
|
||||
public boolean initObjectsFromJsonReader(JsonReader jsonReader, String name) throws IOException {
|
||||
if (super.initObjectsFromJsonReader(jsonReader, name)) {
|
||||
return true;
|
||||
} else {
|
||||
if (name.equals("positionId")) {
|
||||
setPositionId(jsonReader.nextString());
|
||||
} else if (name.equals("longitude")) {
|
||||
setLongitude(jsonReader.nextDouble());
|
||||
} else if (name.equals("latitude")) {
|
||||
setLatitude(jsonReader.nextDouble());
|
||||
} else if (name.equals("memo")) {
|
||||
setMemo(jsonReader.nextString());
|
||||
} else if (name.equals("isEnableRealPositionDistance")) {
|
||||
setIsEnableRealPositionDistance(jsonReader.nextBoolean());
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseBean readBeanFromJsonReader(JsonReader jsonReader) throws IOException {
|
||||
jsonReader.beginObject();
|
||||
while (jsonReader.hasNext()) {
|
||||
String name = jsonReader.nextName();
|
||||
if (!initObjectsFromJsonReader(jsonReader, name)) {
|
||||
jsonReader.skipValue();
|
||||
}
|
||||
}
|
||||
// 结束 JSON 对象
|
||||
jsonReader.endObject();
|
||||
return this;
|
||||
}
|
||||
|
||||
// 从JSON读取位置数据
|
||||
@Override
|
||||
public BaseBean readBeanFromJsonReader(JsonReader jsonReader) throws IOException {
|
||||
jsonReader.beginObject();
|
||||
while (jsonReader.hasNext()) {
|
||||
String name = jsonReader.nextName();
|
||||
if (!initObjectsFromJsonReader(jsonReader, name)) {
|
||||
jsonReader.skipValue(); // 跳过未知字段
|
||||
}
|
||||
}
|
||||
jsonReader.endObject();
|
||||
return this;
|
||||
}
|
||||
|
||||
// ---------------------- 核心工具方法:计算两点距离(Haversine公式,确保精度) ----------------------
|
||||
/**
|
||||
* 计算两个位置之间的直线距离(地球表面最短距离,基于Haversine公式)
|
||||
* @param position1 第一个位置(PositionModel对象,含longitude/latitude)
|
||||
* @param position2 第二个位置(PositionModel对象,含longitude/latitude)
|
||||
* @param isKilometer 是否返回千米单位:true→千米(km),false→米(m)
|
||||
* @return 两个位置间的距离(保留2位小数,单位由isKilometer决定)
|
||||
* 计算两个位置之间的直线距离(地球表面最短距离)
|
||||
* @param position1 第一个位置(非null)
|
||||
* @param position2 第二个位置(非null)
|
||||
* @param isKilometer 是否返回千米单位:true→千米,false→米
|
||||
* @return 距离(保留1位小数,符合显示需求)
|
||||
* @throws IllegalArgumentException 位置为null或经纬度无效时抛出
|
||||
*/
|
||||
public static double calculatePositionDistance(PositionModel position1, PositionModel position2, boolean isKilometer) {
|
||||
// 1. 校验参数(避免空指针/无效经纬度,防止计算异常)
|
||||
// 1. 校验参数有效性(避免计算异常)
|
||||
if (position1 == null || position2 == null) {
|
||||
throw new IllegalArgumentException("位置对象不能为null");
|
||||
}
|
||||
@@ -164,41 +172,38 @@ public class PositionModel extends BaseBean {
|
||||
double lat1 = position1.getLatitude();
|
||||
double lon2 = position2.getLongitude();
|
||||
double lat2 = position2.getLatitude();
|
||||
// 经纬度范围校验(纬度:-90~90,经度:-180~180,超出则视为无效值)
|
||||
// 经纬度范围二次校验(确保有效)
|
||||
if (lat1 < -90 || lat1 > 90 || lat2 < -90 || lat2 > 90
|
||||
|| lon1 < -180 || lon1 > 180 || lon2 < -180 || lon2 > 180) {
|
||||
throw new IllegalArgumentException("经纬度值无效(纬度:-90~90,经度:-180~180)");
|
||||
}
|
||||
|
||||
// 2. Haversine公式核心计算(将角度转为弧度,地球半径取平均半径6371km)
|
||||
final double EARTH_RADIUS_KM = 6371; // 地球平均半径(单位:千米)
|
||||
// 经纬度转为弧度(Math.toRadians:角度→弧度,公式计算需弧度值)
|
||||
double radLat1 = Math.toRadians(lat1);
|
||||
// 2. Haversine公式计算(地球半径取6371km,行业标准)
|
||||
final double EARTH_RADIUS_KM = 6371;
|
||||
double radLat1 = Math.toRadians(lat1); // 角度转弧度
|
||||
double radLat2 = Math.toRadians(lat2);
|
||||
double radLon1 = Math.toRadians(lon1);
|
||||
double radLon2 = Math.toRadians(lon2);
|
||||
|
||||
// 计算纬度差、经度差
|
||||
double deltaLat = radLat2 - radLat1;
|
||||
double deltaLon = radLon2 - radLon1;
|
||||
double deltaLat = radLat2 - radLat1; // 纬度差
|
||||
double deltaLon = radLon2 - radLon1; // 经度差
|
||||
|
||||
// Haversine公式:a = sin²(Δφ/2) + cos φ1 ⋅ cos φ2 ⋅ sin²(Δλ/2)
|
||||
// 核心公式
|
||||
double a = Math.pow(Math.sin(deltaLat / 2), 2)
|
||||
+ Math.cos(radLat1) * Math.cos(radLat2)
|
||||
* Math.pow(Math.sin(deltaLon / 2), 2);
|
||||
// c = 2 ⋅ atan2(√a, √(1−a)) (计算圆心角)
|
||||
double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
||||
// 距离 = 地球半径 × 圆心角(单位:千米)
|
||||
double distanceKm = EARTH_RADIUS_KM * c;
|
||||
double distanceKm = EARTH_RADIUS_KM * c; // 距离(千米)
|
||||
|
||||
// 3. 单位转换+保留2位小数(避免精度冗余,符合日常使用习惯)
|
||||
// 3. 单位转换+精度处理(保留1位小数,符合显示需求)
|
||||
double distance;
|
||||
if (isKilometer) {
|
||||
distance = Math.round(distanceKm * 100.0) / 100.0; // 千米,保留2位小数
|
||||
distance = Math.round(distanceKm * 10.0) / 10.0; // 千米→1位小数
|
||||
} else {
|
||||
distance = Math.round(distanceKm * 1000 * 100.0) / 100.0; // 米,保留2位小数(1km=1000m)
|
||||
distance = Math.round(distanceKm * 1000 * 10.0) / 10.0; // 米→1位小数
|
||||
}
|
||||
|
||||
return distance;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,43 +14,46 @@ import java.util.UUID;
|
||||
public class PositionTaskModel extends BaseBean {
|
||||
|
||||
public static final String TAG = "PositionTaskModel";
|
||||
// 任务标识符
|
||||
// 任务标识符(唯一)
|
||||
String taskId;
|
||||
// 位置标识符
|
||||
// 绑定的位置标识符(与PositionModel的positionId一一对应)
|
||||
String positionId;
|
||||
// 任务描述
|
||||
String taskDescription;
|
||||
// 任务距离条件是否大于
|
||||
// 任务距离条件:是否大于设定距离
|
||||
boolean isGreaterThan;
|
||||
// 任务距离条件是否小于
|
||||
// 任务距离条件:是否小于设定距离(与isGreaterThan互斥)
|
||||
boolean isLessThan;
|
||||
// 任务条件商议距离
|
||||
// 任务条件距离(单位:米)
|
||||
int discussDistance;
|
||||
// 是否启用任务
|
||||
boolean isEnable;
|
||||
|
||||
// 带参构造(强制传入positionId,确保任务与位置绑定)
|
||||
public PositionTaskModel(String taskId, String positionId, String taskDescription, boolean isGreaterThan, int discussDistance, boolean isEnable) {
|
||||
this.taskId = taskId;
|
||||
this.positionId = positionId;
|
||||
this.taskDescription = taskDescription;
|
||||
this.taskId = (taskId == null || taskId.trim().isEmpty()) ? genTaskId() : taskId; // 空ID自动生成
|
||||
this.positionId = positionId; // 强制绑定位置ID
|
||||
this.taskDescription = (taskDescription == null || taskDescription.trim().isEmpty()) ? "新任务" : taskDescription;
|
||||
this.isGreaterThan = isGreaterThan;
|
||||
this.isLessThan = !this.isGreaterThan;
|
||||
this.discussDistance = discussDistance;
|
||||
this.isLessThan = !isGreaterThan; // 确保互斥
|
||||
this.discussDistance = Math.max(discussDistance, 1); // 距离最小1米,避免无效值
|
||||
this.isEnable = isEnable;
|
||||
}
|
||||
|
||||
// 无参构造(初始化默认值,positionId需后续设置)
|
||||
public PositionTaskModel() {
|
||||
this.taskId = "";
|
||||
this.taskId = genTaskId();
|
||||
this.positionId = "";
|
||||
this.taskDescription = "";
|
||||
this.taskDescription = "新任务";
|
||||
this.isGreaterThan = true;
|
||||
this.isLessThan = !this.isGreaterThan ;
|
||||
this.discussDistance = 0;
|
||||
this.isEnable = false;
|
||||
this.isLessThan = false; // 初始互斥
|
||||
this.discussDistance = 100; // 默认100米
|
||||
this.isEnable = true;
|
||||
}
|
||||
|
||||
// ---------------------- Getter/Setter(确保positionId不为空,距离有效) ----------------------
|
||||
public void setTaskId(String taskId) {
|
||||
this.taskId = taskId;
|
||||
this.taskId = (taskId == null || taskId.trim().isEmpty()) ? genTaskId() : taskId;
|
||||
}
|
||||
|
||||
public String getTaskId() {
|
||||
@@ -58,7 +61,7 @@ public class PositionTaskModel extends BaseBean {
|
||||
}
|
||||
|
||||
public void setPositionId(String positionId) {
|
||||
this.positionId = positionId;
|
||||
this.positionId = (positionId == null || positionId.trim().isEmpty()) ? "" : positionId; // 空值防护
|
||||
}
|
||||
|
||||
public String getPositionId() {
|
||||
@@ -66,25 +69,27 @@ public class PositionTaskModel extends BaseBean {
|
||||
}
|
||||
|
||||
public void setTaskDescription(String taskDescription) {
|
||||
this.taskDescription = taskDescription;
|
||||
this.taskDescription = (taskDescription == null || taskDescription.trim().isEmpty()) ? "新任务" : taskDescription;
|
||||
}
|
||||
|
||||
public String getTaskDescription() {
|
||||
return taskDescription;
|
||||
}
|
||||
|
||||
// 修复:确保isGreaterThan和isLessThan互斥
|
||||
public void setIsGreaterThan(boolean isGreaterThan) {
|
||||
this.isGreaterThan = isGreaterThan;
|
||||
this.isLessThan = this.isGreaterThan;
|
||||
this.isLessThan = !isGreaterThan; // 关键:小于 = 非大于
|
||||
}
|
||||
|
||||
public boolean isGreaterThan() {
|
||||
return isGreaterThan;
|
||||
}
|
||||
|
||||
// 修复:确保isLessThan和isGreaterThan互斥
|
||||
public void setIsLessThan(boolean isLessThan) {
|
||||
this.isLessThan = isLessThan;
|
||||
this.isGreaterThan = !this.isLessThan;
|
||||
this.isGreaterThan = !isLessThan; // 关键:大于 = 非小于
|
||||
}
|
||||
|
||||
public boolean isLessThan() {
|
||||
@@ -92,7 +97,7 @@ public class PositionTaskModel extends BaseBean {
|
||||
}
|
||||
|
||||
public void setDiscussDistance(int discussDistance) {
|
||||
this.discussDistance = discussDistance;
|
||||
this.discussDistance = Math.max(discussDistance, 1); // 距离最小1米,避免0或负数
|
||||
}
|
||||
|
||||
public int getDiscussDistance() {
|
||||
@@ -107,67 +112,71 @@ public class PositionTaskModel extends BaseBean {
|
||||
return isEnable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return PositionTaskModel.class.getName();
|
||||
}
|
||||
// ---------------------- 父类方法重写 ----------------------
|
||||
@Override
|
||||
public String getName() {
|
||||
return PositionTaskModel.class.getName();
|
||||
}
|
||||
|
||||
// 生成唯一任务ID(与PositionModel保持一致格式)
|
||||
public static String genTaskId() {
|
||||
// 生成唯一UUID(版本4,随机型)
|
||||
UUID uniqueUuid = UUID.randomUUID();
|
||||
// 转成字符串(标准格式,含横杠,共36位)
|
||||
String uuidStr = uniqueUuid.toString();
|
||||
return uuidStr;
|
||||
return uniqueUuid.toString(); // 36位标准UUID(含横杠,确保唯一)
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException {
|
||||
super.writeThisToJsonWriter(jsonWriter);
|
||||
jsonWriter.name("taskId").value(getTaskId());
|
||||
jsonWriter.name("positionId").value(getPositionId());
|
||||
jsonWriter.name("taskDescription").value(getTaskDescription());
|
||||
jsonWriter.name("isGreaterThan").value(isGreaterThan());
|
||||
jsonWriter.name("isLessThan").value(isLessThan());
|
||||
jsonWriter.name("discussDistance").value(getDiscussDistance());
|
||||
jsonWriter.name("isEnable").value(isEnable());
|
||||
}
|
||||
// JSON序列化(保存任务数据,包含所有字段)
|
||||
@Override
|
||||
public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException {
|
||||
super.writeThisToJsonWriter(jsonWriter);
|
||||
jsonWriter.name("taskId").value(getTaskId());
|
||||
jsonWriter.name("positionId").value(getPositionId());
|
||||
jsonWriter.name("taskDescription").value(getTaskDescription());
|
||||
jsonWriter.name("isGreaterThan").value(isGreaterThan());
|
||||
jsonWriter.name("isLessThan").value(isLessThan());
|
||||
jsonWriter.name("discussDistance").value(getDiscussDistance());
|
||||
jsonWriter.name("isEnable").value(isEnable());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean initObjectsFromJsonReader(JsonReader jsonReader, String name) throws IOException {
|
||||
if (super.initObjectsFromJsonReader(jsonReader, name)) { return true; } else {
|
||||
if (name.equals("taskId")) {
|
||||
setTaskId(jsonReader.nextString());
|
||||
} else if (name.equals("positionId")) {
|
||||
setPositionId(jsonReader.nextString());
|
||||
} else if (name.equals("taskDescription")) {
|
||||
setTaskDescription(jsonReader.nextString());
|
||||
} else if (name.equals("isGreaterThan")) {
|
||||
setIsGreaterThan(jsonReader.nextBoolean());
|
||||
} else if (name.equals("isLessThan")) {
|
||||
setIsLessThan(jsonReader.nextBoolean());
|
||||
} else if (name.equals("discussDistance")) {
|
||||
setDiscussDistance(jsonReader.nextInt());
|
||||
} else if (name.equals("isEnable")) {
|
||||
setIsEnable(jsonReader.nextBoolean());
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// JSON反序列化(加载任务数据,校验字段有效性)
|
||||
@Override
|
||||
public boolean initObjectsFromJsonReader(JsonReader jsonReader, String name) throws IOException {
|
||||
if (super.initObjectsFromJsonReader(jsonReader, name)) {
|
||||
return true;
|
||||
} else {
|
||||
if (name.equals("taskId")) {
|
||||
setTaskId(jsonReader.nextString());
|
||||
} else if (name.equals("positionId")) {
|
||||
setPositionId(jsonReader.nextString());
|
||||
} else if (name.equals("taskDescription")) {
|
||||
setTaskDescription(jsonReader.nextString());
|
||||
} else if (name.equals("isGreaterThan")) {
|
||||
setIsGreaterThan(jsonReader.nextBoolean());
|
||||
} else if (name.equals("isLessThan")) {
|
||||
setIsLessThan(jsonReader.nextBoolean());
|
||||
} else if (name.equals("discussDistance")) {
|
||||
setDiscussDistance(jsonReader.nextInt());
|
||||
} else if (name.equals("isEnable")) {
|
||||
setIsEnable(jsonReader.nextBoolean());
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseBean readBeanFromJsonReader(JsonReader jsonReader) throws IOException {
|
||||
jsonReader.beginObject();
|
||||
while (jsonReader.hasNext()) {
|
||||
String name = jsonReader.nextName();
|
||||
if (!initObjectsFromJsonReader(jsonReader, name)) {
|
||||
jsonReader.skipValue();
|
||||
}
|
||||
}
|
||||
// 结束 JSON 对象
|
||||
jsonReader.endObject();
|
||||
return this;
|
||||
}
|
||||
// 从JSON读取任务数据(确保反序列化完整)
|
||||
@Override
|
||||
public BaseBean readBeanFromJsonReader(JsonReader jsonReader) throws IOException {
|
||||
jsonReader.beginObject();
|
||||
while (jsonReader.hasNext()) {
|
||||
String name = jsonReader.nextName();
|
||||
if (!initObjectsFromJsonReader(jsonReader, name)) {
|
||||
jsonReader.skipValue(); // 跳过未知字段,避免崩溃
|
||||
}
|
||||
}
|
||||
jsonReader.endObject();
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,424 @@
|
||||
package cc.winboll.studio.positions.views;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
|
||||
* @Date 2025/09/30 08:09
|
||||
* @Describe 位置任务列表视图(支持简单/编辑模式,确保任务修改实时生效)
|
||||
*/
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.CompoundButton;
|
||||
import android.widget.EditText;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.RadioButton;
|
||||
import android.widget.RadioGroup;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import cc.winboll.studio.positions.R;
|
||||
import cc.winboll.studio.positions.models.PositionTaskModel;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class PositionTaskListView extends LinearLayout {
|
||||
// 视图模式常量
|
||||
public static final int VIEW_MODE_SIMPLE = 1;
|
||||
public static final int VIEW_MODE_EDIT = 2;
|
||||
|
||||
// 核心成员变量
|
||||
private String mBindPositionId;
|
||||
private ArrayList<PositionTaskModel> mTaskList;
|
||||
private int mCurrentViewMode;
|
||||
private TaskListAdapter mTaskAdapter;
|
||||
private RecyclerView mRvTasks;
|
||||
|
||||
// 任务修改回调接口
|
||||
public interface OnTaskUpdatedListener {
|
||||
void onTaskUpdated(String positionId, ArrayList<PositionTaskModel> updatedTasks);
|
||||
}
|
||||
|
||||
private OnTaskUpdatedListener mOnTaskUpdatedListener;
|
||||
|
||||
// ---------------------- 构造函数 ----------------------
|
||||
public PositionTaskListView(Context context) {
|
||||
super(context);
|
||||
initView(context);
|
||||
}
|
||||
|
||||
public PositionTaskListView(Context context, @Nullable AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
initView(context);
|
||||
}
|
||||
|
||||
public PositionTaskListView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
initView(context);
|
||||
}
|
||||
|
||||
// 初始化视图(绑定控件+设置布局)
|
||||
private void initView(Context context) {
|
||||
setOrientation(VERTICAL);
|
||||
// 加载根布局(view_position_task_list.xml)
|
||||
LayoutInflater.from(context).inflate(R.layout.view_position_task_list, this, true);
|
||||
|
||||
// 绑定RecyclerView并设置布局管理器
|
||||
mRvTasks = (RecyclerView) findViewById(R.id.rv_position_tasks);
|
||||
mRvTasks.setLayoutManager(new LinearLayoutManager(context));
|
||||
|
||||
// 初始化任务列表+适配器(强引用关联,确保数据同步)
|
||||
mTaskList = new ArrayList<PositionTaskModel>();
|
||||
mTaskAdapter = new TaskListAdapter(mTaskList);
|
||||
mRvTasks.setAdapter(mTaskAdapter);
|
||||
|
||||
// 默认简单模式
|
||||
mCurrentViewMode = VIEW_MODE_SIMPLE;
|
||||
}
|
||||
|
||||
// ---------------------- 对外API ----------------------
|
||||
/**
|
||||
* 初始化任务列表(仅首次空列表时加载,避免覆盖修改后数据)
|
||||
*/
|
||||
public void init(ArrayList<PositionTaskModel> taskList, String positionId) {
|
||||
this.mBindPositionId = positionId;
|
||||
// 仅内部列表为空时加载外部数据
|
||||
if (this.mTaskList.isEmpty()) {
|
||||
ArrayList<PositionTaskModel> matchedTasks = new ArrayList<PositionTaskModel>();
|
||||
if (taskList != null && !taskList.isEmpty()) {
|
||||
for (PositionTaskModel task : taskList) {
|
||||
if (task != null && positionId.equals(task.getPositionId())) {
|
||||
matchedTasks.add(task);
|
||||
}
|
||||
}
|
||||
}
|
||||
mTaskList.clear();
|
||||
mTaskList.addAll(matchedTasks);
|
||||
}
|
||||
mTaskAdapter.notifyDataSetChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置视图模式(简单/编辑)
|
||||
*/
|
||||
public void setViewStatus(int viewMode) {
|
||||
if (viewMode != VIEW_MODE_SIMPLE && viewMode != VIEW_MODE_EDIT) {
|
||||
return;
|
||||
}
|
||||
mCurrentViewMode = viewMode;
|
||||
mTaskAdapter.notifyDataSetChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置任务修改回调
|
||||
*/
|
||||
public void setOnTaskUpdatedListener(OnTaskUpdatedListener listener) {
|
||||
this.mOnTaskUpdatedListener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前任务列表(返回副本,避免外部修改污染)
|
||||
*/
|
||||
public ArrayList<PositionTaskModel> getAllTasks() {
|
||||
return new ArrayList<PositionTaskModel>(mTaskList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空任务数据(避免RecyclerView复用残留)
|
||||
*/
|
||||
public void clearData() {
|
||||
mTaskList.clear();
|
||||
if (mTaskAdapter != null && mTaskAdapter.mData != null) {
|
||||
mTaskAdapter.mData.clear();
|
||||
}
|
||||
mTaskAdapter.notifyDataSetChanged();
|
||||
mBindPositionId = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 主动触发任务同步(供外部强制同步数据)
|
||||
*/
|
||||
public void triggerTaskSync() {
|
||||
if (mOnTaskUpdatedListener != null && mBindPositionId != null) {
|
||||
mOnTaskUpdatedListener.onTaskUpdated(mBindPositionId, new ArrayList<PositionTaskModel>(mTaskList));
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------- 内部工具方法 ----------------------
|
||||
/**
|
||||
* 校验任务是否与当前位置匹配
|
||||
*/
|
||||
private boolean isTaskMatchedWithPosition(PositionTaskModel task) {
|
||||
if (task == null || mBindPositionId == null || mBindPositionId.trim().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
return mBindPositionId.equals(task.getPositionId());
|
||||
}
|
||||
|
||||
// ---------------------- 内部Adapter:任务列表适配器(核心修改:确保修改生效) ----------------------
|
||||
private class TaskListAdapter extends RecyclerView.Adapter<TaskListAdapter.TaskViewHolder> {
|
||||
// 适配器数据源(与外部mTaskList强引用,确保数据实时同步)
|
||||
private final List<PositionTaskModel> mData;
|
||||
|
||||
public TaskListAdapter(List<PositionTaskModel> data) {
|
||||
this.mData = data;
|
||||
}
|
||||
|
||||
// 获取列表项数量(空列表显示1个“空提示”项)
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return mData.isEmpty() ? 1 : mData.size();
|
||||
}
|
||||
|
||||
// 区分视图类型(0=空提示,1=任务项)
|
||||
@Override
|
||||
public int getItemViewType(int position) {
|
||||
return mData.isEmpty() ? 0 : 1;
|
||||
}
|
||||
|
||||
// 创建ViewHolder
|
||||
@NonNull
|
||||
@Override
|
||||
public TaskViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
Context context = parent.getContext();
|
||||
LayoutInflater inflater = LayoutInflater.from(context);
|
||||
if (viewType == 0) {
|
||||
// 空提示布局(item_task_empty.xml)
|
||||
View emptyView = inflater.inflate(R.layout.item_task_empty, parent, false);
|
||||
return new EmptyViewHolder(emptyView);
|
||||
} else {
|
||||
// 任务项布局(item_task_content.xml)
|
||||
View taskView = inflater.inflate(R.layout.item_task_content, parent, false);
|
||||
return new TaskContentViewHolder(taskView);
|
||||
}
|
||||
}
|
||||
|
||||
// 绑定数据(核心:修复开关监听重复绑定+实时刷新)
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull TaskViewHolder holder, int position) {
|
||||
if (holder instanceof EmptyViewHolder) {
|
||||
// 空提示项无需绑定数据
|
||||
return;
|
||||
}
|
||||
|
||||
// 校验位置有效性(避免越界)
|
||||
if (position >= mData.size()) {
|
||||
return;
|
||||
}
|
||||
final PositionTaskModel task = mData.get(position);
|
||||
if (task == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 绑定任务数据
|
||||
final TaskContentViewHolder contentHolder = (TaskContentViewHolder) holder;
|
||||
bindTaskData(contentHolder, task, position);
|
||||
}
|
||||
|
||||
/**
|
||||
* 绑定任务具体数据(核心修复:开关监听去重+修改后实时刷新)
|
||||
*/
|
||||
private void bindTaskData(final TaskContentViewHolder holder, final PositionTaskModel task, final int position) {
|
||||
// 1. 绑定基础信息(描述、距离条件、启用状态)
|
||||
String taskDesc = (task.getTaskDescription() == null) ? "未设置描述" : task.getTaskDescription();
|
||||
holder.tvTaskDesc.setText(String.format("任务:%s", taskDesc));
|
||||
|
||||
String distanceCondition = task.isGreaterThan() ? "大于" : "小于";
|
||||
holder.tvTaskDistance.setText(String.format("条件:%s %d 米", distanceCondition, task.getDiscussDistance()));
|
||||
|
||||
holder.cbTaskEnable.setChecked(task.isEnable());
|
||||
holder.cbTaskEnable.setEnabled(mCurrentViewMode == VIEW_MODE_EDIT); // 仅编辑模式可点击
|
||||
|
||||
// 2. 编辑模式处理(核心:修复开关监听重复绑定+修改同步)
|
||||
if (mCurrentViewMode == VIEW_MODE_EDIT) {
|
||||
holder.btnEditTask.setVisibility(View.VISIBLE);
|
||||
holder.btnDeleteTask.setVisibility(View.VISIBLE);
|
||||
|
||||
// (1)删除按钮逻辑
|
||||
holder.btnDeleteTask.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
mData.remove(position); // 移除数据源中任务
|
||||
notifyItemRemoved(position); // 刷新列表(移除项)
|
||||
notifyItemRangeChanged(position, mData.size()); // 刷新后续项索引
|
||||
// 同步通知外部
|
||||
if (mOnTaskUpdatedListener != null && mBindPositionId != null) {
|
||||
mOnTaskUpdatedListener.onTaskUpdated(mBindPositionId, new ArrayList<PositionTaskModel>(mData));
|
||||
}
|
||||
Toast.makeText(getContext(), "任务已删除", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
|
||||
// (2)修改按钮逻辑(弹窗编辑)
|
||||
holder.btnEditTask.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
showTaskEditDialog(task, position);
|
||||
}
|
||||
});
|
||||
|
||||
// (核心修复1:绑定新监听前先移除旧监听,避免重复触发)
|
||||
holder.cbTaskEnable.setOnCheckedChangeListener(null);
|
||||
holder.cbTaskEnable.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||
task.setIsEnable(isChecked); // 直接修改数据源中任务的启用状态
|
||||
notifyItemChanged(position); // 实时刷新当前项(显示最新状态)
|
||||
// 同步通知外部
|
||||
if (mOnTaskUpdatedListener != null && mBindPositionId != null) {
|
||||
mOnTaskUpdatedListener.onTaskUpdated(mBindPositionId, new ArrayList<PositionTaskModel>(mData));
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// 简单模式:隐藏编辑按钮+移除监听
|
||||
holder.btnEditTask.setVisibility(View.GONE);
|
||||
holder.btnDeleteTask.setVisibility(View.GONE);
|
||||
holder.cbTaskEnable.setOnCheckedChangeListener(null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 任务编辑弹窗(核心修复2:修改后实时刷新+同步数据)
|
||||
*/
|
||||
private void showTaskEditDialog(final PositionTaskModel task, final int position) {
|
||||
final Context context = getContext();
|
||||
// 加载弹窗布局(dialog_edit_task.xml)
|
||||
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 RadioGroup rgDistanceCondition = (RadioGroup) dialogView.findViewById(R.id.rg_distance_condition);
|
||||
final EditText etEditDistance = (EditText) dialogView.findViewById(R.id.et_edit_distance);
|
||||
Button btnCancel = (Button) dialogView.findViewById(R.id.btn_dialog_cancel);
|
||||
Button btnSave = (Button) dialogView.findViewById(R.id.btn_dialog_save);
|
||||
|
||||
// 初始化弹窗数据(显示当前任务信息)
|
||||
etEditDesc.setText(task.getTaskDescription());
|
||||
etEditDesc.setSelection(etEditDesc.getText().length()); // 光标定位到末尾
|
||||
|
||||
if (task.isGreaterThan()) {
|
||||
rgDistanceCondition.check(R.id.rb_greater_than);
|
||||
} else {
|
||||
rgDistanceCondition.check(R.id.rb_less_than);
|
||||
}
|
||||
|
||||
etEditDistance.setText(String.valueOf(task.getDiscussDistance()));
|
||||
|
||||
// 创建并显示弹窗
|
||||
final android.app.AlertDialog dialog = new android.app.AlertDialog.Builder(context)
|
||||
.setView(dialogView)
|
||||
.create();
|
||||
dialog.show();
|
||||
|
||||
// 取消按钮:关闭弹窗(不保存)
|
||||
btnCancel.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
dialog.dismiss();
|
||||
}
|
||||
});
|
||||
|
||||
// 保存按钮:校验+更新+同步(核心修复:实时刷新列表)
|
||||
btnSave.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
// 1. 输入校验
|
||||
String newDesc = etEditDesc.getText().toString().trim();
|
||||
String distanceStr = etEditDistance.getText().toString().trim();
|
||||
|
||||
if (distanceStr.isEmpty()) {
|
||||
Toast.makeText(context, "请输入有效距离", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
int newDistance;
|
||||
try {
|
||||
newDistance = Integer.parseInt(distanceStr);
|
||||
if (newDistance < 1) { // 距离最小为1米,避免无效值
|
||||
Toast.makeText(context, "距离不能小于1米", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
Toast.makeText(context, "距离请输入数字", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. 更新任务数据(直接修改数据源中的任务对象,强引用同步)
|
||||
task.setTaskDescription(newDesc);
|
||||
task.setDiscussDistance(newDistance);
|
||||
// 更新距离条件(大于/小于)
|
||||
boolean isGreater = rgDistanceCondition.getCheckedRadioButtonId() == R.id.rb_greater_than;
|
||||
task.setIsGreaterThan(isGreater);
|
||||
// 强制绑定当前位置ID,避免任务串位
|
||||
task.setPositionId(mBindPositionId);
|
||||
|
||||
// 3. 核心修复:实时刷新当前任务项,确保修改后立即显示
|
||||
notifyItemChanged(position);
|
||||
|
||||
// 4. 同步通知外部Adapter更新全局数据
|
||||
if (mOnTaskUpdatedListener != null && mBindPositionId != null) {
|
||||
mOnTaskUpdatedListener.onTaskUpdated(mBindPositionId, new ArrayList<PositionTaskModel>(mData));
|
||||
}
|
||||
|
||||
// 5. 关闭弹窗并提示
|
||||
dialog.dismiss();
|
||||
Toast.makeText(context, "任务已更新", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ---------------------- ViewHolder 定义(内部类,绑定布局控件) ----------------------
|
||||
/**
|
||||
* 基础ViewHolder(抽象类,统一父类型)
|
||||
*/
|
||||
public abstract class TaskViewHolder extends RecyclerView.ViewHolder {
|
||||
public TaskViewHolder(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 空提示ViewHolder(绑定“无任务”提示布局 item_task_empty.xml)
|
||||
*/
|
||||
public class EmptyViewHolder extends TaskViewHolder {
|
||||
public EmptyViewHolder(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
// 根据当前视图模式修改提示文本
|
||||
TextView tvEmptyTip = (TextView) itemView.findViewById(R.id.tv_task_empty_tip);
|
||||
if (mCurrentViewMode == VIEW_MODE_EDIT) {
|
||||
tvEmptyTip.setText("暂无任务,点击\"添加新任务\"创建");
|
||||
} else {
|
||||
tvEmptyTip.setText("暂无启用的任务");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 任务内容ViewHolder(绑定任务项布局 item_task_content.xml)
|
||||
*/
|
||||
public class TaskContentViewHolder extends TaskViewHolder {
|
||||
TextView tvTaskDesc; // 任务描述
|
||||
TextView tvTaskDistance; // 距离条件(如“大于 100 米”)
|
||||
CompoundButton cbTaskEnable; // 任务启用/禁用开关
|
||||
Button btnEditTask; // 编辑任务按钮
|
||||
Button btnDeleteTask; // 删除任务按钮
|
||||
|
||||
public TaskContentViewHolder(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
// 绑定布局控件(与 item_task_content.xml 中的ID严格对应)
|
||||
tvTaskDesc = (TextView) itemView.findViewById(R.id.tv_task_desc);
|
||||
tvTaskDistance = (TextView) itemView.findViewById(R.id.tv_task_distance);
|
||||
cbTaskEnable = (CompoundButton) itemView.findViewById(R.id.cb_task_enable);
|
||||
btnEditTask = (Button) itemView.findViewById(R.id.btn_edit_task);
|
||||
btnDeleteTask = (Button) itemView.findViewById(R.id.btn_delete_task);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
20
positions/src/main/res/drawable/bg_task_item.xml
Normal file
20
positions/src/main/res/drawable/bg_task_item.xml
Normal file
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
|
||||
<solid android:color="@color/white" />
|
||||
|
||||
<corners android:radius="4dp" />
|
||||
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="@color/gray" />
|
||||
|
||||
<padding
|
||||
android:left="8dp"
|
||||
android:top="8dp"
|
||||
android:right="8dp"
|
||||
android:bottom="8dp" />
|
||||
|
||||
</shape>
|
||||
|
||||
26
positions/src/main/res/drawable/item_bg_edit.xml
Normal file
26
positions/src/main/res/drawable/item_bg_edit.xml
Normal file
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- res/drawable/item_bg_edit.xml -->
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<!-- 状态1:按压时(编辑项被点击,反馈明确) -->
|
||||
<item android:state_pressed="true">
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="#E0E6ED" /> <!-- 按压底色:比默认深一点的灰蓝色,反馈清晰 -->
|
||||
<corners android:radius="8dp" /> <!-- 保持圆角统一 -->
|
||||
<stroke
|
||||
android:width="1.5dp"
|
||||
android:color="#A0B4C8" /> <!-- 按压时边框加深,增强视觉焦点 -->
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
<!-- 状态2:默认状态(未按压,区分简单视图) -->
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="#EEEEEE" /> <!-- 默认底色:比简单视图深,明确编辑模式 -->
|
||||
<corners android:radius="8dp" /> <!-- 与简单视图一致的圆角,UI不割裂 -->
|
||||
<stroke
|
||||
android:width="1.5dp"
|
||||
android:color="#D0D8E0" /> <!-- 淡蓝色边框,暗示“可编辑” -->
|
||||
</shape>
|
||||
</item>
|
||||
</selector>
|
||||
|
||||
26
positions/src/main/res/drawable/item_bg_simple.xml
Normal file
26
positions/src/main/res/drawable/item_bg_simple.xml
Normal file
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- res/drawable/item_bg_simple.xml -->
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<!-- 状态1:按压时的背景(深一点的灰色,明确反馈点击动作) -->
|
||||
<item android:state_pressed="true">
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="#E8E8E8" /> <!-- 按压底色(比默认深一点) -->
|
||||
<corners android:radius="8dp" /> <!-- 保持和默认状态一致的圆角 -->
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="#E0E0E0" />
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
<!-- 状态2:默认状态(未按压时的背景,和基础版一致) -->
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="#F5F5F5" /> <!-- 默认浅灰底色 -->
|
||||
<corners android:radius="8dp" /> <!-- 8dp圆角 -->
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="#E0E0E0" /> <!-- 细灰边框 -->
|
||||
</shape>
|
||||
</item>
|
||||
</selector>
|
||||
|
||||
82
positions/src/main/res/layout/dialog_edit_task.xml
Normal file
82
positions/src/main/res/layout/dialog_edit_task.xml
Normal file
@@ -0,0 +1,82 @@
|
||||
<?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="vertical"
|
||||
android:padding="16dp"
|
||||
android:background="@color/white">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/et_edit_task_desc"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="输入任务描述"
|
||||
android:maxLines="1"
|
||||
android:textSize="14sp" />
|
||||
|
||||
<RadioGroup
|
||||
android:id="@+id/rg_distance_condition"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginTop="12dp">
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/rb_greater_than"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="大于"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="14sp" />
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/rb_less_than"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="小于"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="14sp"
|
||||
android:layout_marginLeft="24dp" />
|
||||
|
||||
</RadioGroup>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/et_edit_distance"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="输入距离(米)"
|
||||
android:inputType="number"
|
||||
android:maxLines="1"
|
||||
android:textSize="14sp"
|
||||
android:layout_marginTop="8dp" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="end"
|
||||
android:layout_marginTop="16dp">
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_dialog_cancel"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="40dp"
|
||||
android:text="取消"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="14sp"
|
||||
android:layout_marginRight="8dp"
|
||||
android:background="@color/gray" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_dialog_save"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="40dp"
|
||||
android:text="保存"
|
||||
android:textSize="14sp"
|
||||
android:background="@color/blue"
|
||||
android:textColor="@color/white" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
@@ -1,152 +1,170 @@
|
||||
<?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="vertical"
|
||||
android:padding="12dp"
|
||||
android:background="@drawable/item_position_bg"
|
||||
android:layout_marginBottom="8dp">
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="12dp"
|
||||
android:background="@drawable/item_bg_edit">
|
||||
|
||||
<!-- 1. 经纬度显示(不变) -->
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:layout_marginBottom="12dp">
|
||||
<TextView
|
||||
android:id="@+id/tv_edit_longitude"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="14sp"
|
||||
android:textColor="#999999"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_edit_longitude"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="14sp"
|
||||
android:textColor="#666666"/>
|
||||
<TextView
|
||||
android:id="@+id/tv_edit_latitude"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="14sp"
|
||||
android:textColor="#999999"
|
||||
android:layout_marginTop="4dp"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_edit_latitude"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="14sp"
|
||||
android:textColor="#666666"
|
||||
android:layout_marginTop="5dp"/>
|
||||
<EditText
|
||||
android:id="@+id/et_edit_memo"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="请输入位置备注"
|
||||
android:textSize="14sp"
|
||||
android:padding="8dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:background="@drawable/edittext_bg"/>
|
||||
|
||||
</LinearLayout>
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:layout_marginTop="10dp">
|
||||
|
||||
<!-- 2. 备注输入框(不变,调整下方间距为8dp,与新增单选框区过渡更自然) -->
|
||||
<EditText
|
||||
android:id="@+id/et_edit_memo"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="请输入位置备注(如:公司/家/学校)"
|
||||
android:textSize="16sp"
|
||||
android:inputType="textMultiLine"
|
||||
android:minLines="2"
|
||||
android:maxLines="4"
|
||||
android:padding="10dp"
|
||||
android:background="@drawable/edittext_bg"
|
||||
android:layout_marginBottom="8dp"/> <!-- 原12dp→8dp,优化与单选框的间距 -->
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="14sp"
|
||||
android:textColor="#333333"
|
||||
android:id="@+id/tv_edit_real_distance"/>
|
||||
|
||||
<!-- 新增3:实时距离开关(单选框组)- 控制isEnableRealPositionDistance属性 -->
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_marginBottom="12dp"> <!-- 与下方按钮区保持12dp间距,符合原有布局规范 -->
|
||||
<RadioGroup
|
||||
android:id="@+id/rg_real_distance_switch"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginLeft="8dp">
|
||||
|
||||
<!-- 单选框标题(说明用途,文字颜色与经纬度一致,保持风格统一) -->
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="实时距离计算:"
|
||||
android:textSize="14sp"
|
||||
android:textColor="#666666"
|
||||
android:layout_marginRight="12dp"/> <!-- 与单选框保持12dp间距,避免拥挤 -->
|
||||
<RadioButton
|
||||
android:id="@+id/rb_disable"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="禁用"
|
||||
android:textSize="14sp"
|
||||
android:checked="true"/>
|
||||
|
||||
<!-- 单选框组(RadioGroup):确保“启用/禁用”二选一 -->
|
||||
<RadioGroup
|
||||
android:id="@+id/rg_real_distance_switch"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:checkedButton="@+id/rb_disable"> <!-- 默认选中“禁用”,匹配isEnable默认值false -->
|
||||
<RadioButton
|
||||
android:id="@+id/rb_enable"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="启用"
|
||||
android:textSize="14sp"
|
||||
android:layout_marginLeft="15dp"/>
|
||||
|
||||
<!-- 单选框1:禁用实时距离(对应isEnableRealPositionDistance=false) -->
|
||||
<RadioButton
|
||||
android:id="@+id/rb_disable"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="禁用"
|
||||
android:textSize="13sp"
|
||||
android:textColor="#333333"
|
||||
android:layout_marginRight="16dp"/> <!-- 与“启用”单选框保持16dp间距 -->
|
||||
</RadioGroup>
|
||||
|
||||
<!-- 单选框2:启用实时距离(对应isEnableRealPositionDistance=true) -->
|
||||
<RadioButton
|
||||
android:id="@+id/rb_enable"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="启用"
|
||||
android:textSize="13sp"
|
||||
android:textColor="#333333"/>
|
||||
</LinearLayout>
|
||||
|
||||
</RadioGroup>
|
||||
</LinearLayout>
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginTop="12dp">
|
||||
|
||||
<!-- 4. 按钮区(不变:顺序→删除(左)→取消→确定(右)) -->
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical">
|
||||
<Button
|
||||
android:id="@+id/btn_edit_delete"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="32dp"
|
||||
android:background="@drawable/btn_delete_bg"
|
||||
android:text="删除位置"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="13sp"
|
||||
android:paddingStart="15dp"
|
||||
android:paddingEnd="15dp"/>
|
||||
|
||||
<!-- ① 删除按钮(最左侧,红色) -->
|
||||
<Button
|
||||
android:id="@+id/btn_edit_delete"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="32dp"
|
||||
android:background="@drawable/btn_delete_bg"
|
||||
android:text="删除"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="13sp"
|
||||
android:paddingStart="15dp"
|
||||
android:paddingEnd="15dp"/>
|
||||
<View
|
||||
android:layout_width="10dp"
|
||||
android:layout_height="match_parent"/>
|
||||
|
||||
<!-- 间隔(按钮间间距) -->
|
||||
<View
|
||||
android:layout_width="10dp"
|
||||
android:layout_height="match_parent"/>
|
||||
<Button
|
||||
android:id="@+id/btn_edit_cancel"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="32dp"
|
||||
android:background="@drawable/btn_cancel_bg"
|
||||
android:text="取消"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="13sp"
|
||||
android:paddingStart="15dp"
|
||||
android:paddingEnd="15dp"/>
|
||||
|
||||
<!-- ② 取消按钮(中间,灰色,不保存返回简单视图) -->
|
||||
<Button
|
||||
android:id="@+id/btn_edit_cancel"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="32dp"
|
||||
android:background="@drawable/btn_cancel_bg"
|
||||
android:text="取消"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="13sp"
|
||||
android:paddingStart="15dp"
|
||||
android:paddingEnd="15dp"/>
|
||||
<View
|
||||
android:layout_width="10dp"
|
||||
android:layout_height="match_parent"/>
|
||||
|
||||
<!-- 间隔 -->
|
||||
<View
|
||||
android:layout_width="10dp"
|
||||
android:layout_height="match_parent"/>
|
||||
<Button
|
||||
android:id="@+id/btn_edit_confirm"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="32dp"
|
||||
android:background="@drawable/btn_confirm_bg"
|
||||
android:text="确定"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="13sp"
|
||||
android:paddingStart="15dp"
|
||||
android:paddingEnd="15dp"/>
|
||||
|
||||
<!-- ③ 确定按钮(最右侧,蓝色,保存并返回简单视图) -->
|
||||
<Button
|
||||
android:id="@+id/btn_edit_confirm"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="32dp"
|
||||
android:background="@drawable/btn_confirm_bg"
|
||||
android:text="确定"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="13sp"
|
||||
android:paddingStart="15dp"
|
||||
android:paddingEnd="15dp"/>
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:layout_marginTop="16dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="30dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center_vertical"
|
||||
android:text="关联任务(可编辑):"
|
||||
android:textSize="14sp"
|
||||
android:textColor="#333333"/>
|
||||
|
||||
<View
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_add_task"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@drawable/btn_confirm_bg"
|
||||
android:text="添加新任务"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="12sp"
|
||||
android:paddingStart="12dp"
|
||||
android:paddingEnd="12dp"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<cc.winboll.studio.positions.views.PositionTaskListView
|
||||
android:id="@+id/ptlv_edit_tasks"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="400dp"
|
||||
android:layout_marginTop="6dp"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
@@ -1,49 +1,55 @@
|
||||
<?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="vertical"
|
||||
android:padding="12dp"
|
||||
android:background="@drawable/item_position_bg"
|
||||
android:layout_marginBottom="8dp">
|
||||
|
||||
<!-- 经度 -->
|
||||
android:background="@drawable/item_bg_simple">
|
||||
<TextView
|
||||
android:id="@+id/tv_simple_longitude"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="14sp"
|
||||
android:textColor="#666666"/>
|
||||
android:textColor="#333333"/>
|
||||
|
||||
<!-- 纬度 -->
|
||||
<TextView
|
||||
android:id="@+id/tv_simple_latitude"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="14sp"
|
||||
android:textColor="#666666"
|
||||
android:layout_marginTop="5dp"/>
|
||||
android:textColor="#333333"
|
||||
android:layout_marginTop="4dp"/>
|
||||
|
||||
<!-- 备注(加粗突出) -->
|
||||
<TextView
|
||||
android:id="@+id/tv_simple_memo"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="16sp"
|
||||
android:textColor="#000000"
|
||||
android:textStyle="bold"
|
||||
android:layout_marginTop="8dp"/>
|
||||
android:textSize="14sp"
|
||||
android:textColor="#666666"
|
||||
android:layout_marginTop="4dp"/>
|
||||
|
||||
<!-- 新增:实时距离显示(距离计算结果/未启用提示) -->
|
||||
<TextView
|
||||
android:id="@+id/tv_simple_real_distance"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="14sp"
|
||||
android:textColor="#2196F3"
|
||||
android:layout_marginTop="6dp"
|
||||
android:text="实时距离未启用"/> <!-- 默认提示(isEnable=false时显示) -->
|
||||
android:textColor="#2E8B57"
|
||||
android:layout_marginTop="6dp"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="关联任务(已启用):"
|
||||
android:textSize="14sp"
|
||||
android:textColor="#333333"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginBottom="4dp"/>
|
||||
|
||||
<cc.winboll.studio.positions.views.PositionTaskListView
|
||||
android:id="@+id/ptlv_simple_tasks"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="200dp"
|
||||
android:layout_marginTop="4dp"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
184
positions/src/main/res/layout/item_position_task_edit.xml
Normal file
184
positions/src/main/res/layout/item_position_task_edit.xml
Normal file
@@ -0,0 +1,184 @@
|
||||
<?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="vertical"
|
||||
android:padding="12dp"
|
||||
android:background="@drawable/item_bg_edit"
|
||||
android:layout_marginBottom="8dp">
|
||||
|
||||
<!-- 1. 不可编辑字段(仅显示,灰色文字) -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:layout_marginBottom="10dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_edit_task_id"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="12sp"
|
||||
android:textColor="#999999"
|
||||
android:text="任务ID:"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_edit_position_id"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="12sp"
|
||||
android:textColor="#999999"
|
||||
android:layout_marginTop="2dp"
|
||||
android:text="位置ID:"/>
|
||||
</LinearLayout>
|
||||
|
||||
<!-- 2. 任务描述(可编辑) -->
|
||||
<EditText
|
||||
android:id="@+id/et_edit_task_desc"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="请输入任务描述(如:到达后提醒打卡)"
|
||||
android:textSize="14sp"
|
||||
android:padding="8dp"
|
||||
android:background="@drawable/edittext_bg"
|
||||
android:layout_marginBottom="10dp"/>
|
||||
|
||||
<!-- 3. 距离条件(单选组:大于/小于) -->
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginBottom="10dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="距离条件:"
|
||||
android:textSize="14sp"
|
||||
android:textColor="#333333"
|
||||
android:layout_marginRight="10dp"/>
|
||||
|
||||
<RadioGroup
|
||||
android:id="@+id/rg_edit_distance_cond"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/rb_edit_greater"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="大于"
|
||||
android:textSize="14sp"
|
||||
android:layout_marginRight="15dp"/>
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/rb_edit_less"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="小于"
|
||||
android:textSize="14sp"/>
|
||||
</RadioGroup>
|
||||
</LinearLayout>
|
||||
|
||||
<!-- 4. 商议距离(可编辑,仅整数) -->
|
||||
<EditText
|
||||
android:id="@+id/et_edit_discuss_dist"
|
||||
android:layout_width="120dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="请输入距离(米)"
|
||||
android:textSize="14sp"
|
||||
android:inputType="number"
|
||||
android:padding="8dp"
|
||||
android:background="@drawable/edittext_bg"
|
||||
android:layout_marginBottom="10dp"/>
|
||||
|
||||
<!-- 5. 启用状态(单选组:是/否) -->
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginBottom="12dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="启用任务:"
|
||||
android:textSize="14sp"
|
||||
android:textColor="#333333"
|
||||
android:layout_marginRight="10dp"/>
|
||||
|
||||
<RadioGroup
|
||||
android:id="@+id/rg_edit_is_enable"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/rb_edit_enable_yes"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="是"
|
||||
android:textSize="14sp"
|
||||
android:layout_marginRight="15dp"/>
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/rb_edit_enable_no"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="否"
|
||||
android:textSize="14sp"/>
|
||||
</RadioGroup>
|
||||
</LinearLayout>
|
||||
|
||||
<!-- 6. 功能按钮(删除-红 / 取消-灰 / 确定-蓝) -->
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_edit_delete"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="32dp"
|
||||
android:background="@drawable/btn_delete_bg"
|
||||
android:text="删除"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="13sp"
|
||||
android:paddingStart="15dp"
|
||||
android:paddingEnd="15dp"/>
|
||||
|
||||
<View
|
||||
android:layout_width="10dp"
|
||||
android:layout_height="match_parent"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_edit_cancel"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="32dp"
|
||||
android:background="@drawable/btn_cancel_bg"
|
||||
android:text="取消"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="13sp"
|
||||
android:paddingStart="15dp"
|
||||
android:paddingEnd="15dp"/>
|
||||
|
||||
<View
|
||||
android:layout_width="10dp"
|
||||
android:layout_height="match_parent"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_edit_confirm"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="32dp"
|
||||
android:background="@drawable/btn_confirm_bg"
|
||||
android:text="确定"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="13sp"
|
||||
android:paddingStart="15dp"
|
||||
android:paddingEnd="15dp"/>
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
40
positions/src/main/res/layout/item_position_task_simple.xml
Normal file
40
positions/src/main/res/layout/item_position_task_simple.xml
Normal file
@@ -0,0 +1,40 @@
|
||||
<?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="vertical"
|
||||
android:padding="12dp"
|
||||
android:background="@drawable/item_bg_simple"
|
||||
android:layout_marginBottom="8dp">
|
||||
|
||||
<!-- 任务描述 -->
|
||||
<TextView
|
||||
android:id="@+id/tv_simple_task_desc"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="16sp"
|
||||
android:textColor="#333333"
|
||||
android:text="任务:无描述"/>
|
||||
|
||||
<!-- 距离条件 -->
|
||||
<TextView
|
||||
android:id="@+id/tv_simple_distance_cond"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="14sp"
|
||||
android:textColor="#666666"
|
||||
android:layout_marginTop="6dp"
|
||||
android:text="条件:距离 > 0 米"/>
|
||||
|
||||
<!-- 启用状态(简单视图仅显示“已启用”) -->
|
||||
<TextView
|
||||
android:id="@+id/tv_simple_is_enable"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="14sp"
|
||||
android:textColor="#2E8B57"
|
||||
android:layout_marginTop="4dp"
|
||||
android:text="状态:已启用"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
70
positions/src/main/res/layout/item_task_content.xml
Normal file
70
positions/src/main/res/layout/item_task_content.xml
Normal file
@@ -0,0 +1,70 @@
|
||||
<?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:gravity="center_vertical"
|
||||
android:padding="8dp"
|
||||
android:background="@drawable/bg_task_item"
|
||||
android:layout_marginVertical="4dp">
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/cb_task_enable"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginRight="12dp" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_task_desc"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="任务描述"
|
||||
android:textSize="16sp"
|
||||
android:textColor="@color/black" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_task_distance"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="条件:大于 100 米"
|
||||
android:textSize="12sp"
|
||||
android:textColor="@color/gray_dark"
|
||||
android:layout_marginTop="2dp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginLeft="8dp">
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_edit_task"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="30dp"
|
||||
android:text="修改"
|
||||
android:textSize="12sp"
|
||||
android:layout_marginRight="4dp"
|
||||
android:background="@color/blue"
|
||||
android:textColor="@color/white" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_delete_task"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="30dp"
|
||||
android:text="删除"
|
||||
android:textSize="12sp"
|
||||
android:background="@color/red"
|
||||
android:textColor="@color/white" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
18
positions/src/main/res/layout/item_task_empty.xml
Normal file
18
positions/src/main/res/layout/item_task_empty.xml
Normal file
@@ -0,0 +1,18 @@
|
||||
<?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="80dp"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:background="@color/background_light">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_task_empty_tip"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="暂无任务"
|
||||
android:textColor="@color/gray"
|
||||
android:textSize="14sp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
16
positions/src/main/res/layout/view_position_task_list.xml
Normal file
16
positions/src/main/res/layout/view_position_task_list.xml
Normal file
@@ -0,0 +1,16 @@
|
||||
<?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="vertical"
|
||||
android:padding="8dp">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rv_position_tasks"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/white"
|
||||
android:paddingVertical="4dp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
@@ -3,4 +3,31 @@
|
||||
<color name="colorPrimary">#009688</color>
|
||||
<color name="colorPrimaryDark">#00796B</color>
|
||||
<color name="colorAccent">#FF9800</color>
|
||||
</resources>
|
||||
<!-- 1. colorEnableGreen:简单视图“已启用”状态文字颜色(柔和绿色,不刺眼) -->
|
||||
<color name="colorEnableGreen">#2E8B57</color> <!-- 海绿色:比纯绿柔和,适合文字提示 -->
|
||||
|
||||
<!-- 2. colorGrayText:编辑视图“不可编辑字段”文字颜色(浅灰色,暗示“不可修改”) -->
|
||||
<color name="colorGrayText">#999999</color> <!-- 中浅灰:既不模糊,又能和可编辑文字区分开 -->
|
||||
|
||||
<!-- (可选补充)适配之前的背景资源颜色(避免后续新增资源报错,提前定义) -->
|
||||
<color name="colorItemSimpleBg">#F5F5F5</color> <!-- item_bg_simple 底色 -->
|
||||
<color name="colorItemEditBg">#EEEEEE</color> <!-- item_bg_edit 底色 -->
|
||||
<color name="colorItemBorder">#E0E0E0</color> <!-- 列表项边框颜色 -->
|
||||
<color name="colorRed">#F44336</color> <!-- 标准 Material 红色,可自定义RGB值 -->
|
||||
|
||||
<!-- 基础颜色:解决@color/gray未定义问题 -->
|
||||
<color name="gray">#9E9E9E</color> <!-- 浅灰色(用于提示文本、次要信息) -->
|
||||
<color name="gray_dark">#616161</color> <!-- 深灰色(备用,避免后续扩展缺失) -->
|
||||
|
||||
<!-- 其他已用颜色:确保布局中所有颜色引用都有定义 -->
|
||||
<color name="white">#FFFFFF</color> <!-- 白色(任务列表背景、弹窗背景) -->
|
||||
<color name="black">#000000</color> <!-- 黑色(任务描述文本) -->
|
||||
<color name="blue">#2196F3</color> <!-- 蓝色(编辑/保存按钮背景) -->
|
||||
<color name="red">#F44336</color> <!-- 红色(删除按钮背景) -->
|
||||
<color name="background_light">#F5F5F5</color> <!-- 浅灰背景(备用,提升页面质感) -->
|
||||
|
||||
<!-- 扩展颜色(避免后续新增功能时再次缺失) -->
|
||||
<color name="green">#4CAF50</color> <!-- 绿色(备用:如“启用”状态标识) -->
|
||||
<color name="yellow">#FFC107</color> <!-- 黄色(备用:如“提醒”标识) -->
|
||||
|
||||
</resources>
|
||||
|
||||
Reference in New Issue
Block a user