From dee95867489da4f5256817e54e123d391ce59720 Mon Sep 17 00:00:00 2001 From: ZhanGSKen Date: Tue, 30 Sep 2025 01:54:11 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E4=BD=8D=E7=BD=AE=E4=BF=A1?= =?UTF-8?q?=E6=81=AF=E7=BC=96=E8=BE=91=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- positions/build.properties | 4 +- .../activities/LocationActivity.java | 127 ++++++++- .../positions/adapters/PositionAdapter.java | 259 ++++++++++++++++++ .../positions/models/PositionModel.java | 10 + .../src/main/res/drawable/btn_cancel_bg.xml | 18 ++ .../src/main/res/drawable/btn_confirm_bg.xml | 18 ++ .../src/main/res/drawable/btn_delete_bg.xml | 16 ++ .../src/main/res/drawable/edittext_bg.xml | 18 ++ .../main/res/drawable/item_position_bg.xml | 18 ++ .../src/main/res/layout/activity_location.xml | 25 +- .../main/res/layout/dialog_edit_position.xml | 41 +++ .../src/main/res/layout/item_position.xml | 60 ++++ .../main/res/layout/item_position_edit.xml | 105 +++++++ .../main/res/layout/item_position_simple.xml | 39 +++ .../src/main/res/menu/menu_item_edit.xml | 9 + 15 files changed, 752 insertions(+), 15 deletions(-) create mode 100644 positions/src/main/java/cc/winboll/studio/positions/adapters/PositionAdapter.java create mode 100644 positions/src/main/res/drawable/btn_cancel_bg.xml create mode 100644 positions/src/main/res/drawable/btn_confirm_bg.xml create mode 100644 positions/src/main/res/drawable/btn_delete_bg.xml create mode 100644 positions/src/main/res/drawable/edittext_bg.xml create mode 100644 positions/src/main/res/drawable/item_position_bg.xml create mode 100644 positions/src/main/res/layout/dialog_edit_position.xml create mode 100644 positions/src/main/res/layout/item_position.xml create mode 100644 positions/src/main/res/layout/item_position_edit.xml create mode 100644 positions/src/main/res/layout/item_position_simple.xml create mode 100644 positions/src/main/res/menu/menu_item_edit.xml diff --git a/positions/build.properties b/positions/build.properties index b5d68b6..b21727d 100644 --- a/positions/build.properties +++ b/positions/build.properties @@ -1,8 +1,8 @@ #Created by .winboll/winboll_app_build.gradle -#Mon Sep 29 12:02:00 GMT 2025 +#Mon Sep 29 17:49:24 GMT 2025 stageCount=3 libraryProject= baseVersion=15.0 publishVersion=15.0.2 -buildCount=4 +buildCount=12 baseBetaVersion=15.0.3 diff --git a/positions/src/main/java/cc/winboll/studio/positions/activities/LocationActivity.java b/positions/src/main/java/cc/winboll/studio/positions/activities/LocationActivity.java index a328e8b..884dc3f 100644 --- a/positions/src/main/java/cc/winboll/studio/positions/activities/LocationActivity.java +++ b/positions/src/main/java/cc/winboll/studio/positions/activities/LocationActivity.java @@ -6,17 +6,28 @@ package cc.winboll.studio.positions.activities; * @Describe 当前位置实时显示 */ import android.Manifest; +import android.app.AlertDialog; +import android.content.DialogInterface; import android.content.pm.PackageManager; import android.location.Location; import android.os.Bundle; +import android.text.InputType; +import android.view.GestureDetector; +import android.view.MotionEvent; import android.view.View; import android.widget.Button; +import android.widget.EditText; import android.widget.TextView; import android.widget.Toast; import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import androidx.core.app.ActivityCompat; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; import cc.winboll.studio.positions.R; +import cc.winboll.studio.positions.activities.LocationActivity; +import cc.winboll.studio.positions.adapters.PositionAdapter; +import cc.winboll.studio.positions.models.PositionModel; import com.google.android.gms.location.FusedLocationProviderClient; import com.google.android.gms.location.LocationCallback; import com.google.android.gms.location.LocationRequest; @@ -24,11 +35,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 android.app.AlertDialog; -import android.content.DialogInterface; -import android.widget.EditText; -import android.text.InputType; -import cc.winboll.studio.positions.models.PositionModel; +import android.view.LayoutInflater; /** * 实时定位活动窗口: @@ -51,6 +58,9 @@ public class LocationActivity extends AppCompatActivity { private TextView tvLongitude; // 经度显示 private TextView tvLatitude; // 纬度显示 private Button fabPButton; // 右下角圆形悬浮按钮(P字母) + // UI控件:新增RecyclerView和Adapter + private RecyclerView rvPositionList; // 位置列表(RecyclerView) + private PositionAdapter positionAdapter; // 列表Adapter(数据联动) ArrayList mPositionList = new ArrayList(); @Override @@ -67,6 +77,15 @@ public class LocationActivity extends AppCompatActivity { initLocationConfig(); // 初始化悬浮按钮(设置点击事件) initFabPButton(); + + // 新增1:绑定RecyclerView控件(显式强转,适配Java 7) + rvPositionList = (RecyclerView) findViewById(R.id.rv_position_list); + + // 新增2:初始化Adapter(传入mPositionList,确保数据联动) + initRecyclerViewAndAdapter(); + + // 新增3:从本地加载历史位置数据(BaseBean的saveBeanList对应加载方法) + loadHistoryPositions(); // 检查并申请定位权限 if (checkLocationPermissions()) { @@ -122,6 +141,90 @@ public class LocationActivity extends AppCompatActivity { } }; } + + private void initRecyclerViewAndAdapter() { + // 1. 初始化Adapter(传入Context和mPositionList,适配新构造函数) + positionAdapter = new PositionAdapter(this, mPositionList); + rvPositionList.setAdapter(positionAdapter); + + // 2. 设置删除监听(编辑视图点击“删除”时触发) + positionAdapter.setOnDeleteClickListener(new PositionAdapter.OnDeleteClickListener() { + @Override + public void onDeleteClick(int position) { + // 弹删除确认对话框(复用之前的showDeleteConfirmDialog方法) + showDeleteConfirmDialog(position); + } + }); + + // 3. 设置保存监听(编辑视图点击“确定”时触发,同步本地数据) + positionAdapter.setOnSaveClickListener(new PositionAdapter.OnSaveClickListener() { + @Override + public void onSaveClick() { + // 编辑后保存到本地 + try { + PositionModel.saveBeanList(LocationActivity.this, mPositionList, PositionModel.class); + } catch (Exception e) { + e.printStackTrace(); + Toast.makeText(LocationActivity.this, "数据保存失败", Toast.LENGTH_SHORT).show(); + } + } + }); + + // 4. 原有布局管理器设置(保留不变) + RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(this); + ((LinearLayoutManager) layoutManager).setOrientation(LinearLayoutManager.VERTICAL); + rvPositionList.setLayoutManager(layoutManager); + } + +// 保留原删除确认对话框(无修改,编辑视图点击“删除”会触发此方法) + private void showDeleteConfirmDialog(final int position) { + AlertDialog.Builder deleteDialogBuilder = new AlertDialog.Builder(this); + deleteDialogBuilder.setTitle("删除位置记录") + .setMessage("确定要删除这条位置记录吗?(删除后不可恢复)") + .setPositiveButton("确定", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + positionAdapter.removePosition(position); + // 删除后同步本地数据 + 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(); + } + } + }) + .setNegativeButton("取消", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + } + }) + .setCancelable(false); + deleteDialogBuilder.show(); + } + + /** + * 从本地加载历史位置数据: + * 基于BaseBean的saveBeanList方法,对应加载方法为readBeanList(通用持久化逻辑) + */ + private void loadHistoryPositions() { + try { + // 读取本地保存的PositionModel列表(参数:上下文、数据模型类) + ArrayList historyList = new ArrayList(); + PositionModel.loadBeanList(LocationActivity.this, historyList, PositionModel.class); + if (historyList != null && !historyList.isEmpty()) { + mPositionList.clear(); // 清空原有空列表 + mPositionList.addAll(historyList); // 添加历史数据 + positionAdapter.notifyDataSetChanged(); // 通知Adapter更新列表 + } + } catch (Exception e) { + // 异常处理(如首次启动无历史数据,不提示) + e.printStackTrace(); + } + } + /** @@ -217,10 +320,20 @@ public class LocationActivity extends AppCompatActivity { @Override protected void onDestroy() { super.onDestroy(); - // Java 7条件判断格式无差异,保持空指针防护 + + // 1. 停止定位监听(原有逻辑,保留) if (fusedLocationClient != null && locationCallback != null) { fusedLocationClient.removeLocationUpdates(locationCallback); } + + // 2. 补充:销毁前同步一次数据(确保最后修改的记录被保存) + try { + if (mPositionList != null && !mPositionList.isEmpty()) { + PositionModel.saveBeanList(this, mPositionList, PositionModel.class); + } + } catch (Exception e) { + e.printStackTrace(); + } } /** @@ -285,7 +398,7 @@ public class LocationActivity extends AppCompatActivity { // 5. 配置对话框其他属性(禁止点击外部关闭、禁止按返回键关闭) dialogBuilder.setCancelable(false); // 点击对话框外部不关闭 AlertDialog remarkDialog = dialogBuilder.create(); // 创建对话框实例 - remarkDialog.setCanceledOnTouchOutside(false); // 再次确认“外部点击不关闭”(兼容部分机型) + remarkDialog.setCanceledOnTouchOutside(true); // 再次确认“外部点击不关闭”(兼容部分机型) // 6. 显示对话框 remarkDialog.show(); diff --git a/positions/src/main/java/cc/winboll/studio/positions/adapters/PositionAdapter.java b/positions/src/main/java/cc/winboll/studio/positions/adapters/PositionAdapter.java new file mode 100644 index 0000000..04d5865 --- /dev/null +++ b/positions/src/main/java/cc/winboll/studio/positions/adapters/PositionAdapter.java @@ -0,0 +1,259 @@ +package cc.winboll.studio.positions.adapters; + +/** + * @Author ZhanGSKen&豆包大模型 + * @Date 2025/09/29 20:25 + * @Describe 位置数据适配器(支持简单视图/编辑视图切换) + */ +import android.content.Context; +import android.view.LayoutInflater; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.EditText; +import android.widget.PopupMenu; +import android.widget.TextView; +import android.widget.Toast; +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; +import cc.winboll.studio.positions.R; +import cc.winboll.studio.positions.models.PositionModel; +import java.util.ArrayList; + +public class PositionAdapter extends RecyclerView.Adapter { + public static final String TAG = "PositionAdapter"; + private static final int VIEW_TYPE_SIMPLE = 1; // 简单视图 + private static final int VIEW_TYPE_EDIT = 2; // 编辑视图 + + private ArrayList mPositionList; + private Context mContext; + + // 接口定义(不变) + public interface OnDeleteClickListener { + void onDeleteClick(int position); + } + + public interface OnSaveClickListener { + void onSaveClick(); + } + + private OnDeleteClickListener mOnDeleteClickListener; + private OnSaveClickListener mOnSaveClickListener; + + // 构造函数(不变) + public PositionAdapter(Context context, ArrayList positionList) { + this.mContext = context; + this.mPositionList = positionList; + } + + // 设置监听(不变) + public void setOnDeleteClickListener(OnDeleteClickListener listener) { + this.mOnDeleteClickListener = listener; + } + + public void setOnSaveClickListener(OnSaveClickListener listener) { + this.mOnSaveClickListener = listener; + } + + // 视图类型判断(不变) + @Override + public int getItemViewType(int position) { + PositionModel model = mPositionList.get(position); + return model.isSimpleView() ? VIEW_TYPE_SIMPLE : VIEW_TYPE_EDIT; + } + + // 创建ViewHolder(不变,布局已在xml中修改) + @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); + if (holder instanceof SimpleViewHolder) { + bindSimpleView((SimpleViewHolder) holder, model, position); + } else if (holder instanceof EditViewHolder) { + // 编辑视图绑定:新增取消按钮逻辑 + bindEditView((EditViewHolder) holder, model, position); + } + } + + // 简单视图绑定(不变) + 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)); + + holder.itemView.setOnLongClickListener(new View.OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + PopupMenu popupMenu = new PopupMenu(mContext, v); + MenuInflater menuInflater = popupMenu.getMenuInflater(); + menuInflater.inflate(R.menu.menu_item_edit, popupMenu.getMenu()); + + popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { + @Override + public boolean onMenuItemClick(MenuItem item) { + if (item.getItemId() == R.id.menu_edit) { + model.setIsSimpleView(false); + notifyItemChanged(position); + return true; + } + return false; + } + }); + popupMenu.show(); + return true; + } + }); + } + + // 编辑视图绑定(核心修改:新增取消按钮+调整按钮顺序) + private void bindEditView(final EditViewHolder holder, final PositionModel model, final int position) { + // 1. 绑定数据(不变) + 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()); + + // 2. 按钮逻辑(顺序:①删除→②取消→③确定) + // ① 删除按钮(最左侧,逻辑不变) + holder.btnEditDelete.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (mOnDeleteClickListener != null) { + mOnDeleteClickListener.onDeleteClick(position); + } + } + }); + + // ② 新增:取消按钮(中间,不保存→切回简单视图) + holder.btnEditCancel.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + // 关键:不保存任何修改,直接设置为简单视图 + model.setIsSimpleView(true); + // 局部刷新当前项→切换回简单视图 + notifyItemChanged(position); + // (可选)清空输入框焦点,避免残留光标 + holder.etEditMemo.clearFocus(); + } + }); + + // ③ 确定按钮(最右侧,逻辑不变) + 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.setIsSimpleView(true); + notifyItemChanged(position); + + if (mOnSaveClickListener != null) { + mOnSaveClickListener.onSaveClick(); + } + Toast.makeText(mContext, "备注已保存", Toast.LENGTH_SHORT).show(); + } + }); + } + + // ---------------------- ViewHolder 定义(核心修改:新增取消按钮引用) ---------------------- + /** + * 简单视图Holder(不变) + */ + public static class SimpleViewHolder extends RecyclerView.ViewHolder { + TextView tvSimpleLongitude; + TextView tvSimpleLatitude; + TextView tvSimpleMemo; + + 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); + } + } + + /** + * 编辑视图Holder(核心修改:新增btnEditCancel引用) + */ + public static class EditViewHolder extends RecyclerView.ViewHolder { + TextView tvEditLongitude; + TextView tvEditLatitude; + EditText etEditMemo; + Button btnEditDelete; // 删除按钮(已调整到最左) + Button btnEditCancel; // 新增:取消按钮(中间) + Button btnEditConfirm; // 确定按钮(最右) + + public EditViewHolder(@NonNull View itemView) { + super(itemView); + // 绑定控件(顺序与xml一致:删除→取消→确定) + 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); // 最右 + } + } + + // 数据存取方法(不变) + public void addPosition(PositionModel model) { + if (mPositionList != null && model != null) { + model.setIsSimpleView(true); + mPositionList.add(model); + notifyItemInserted(mPositionList.size() - 1); + } + } + + public void removePosition(int position) { + if (mPositionList != null && position >= 0 && position < mPositionList.size()) { + mPositionList.remove(position); + notifyItemRemoved(position); + notifyItemRangeChanged(position, mPositionList.size()); + } + } + + public void updateAllPositions(ArrayList newList) { + if (newList != null) { + mPositionList.clear(); + for (PositionModel model : newList) { + model.setIsSimpleView(true); + } + mPositionList.addAll(newList); + notifyDataSetChanged(); + } + } + + public ArrayList getPositionList() { + return mPositionList; + } + + public void switchAllToSimpleView() { + if (mPositionList != null) { + for (PositionModel model : mPositionList) { + model.setIsSimpleView(true); + } + notifyDataSetChanged(); + } + } + + @Override + public int getItemCount() { + return mPositionList == null ? 0 : mPositionList.size(); + } +} + diff --git a/positions/src/main/java/cc/winboll/studio/positions/models/PositionModel.java b/positions/src/main/java/cc/winboll/studio/positions/models/PositionModel.java index 1b6b024..3abcf19 100644 --- a/positions/src/main/java/cc/winboll/studio/positions/models/PositionModel.java +++ b/positions/src/main/java/cc/winboll/studio/positions/models/PositionModel.java @@ -19,6 +19,8 @@ public class PositionModel extends BaseBean { double latitude; // 位置信息备注 String memo; + // 是否是简单视图 + boolean isSimpleView = true; public PositionModel(double longitude, double latitude, String memo) { this.longitude = longitude; @@ -32,6 +34,14 @@ public class PositionModel extends BaseBean { this.memo = ""; } + public void setIsSimpleView(boolean isSimpleView) { + this.isSimpleView = isSimpleView; + } + + public boolean isSimpleView() { + return isSimpleView; + } + public void setMemo(String memo) { this.memo = memo; } diff --git a/positions/src/main/res/drawable/btn_cancel_bg.xml b/positions/src/main/res/drawable/btn_cancel_bg.xml new file mode 100644 index 0000000..74ca496 --- /dev/null +++ b/positions/src/main/res/drawable/btn_cancel_bg.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/positions/src/main/res/drawable/btn_confirm_bg.xml b/positions/src/main/res/drawable/btn_confirm_bg.xml new file mode 100644 index 0000000..9524b33 --- /dev/null +++ b/positions/src/main/res/drawable/btn_confirm_bg.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/positions/src/main/res/drawable/btn_delete_bg.xml b/positions/src/main/res/drawable/btn_delete_bg.xml new file mode 100644 index 0000000..c7f8623 --- /dev/null +++ b/positions/src/main/res/drawable/btn_delete_bg.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/positions/src/main/res/drawable/edittext_bg.xml b/positions/src/main/res/drawable/edittext_bg.xml new file mode 100644 index 0000000..85b3d28 --- /dev/null +++ b/positions/src/main/res/drawable/edittext_bg.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/positions/src/main/res/drawable/item_position_bg.xml b/positions/src/main/res/drawable/item_position_bg.xml new file mode 100644 index 0000000..a6dba44 --- /dev/null +++ b/positions/src/main/res/drawable/item_position_bg.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/positions/src/main/res/layout/activity_location.xml b/positions/src/main/res/layout/activity_location.xml index 8303f07..af9064c 100644 --- a/positions/src/main/res/layout/activity_location.xml +++ b/positions/src/main/res/layout/activity_location.xml @@ -1,15 +1,16 @@ - - + @@ -27,7 +28,8 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="当前经度:等待更新..." - android:textSize="18sp"/> + android:textSize="18sp" + android:layout_marginTop="15dp"/> + android:textSize="18sp" + android:layout_marginTop="10dp"/> - + + + +