SearchView中onQueryTextSubmit()执行两次的原因和解决办法

在Android应用中,SearchView的onQueryTextSubmit()方法可能因设备差异导致执行两次,造成搜索结果重复。问题源于ACTION_DOWN和ACTION_UP事件,可通过在提交后立即iconify SearchView以清空查询文本,防止额外请求。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在ActionBar上使用SearchView进行搜索时,我们一般使用searchview.setOnQueryTextListener()中的两个方法来提交搜索结果。我个人项目的代码如下,对SearchView中如何进行搜索操作不是很熟的同学也可以小小参考下,

sv.setOnQueryTextListener(new OnQueryTextListener(){

			@Override
			public boolean onQueryTextChange(String newText) {
				// TODO Auto-generated method stub
				return true;
			}

			@Override
			public boolean onQueryTextSubmit(String query) {
				list.clear();
				href.clear();
				String str = null;
				try {//encoding Chinese character
					str = new String(query
							.trim().getBytes(), "ISO-8859-1");
					} catch (UnsupportedEncodingException e) {
						e.printStackTrace();
					}
				SearchPost sp = new SearchPost(SEARCH_URL + str);
				new Thread(sp).start(); 
				
				return true;
			}
		});
项目在虚拟机上测试时一切正常,但是在本人的DesireHD上测试时,搜索结果却被显示了两遍(本应是1,2,3,结果是1,2,3,1,2,3,)。

在handler类、线程类与submit方法中打断点一步步调试,我发现是submit这个方法被触发了两次,也就是说点击了键盘上search按钮后,里面的代码被先后执行了两次,而这个间隔时间比我的线程(需要联网,所以比较耗时)执行所花地时间要短,导致两次联网得到的结果在handler里一次性显示了,造成了搜索结果的重复。


在stackoverflow网站上提问,都没能解决该问题。后来在https://code.google.com/p/android/issues/detail?id=24599 这个网站,发现了门道:

But if I have the keyboard dock attached, the only way to search is typing something and then pressing Enter. When doing this, I get TWO invocations to the same method. Looking at the stack trace, they are the result of two KeyEvents: ACTION_DOWN and ACTION_UP, both associated to KEYCODE_ENTER.
也就是说,是按下键盘上得search键和松开search键的过程中,产生了ACTION_DOWN和ACITON_UP两个消息,并且两个消息都触发了onQueryTextSubmit()方法。然后,我在自己的手机上测试,按下search键不松开,果然显示了正常的没有重复的搜索结果。

至此,问题差不多明白了:在某些机型(比如我的DesireHD)上,按下搜索键产生的两个消息会使submit方法触发两次,这是SDK或是手机中我们作为开发者无法解决的bug。


如何解决?既然submit方法会被ACTION_DOWN和ACTION_UP触发两次,两次触发之间的间隔就是用户按下和松开search键时的间隔。而我们知道,当搜索框中的字为空时,submit方法中得代码是不会被执行的。所以,只需要在执行一次之后,清空搜索框中输入的内容边可以解决这一问题~~~

so,在我的代码中,new Thread(sp).start();之后加上一行代码,sv.setIconified(),这个方法会清空QueryText,这样ACTION_UP的消息就不会再开启新submit方法中新的线程啦~~

package com.example.kucun2.ui.dingdan; import android.app.AlertDialog; import android.graphics.Color; import android.graphics.Typeface; import android.os.Bundle; import android.util.TypedValue; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.HorizontalScrollView; import android.widget.PopupMenu; import android.widget.SearchView; import android.widget.Spinner; import android.widget.TableLayout; import android.widget.TableRow; import android.widget.TextView; import android.widget.Toast; import androidx.core.content.ContextCompat; import androidx.fragment.app.Fragment; import com.example.kucun2.MainActivity; import com.example.kucun2.R; import com.example.kucun2.View.HorizontalScrollTextView; import com.example.kucun2.entity.Bancai; import com.example.kucun2.entity.Chanpin; import com.example.kucun2.entity.Chanpin_Zujian; import com.example.kucun2.entity.Dingdan; import com.example.kucun2.entity.Dingdan_chanpin_zujian; import com.example.kucun2.entity.Dingdan_Chanpin; import com.example.kucun2.entity.Zujian; import com.example.kucun2.entity.data.Data; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; /** * 订单展示Fragment - 用于展示订单、产品、组件板材的四级关系数据 * * 主要功能: * 1. 从全局数据源加载订单数据 * 2. 建立订单->产品->组件->板材的四级关系映射 * 3. 提供表格展示数据 * 4. 支持按列排序 * 5. 支持多列搜索过滤 * 6. 提供行操作(查看详情、编辑、删除) */ public class OrderDisplayFragment extends Fragment { //=== 视图组件 ===// private TableLayout table; // 表格布局,用于显示数据行 private HorizontalScrollView horizontalScrollView; // 水平滚动容器,容纳表格 private SearchView searchView; // 搜索框,用于数据过滤 private Spinner columnSelector; // 列选择器,指定搜索列 private View rootView; // Fragment的根视图 //=== 数据管理 ===// private List<Object[]> allTableRowsData = new ArrayList<>(); // 所有数据行(未过滤) private List<Object[]> filteredTableRowsData = new ArrayList<>(); // 过滤后的数据行 private boolean isDataLoaded = false; // 数据加载状态标志 //=== 排序状态 ===// private int currentSortColumn = -1; // 当前排序列索引(-1表示未排序) private boolean sortAscending = true; // 排序方向(true为升序) //=== Fragment生命周期方法 ===// /** * 创建Fragment视图 * * @param inflater 布局填充器 * @param container 父容器 * @param savedInstanceState 保存的状态 * @return 创建的视图 */ @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // 1. 填充布局 rootView = inflater.inflate(R.layout.fragment_order_display, container, false); // 2. 初始化视图组件 initViews(); // 3. 初始化列选择器 initColumnSelector(); // 4. 设置搜索功能 setupSearchFunctionality(); // 5. 数据加载逻辑 if (Data.dingdans.isEmpty()) { // 显示加载指示器 showLoadingIndicator(); // 设置数据加载监听器 setupDataLoadListener(); } else { // 填充表格数据 fillTableData(); isDataLoaded = true; } return rootView; } /** * 销毁视图时清理资源 */ @Override public void onDestroyView() { super.onDestroyView(); // 1. 清除视图引用,防止内存泄漏 horizontalScrollView = null; table = null; rootView = null; } //=== 初始化方法 ===// /** * 初始化视图组件 */ private void initViews() { // 1. 查找视图组件 table = rootView.findViewById(R.id.orderTable); horizontalScrollView = rootView.findViewById(R.id.horizontalScrollContainer); searchView = rootView.findViewById(R.id.search_view); columnSelector = rootView.findViewById(R.id.column_selector); // 2. 添加表格表头 addTableHeader(table); } /** * 初始化列选择器(用于指定搜索列) */ private void initColumnSelector() { // 1. 创建适配器(使用资源数组) ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource( requireContext(), R.array.table_headers, // 表头字符串数组资源 android.R.layout.simple_spinner_item ); adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); // 2. 设置适配器默认选择 columnSelector.setAdapter(adapter); columnSelector.setSelection(0); // 默认选择第一项(全部列) // 3. 设置选择监听器 columnSelector.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) { // 当列选择变化时应用搜索过滤 applySearchFilter(); } @Override public void onNothingSelected(AdapterView<?> parent) { // 无操作 } }); } /** * 设置搜索功能 */ private void setupSearchFunctionality() { // 设置搜索查询监听器 searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() { @Override public boolean onQueryTextSubmit(String query) { // 提交查询时应用过滤 applySearchFilter(); return true; } @Override public boolean onQueryTextChange(String newText) { // 文本变化时实时应用过滤 applySearchFilter(); return true; } }); } //=== 数据加载与处理 ===// /** * 填充表格数据 - 核心数据重组逻辑 * 建立订单->产品->组件->板材的四级关系映射 */ private void fillTableData() { // 1. 创建映射关系提高查询效率 Map<Integer, List<Dingdan_Chanpin>> orderProductMap = new HashMap<>(); Map<Integer, List<Chanpin_Zujian>> productComponentMap = new HashMap<>(); Map<Integer, List<Dingdan_chanpin_zujian>> componentMaterialMap = new HashMap<>(); // 2. 构建映射关系 // 2.1 订单-产品映射 for (Dingdan_Chanpin op : Data.dingdan_chanpins) { if (op != null && op.getDingdan() != null) { orderProductMap.computeIfAbsent( op.getDingdan().getId(), k -> new ArrayList<>() ).add(op); } } // 2.2 产品-组件映射 for (Chanpin_Zujian cz : Data.chanpin_zujians) { if (cz.getChanpin() != null) { productComponentMap.computeIfAbsent( cz.getChanpin().getId(), k -> new ArrayList<>() ).add(cz); } } // 2.3 组件-板材映射 for (Dingdan_chanpin_zujian dm : Data.Dingdan_chanpin_zujians) { if (dm.getZujian() != null) { componentMaterialMap.computeIfAbsent( dm.getZujian().getId(), k -> new ArrayList<>() ).add(dm); } } // 3. 重组数据(四级关系遍历) for (Dingdan order : Data.dingdans) { List<Dingdan_Chanpin> products = orderProductMap.get(order.getId()); if (products != null) { for (Dingdan_Chanpin op : products) { Chanpin product = op.getChanpin(); if (product == null) continue; List<Chanpin_Zujian> components = productComponentMap.get(product.getId()); if (components == null) continue; for (Chanpin_Zujian cz : components) { Zujian component = cz.getZujian(); if (component == null) continue; List<Dingdan_chanpin_zujian> materials = componentMaterialMap.get(component.getId()); if (materials == null) continue; for (Dingdan_chanpin_zujian dm : materials) { // 创建行数据并添加到数据集 Object[] rowData = createRowData(order, product, op, cz, dm); allTableRowsData.add(rowData); filteredTableRowsData.add(rowData); } } } } } // 4. 初始排序(不指定列,保持原始顺序) sortTableData(-1, true); } /** * 创建表格行数据对象 * * @param order 订单对象 * @param product 产品对象 * @param dingdan_chanpin 订单-产品关联对象 * @param component 产品-组件关联对象 * @param material 组件-板材关联对象 * @return 行数据数组 */ private Object[] createRowData(Dingdan order, Chanpin product, Dingdan_Chanpin dingdan_chanpin, Chanpin_Zujian component, Dingdan_chanpin_zujian material) { Bancai board = material.getBancai(); return new Object[]{ order.getNumber(), // 订单号 product.getBianhao(), // 产品编号 dingdan_chanpin.getShuliang(),// 产品数量 component.getZujian().getName(), // 组件名 board != null ? board.TableText() : "N/A", // 板材信息 Math.round(component.getOne_howmany()), // 每组件所需板材数 material.getShuliang(), // 板材订购数量 "操作" // 操作按钮 }; } //=== 表格操作 ===// /** * 排序表格数据 * * @param columnIndex 列索引(-1表示初始状态) * @param ascending 是否升序 */ private void sortTableData(int columnIndex, boolean ascending) { // 1. 更新排序状态 if (columnIndex >= 0) { if (currentSortColumn == columnIndex) { // 同一列点击切换排序方向 sortAscending = !ascending; } else { // 不同列点击设为升序 currentSortColumn = columnIndex; sortAscending = true; } } // 2. 创建自定义比较器 Comparator<Object[]> comparator = (row1, row2) -> { if (currentSortColumn < 0) return 0; // 不排序 Object val1 = row1[currentSortColumn]; Object val2 = row2[currentSortColumn]; // 2.1 处理空值 if (val1 == null && val2 == null) return 0; if (val1 == null) return -1; if (val2 == null) return 1; try { // 2.2 数值列比较(第3、6、7列) if (currentSortColumn == 2 || currentSortColumn == 5 || currentSortColumn == 6) { double num1 = Double.parseDouble(val1.toString()); double num2 = Double.parseDouble(val2.toString()); return sortAscending ? Double.compare(num1, num2) : Double.compare(num2, num1); } // 2.3 字符串列比较 else { String str1 = val1.toString().toLowerCase(); String str2 = val2.toString().toLowerCase(); return sortAscending ? str1.compareTo(str2) : str2.compareTo(str1); } } catch (NumberFormatException e) { // 2.4 数值解析失败时按字符串比较 String str1 = val1.toString().toLowerCase(); String str2 = val2.toString().toLowerCase(); return sortAscending ? str1.compareTo(str2) : str2.compareTo(str1); } }; // 3. 执行排序或直接复制数据 if (columnIndex == -1) { // 初始状态:复制所有数据 filteredTableRowsData.clear(); filteredTableRowsData.addAll(allTableRowsData); } else { // 执行排序 Collections.sort(filteredTableRowsData, comparator); } // 4. 刷新表格显示 refreshTableWithData(filteredTableRowsData); } /** * 刷新表格显示 * * @param dataToShow 要显示的数据集合 */ private void refreshTableWithData(Iterable<? extends Object[]> dataToShow) { // 1. 清除旧行(保留表头) removeAllRowsSafely(); // 2. 添加新行 int addedRows = 0; for (Object[] rowData : dataToShow) { addTableRow(rowData); addedRows++; } // 3. 空数据处理 if (addedRows == 0) { addEmptyTableRow(); } } /** * 添加表格行(动态创建视图) * * @param rowData 行数据数组 */ private void addTableRow(Object[] rowData) { // 1. 创建行容器 TableRow row = new TableRow(requireContext()); TableLayout.LayoutParams rowParams = new TableLayout.LayoutParams( TableLayout.LayoutParams.MATCH_PARENT, TableLayout.LayoutParams.WRAP_CONTENT ); row.setLayoutParams(rowParams); row.setMinimumHeight(dpToPx(36)); // 设置最小高度 // 2. 遍历列数据 for (int i = 0; i < rowData.length; i++) { if (i == rowData.length - 1) { // 2.1 操作列(按钮) Button actionButton = new Button(requireContext()); actionButton.setText("操作"); actionButton.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14); actionButton.setBackgroundResource(R.drawable.btn_selector); // 自定义按钮样式 // 按钮布局参数 TableRow.LayoutParams btnParams = new TableRow.LayoutParams( 0, TableRow.LayoutParams.WRAP_CONTENT, 0.5f // 权重 ); btnParams.setMargins(dpToPx(1), dpToPx(1), dpToPx(1), dpToPx(1)); actionButton.setLayoutParams(btnParams); // 设置点击监听 actionButton.setOnClickListener(v -> handleRowAction(rowData, v)); row.addView(actionButton); } else { // 2.2 数据列(文本) HorizontalScrollTextView textView = new HorizontalScrollTextView(requireContext()); textView.setText(String.valueOf(rowData[i])); textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14); // 设置内边距 int padding = dpToPx(8); textView.setPadding(padding, padding/2, padding, padding); textView.setBackgroundResource(R.drawable.cell_border); // 单元格边框 // 根据内容长度设置权重(长内容占更多空间) float weight = rowData[i].toString().length() > 10 ? 2.0f : 1.0f; TableRow.LayoutParams colParams = new TableRow.LayoutParams( 0, TableRow.LayoutParams.MATCH_PARENT, weight ); textView.setLayoutParams(colParams); row.addView(textView); } } // 3. 将行添加到表格 table.addView(row); } //=== 用户交互处理 ===// /** * 处理行操作按钮点击 * * @param rowData 行数据 * @param anchorButton 锚点按钮(用于定位弹出菜单) */ private void handleRowAction(Object[] rowData, View anchorButton) { // 1. 创建弹出菜单 PopupMenu popupMenu = new PopupMenu(requireContext(), anchorButton); popupMenu.getMenuInflater().inflate(R.menu.row_actions_menu, popupMenu.getMenu()); popupMenu.setGravity(Gravity.BOTTOM); // 菜单显示在下方 // 2. 设置菜单项点击监听 popupMenu.setOnMenuItemClickListener(item -> { int itemId = item.getItemId(); if (itemId == R.id.action_view_details) { // 查看详情 showDetailDialog(rowData[0].toString(), rowData[1].toString()); return true; } else if (itemId == R.id.action_edit) { // 编辑行 editRowData(rowData); return true; } else if (itemId == R.id.action_delete) { // 删除行(带确认) deleteRowWithConfirm(rowData); return true; } return false; }); // 3. 显示菜单 popupMenu.show(); } /** * 删除行数据(带确认对话框) * * @param rowData 要删除的行数据 */ private void deleteRowWithConfirm(Object[] rowData) { new AlertDialog.Builder(requireContext()) .setTitle("确认删除") .setMessage("确定要删除订单 " + rowData[0] + " 吗?") .setPositiveButton("删除", (dialog, which) -> deleteRow(rowData)) .setNegativeButton("取消", null) .show(); } /** * 实际删除逻辑 * * @param rowData 要删除的行数据 */ private void deleteRow(Object[] rowData) { // 1. 从数据集中移除 allTableRowsData.removeIf(row -> Arrays.equals(row, rowData)); filteredTableRowsData.removeIf(row -> Arrays.equals(row, rowData)); // 2. 刷新表格 refreshTableWithData(filteredTableRowsData); // 3. 显示提示 Toast.makeText(requireContext(), "已删除订单", Toast.LENGTH_SHORT).show(); } //=== 辅助方法 ===// /** * 应用搜索过滤 */ private void applySearchFilter() { // 1. 获取搜索查询选择的列 String query = searchView.getQuery().toString().trim().toLowerCase(); int selectedColumn = columnSelector.getSelectedItemPosition(); // 2. 清空过滤数据集 filteredTableRowsData.clear(); if (query.isEmpty()) { // 3. 无查询时显示所有数据 filteredTableRowsData.addAll(allTableRowsData); } else { // 4. 有查询时过滤数据 for (Object[] row : allTableRowsData) { if (selectedColumn == 0) { // 4.1 全局搜索(所有列) for (Object cell : row) { if (cell != null && cell.toString().toLowerCase().contains(query)) { filteredTableRowsData.add(row); break; // 匹配任一列即可 } } } else if (selectedColumn > 0 && selectedColumn <= row.length) { // 4.2 特定列搜索 int colIndex = selectedColumn - 1; // 调整索引(0是"全部") if (row[colIndex] != null && row[colIndex].toString().toLowerCase().contains(query)) { filteredTableRowsData.add(row); } } } } // 5. 刷新表格显示 refreshTableWithData(filteredTableRowsData); } /** * 添加表格表头 * * @param table 表格布局 */ private void addTableHeader(TableLayout table) { // 1. 创建表头行 TableRow headerRow = new TableRow(requireContext()); headerRow.setLayoutParams(new TableLayout.LayoutParams( TableLayout.LayoutParams.MATCH_PARENT, TableLayout.LayoutParams.WRAP_CONTENT )); headerRow.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.purple_500)); // 紫色背景 // 2. 获取表头文本权重 String[] headers = getResources().getStringArray(R.array.table_headers); float[] weights = {1.0f, 1.0f, 1.0f, 1.0f, 2.0f, 1.0f, 1.0f}; // 各列权重 // 3. 创建表头单元格 for (int i = 0; i < headers.length; i++) { HorizontalScrollTextView headerView = new HorizontalScrollTextView(requireContext()); headerView.setText(headers[i]); headerView.setTextColor(Color.WHITE); // 白色文字 headerView.setTypeface(null, Typeface.BOLD); // 粗体 headerView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 16); headerView.setPadding(dpToPx(8), dpToPx(8), dpToPx(8), dpToPx(8)); // 内边距 // 单元格布局参数 TableRow.LayoutParams colParams = new TableRow.LayoutParams( 0, TableRow.LayoutParams.MATCH_PARENT, weights[i] // 使用预定义权重 ); headerView.setLayoutParams(colParams); // 设置点击排序监听 final int columnIndex = i; headerView.setOnClickListener(v -> sortTableData(columnIndex, sortAscending)); headerRow.addView(headerView); } // 4. 将表头行添加到表格 table.addView(headerRow); } /** * 安全移除所有数据行(保留表头) */ private void removeAllRowsSafely() { // 确保至少保留表头行 if (table.getChildCount() > 1) { // 从后向前移除(避免索引变化问题) for (int i = table.getChildCount() - 1; i >= 1; i--) { View child = table.getChildAt(i); if (child instanceof TableRow) { // 清理行内视图资源 cleanupRowViews((TableRow) child); } table.removeViewAt(i); } } } /** * 清理行视图资源 * * @param row 表格行 */ private void cleanupRowViews(TableRow row) { for (int i = 0; i < row.getChildCount(); i++) { View view = row.getChildAt(i); if (view instanceof Button) { // 清除按钮点击监听 ((Button) view).setOnClickListener(null); } } } /** * 添加空数据提示行 */ private void addEmptyTableRow() { TableRow row = new TableRow(requireContext()); TextView emptyView = new TextView(requireContext()); emptyView.setText("暂无数据"); emptyView.setGravity(Gravity.CENTER); emptyView.setLayoutParams(new TableRow.LayoutParams( TableRow.LayoutParams.MATCH_PARENT, TableRow.LayoutParams.WRAP_CONTENT )); row.addView(emptyView); table.addView(row); } /** * 显示详情对话框 * * @param orderNumber 订单号 * @param productId 产品ID */ private void showDetailDialog(String orderNumber, String productId) { new AlertDialog.Builder(requireContext()) .setTitle("订单详情") .setMessage("订单号: " + orderNumber + "\n产品ID: " + productId) .setPositiveButton("确定", null) .show(); } /** * 编辑行数据 * * @param rowData 行数据 */ private void editRowData(Object[] rowData) { // 实际项目中应实现编辑表单 Toast.makeText(requireContext(), "编辑: " + rowData[0], Toast.LENGTH_SHORT).show(); } /** * 显示加载指示器 */ private void showLoadingIndicator() { // 实际项目中应显示进度条 Toast.makeText(requireContext(), "加载中...", Toast.LENGTH_SHORT).show(); } /** * 设置数据加载监听器 */ private void setupDataLoadListener() { if (getActivity() instanceof MainActivity) { ((MainActivity) getActivity()).setOnDataLoadListener(new MainActivity.OnDataLoadListener() { @Override public void onDataLoaded() { // 在主线程更新UI requireActivity().runOnUiThread(() -> { hideLoadingIndicator(); isDataLoaded = true; fillTableData(); }); } @Override public void onDataError() { requireActivity().runOnUiThread(() -> { hideLoadingIndicator(); Toast.makeText(getContext(), "数据加载失败", Toast.LENGTH_SHORT).show(); }); } }); } } /** * 隐藏加载指示器 */ private void hideLoadingIndicator() { // 实际项目中应隐藏进度条 } /** * dp转px工具方法 * * @param dp 设备无关像素值 * @return 像素值 */ private int dpToPx(int dp) { return (int) TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, dp, getResources().getDisplayMetrics() ); } }OrderDisplayFragment每次打开数据显示都的刷新
最新发布
06-15
package com.example.kucun2.ui.dingdan;//package com.example.kucun2; import static android.content.ContentValues.TAG; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.app.AlertDialog; import android.content.Context; import android.graphics.Color; import android.graphics.Typeface; import android.os.Bundle; import android.util.Log; import android.util.TypedValue; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.ViewTreeObserver; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.FrameLayout; import android.widget.HorizontalScrollView; import android.widget.LinearLayout; import android.widget.PopupMenu; import android.widget.SearchView; import android.widget.Spinner; import android.widget.TableLayout; import android.widget.TableRow; import android.widget.TextView; import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.content.ContextCompat; import androidx.fragment.app.Fragment; import com.example.kucun2.MainActivity; import com.example.kucun2.R; import com.example.kucun2.View.HorizontalScrollTextView; import com.example.kucun2.entity.Bancai; import com.example.kucun2.entity.Chanpin; import com.example.kucun2.entity.Chanpin_Zujian; import com.example.kucun2.entity.Dingdan; import com.example.kucun2.entity.Dingdan_chanpin_zujian; import com.example.kucun2.entity.Dingdan_Chanpin; import com.example.kucun2.entity.data.Data; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; public class OrderDisplayFragment extends Fragment { private TableLayout table; private HorizontalScrollView horizontalScrollView; private ValueAnimator scrollIndicatorAnimator; private boolean isIndicatorVisible = false; // 添加排序相关的成员变量 private int currentSortColumn = -1; private boolean sortAscending = true; private List<Object[]> allTableRowsData = new ArrayList<>(); // 添加搜索相关成员变量 private SearchView searchView; private Spinner columnSelector; private List<Object[]> filteredTableRowsData = new ArrayList<>(); private boolean isDataLoaded = false; // 添加成员变量保存关键视图引用 private View scrollIndicator; private View rootView; // 保存根视图引用 // 添加滚动监听器的引用 private final ViewTreeObserver.OnScrollChangedListener scrollListener = new ViewTreeObserver.OnScrollChangedListener() { @Override public void onScrollChanged() { // 关键修复:添加全面的空值检查 if (horizontalScrollView == null || !isAdded() || horizontalScrollView.getChildCount() == 0) { return; } View child = horizontalScrollView.getChildAt(0); if (child == null) return; int maxScroll = child.getWidth() - horizontalScrollView.getWidth(); int currentScroll = horizontalScrollView.getScrollX(); if (currentScroll > 0 && maxScroll > 0) { if (!isIndicatorVisible) { showScrollIndicator(); } updateScrollIndicatorPosition(currentScroll, maxScroll); } else { hideScrollIndicator(); } } }; /** * 加载初始化 * * @param inflater The LayoutInflater object that can be used to inflate * any views in the fragment, * @param container If non-null, this is the parent view that the fragment's * UI should be attached to. The fragment should not add the view itself, * but this can be used to generate the LayoutParams of the view. * @param savedInstanceState If non-null, this fragment is being re-constructed * from a previous saved state as given here. * @return */ @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { rootView = inflater.inflate(R.layout.fragment_order_display, container, false); table = rootView.findViewById(R.id.orderTable); horizontalScrollView = rootView.findViewById(R.id.horizontalScrollContainer); scrollIndicator = rootView.findViewById(R.id.scroll_indicator); // 获取搜索控件 searchView = rootView.findViewById(R.id.search_view); columnSelector = rootView.findViewById(R.id.column_selector); table = rootView.findViewById(R.id.orderTable); horizontalScrollView = rootView.findViewById(R.id.horizontalScrollContainer); scrollIndicator = rootView.findViewById(R.id.scroll_indicator); // 初始化表头选择器 initColumnSelector(); // 设置搜索监听 searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() { @Override public boolean onQueryTextSubmit(String query) { applySearchFilter(); return true; } @Override public boolean onQueryTextChange(String newText) { applySearchFilter(); return true; } }); LinearLayout fixedSearchBar = rootView.findViewById(R.id.fixedSearchBar); View placeholder = rootView.findViewById(R.id.search_bar_placeholder); // 添加全局布局监听器以获取正确的搜索框高度 fixedSearchBar.getViewTreeObserver().addOnGlobalLayoutListener( new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { // 获取搜索框的实际高度 int searchBarHeight = fixedSearchBar.getHeight(); // 设置占位视图的高度 ViewGroup.LayoutParams params = placeholder.getLayoutParams(); params.height = searchBarHeight; placeholder.setLayoutParams(params); // 确保仅运行一次 fixedSearchBar.getViewTreeObserver().removeOnGlobalLayoutListener(this); } } ); Log.d(TAG, "onCreateView: " + Data.dingdans.get(0).getNumber()); // 添加表头 addTableHeader(table); // 检查数据是否已加载 if (Data.dingdans.isEmpty()) { // 显示加载指示器 showLoadingIndicator(); // 设置数据加载监听器 if (getActivity() instanceof MainActivity) { ((MainActivity) getActivity()).setOnDataLoadListener(new MainActivity.OnDataLoadListener() { @Override public void onDataLoaded() { requireActivity().runOnUiThread(() -> { hideLoadingIndicator(); isDataLoaded = true; fillTableData(); // 填充数据 }); } @Override public void onDataError() { requireActivity().runOnUiThread(() -> { hideLoadingIndicator(); Toast.makeText(getContext(), "检查网络", Toast.LENGTH_SHORT).show(); //showError("数据加载失败"); }); } }); } } else { // 数据已加载,直接填充 fillTableData(); isDataLoaded = true; } // 填充表格数据 // fillTableData(); // 添加滚动监听 horizontalScrollView.getViewTreeObserver().addOnScrollChangedListener(() -> { // 添加安全检查空值防护 if (horizontalScrollView == null || horizontalScrollView.getChildCount() == 0 || getView() == null) { return; } View child = horizontalScrollView.getChildAt(0); if (child == null) return; int maxScroll = child.getWidth() - horizontalScrollView.getWidth(); int currentScroll = horizontalScrollView.getScrollX(); if (currentScroll > 0 && maxScroll > 0) { if (!isIndicatorVisible) { showScrollIndicator(); } updateScrollIndicatorPosition(currentScroll, maxScroll); } else { hideScrollIndicator(); } }); return rootView; } // 显示/隐藏加载指示器的方法 private void showLoadingIndicator() { // 实现加载动画或进度条 } private void hideLoadingIndicator() { if (scrollIndicator == null || !isAdded()) return; isIndicatorVisible = false; View indicator = this.scrollIndicator; // 使用成员变量 // 隐藏加载指示器 } @Override public void onAttach(@NonNull Context context) { super.onAttach(context); if (context instanceof MainActivity) { ((MainActivity) context).setOnDataLoadListener(new MainActivity.OnDataLoadListener() { @Override public void onDataLoaded() { // 数据加载完成后填充表格 getActivity().runOnUiThread(() -> { Log.d("DataLoad", "Data loaded, filling table"); fillTableData(); }); } @Override public void onDataError() { //showToast("数据加载失败"); } }); } } /** * 获取数据 */ private void fillTableData() { List<Dingdan> orders = Data.dingdans; List<Dingdan_Chanpin> orderProducts = Data.dingdan_chanpins; List<Dingdan_chanpin_zujian> orderMaterials = Data.Dingdan_chanpin_zujians; allTableRowsData.clear(); filteredTableRowsData.clear(); // 创建映射关系提高效率 Map<Integer, List<Dingdan_Chanpin>> orderProductMap = new HashMap<>(); Map<Integer, List<Chanpin_Zujian>> productComponentMap = new HashMap<>(); Map<Integer, List<Dingdan_chanpin_zujian>> componentMaterialMap = new HashMap<>(); // 构建映射 for (Dingdan_Chanpin op : orderProducts) { if (op != null && op.getDingdan() != null) { int orderId = op.getDingdan().getId(); orderProductMap.computeIfAbsent(orderId, k -> new ArrayList<>()).add(op); } } for (Chanpin_Zujian cz : Data.chanpin_zujians) { int productId = cz.getChanpin().getId(); productComponentMap.computeIfAbsent(productId, k -> new ArrayList<>()).add(cz); } for (Dingdan_chanpin_zujian dm : orderMaterials) { int componentId = dm.getZujian().getId(); componentMaterialMap.computeIfAbsent(componentId, k -> new ArrayList<>()).add(dm); } // 重组数据 for (Dingdan order : orders) { List<Dingdan_Chanpin> productsForOrder = orderProductMap.get(order.getId()); if (productsForOrder != null) { for (Dingdan_Chanpin op : productsForOrder) { Chanpin product = op.getChanpin(); List<Chanpin_Zujian> componentsForProduct = productComponentMap.get(product.getId()); if (componentsForProduct != null) { for (Chanpin_Zujian cz : componentsForProduct) { List<Dingdan_chanpin_zujian> materialsForComponent = componentMaterialMap.get(cz.getZujian().getId()); if (materialsForComponent != null) { for (Dingdan_chanpin_zujian dm : materialsForComponent) { Object[] rowData = createRowData(order, product, op, cz, dm); allTableRowsData.add(rowData); filteredTableRowsData.add(rowData); } } } } } } } // 日志记录添加行数 Log.d("TableFill", "Total rows created: " + allTableRowsData.size()); // 初始排序 sortTableData(-1, true); } /** * 排序表格数据并刷新显示 * * @param columnIndex 要排序的列索引 * @param ascending 是否升序排列 */ private void sortTableData(int columnIndex, boolean ascending) { // 更新排序状态 if (columnIndex >= 0) { if (currentSortColumn == columnIndex) { // 相同列点击时切换排序方向 sortAscending = !ascending; } else { currentSortColumn = columnIndex; sortAscending = true; // 新列默认升序 } } // 创建排序比较器 Comparator<Object[]> comparator = (row1, row2) -> { if (currentSortColumn < 0) { return 0; // 返回0表示相等,保持原顺序 } Object value1 = row1[currentSortColumn]; Object value2 = row2[currentSortColumn]; if (value1 == null && value2 == null) return 0; if (value1 == null) return -1; if (value2 == null) return 1; // 根据不同列数据类型定制比较规则 try { // 数值列:2(数量), 5(板材/组件), 6(订购数量) if (currentSortColumn == 2 || currentSortColumn == 5 || currentSortColumn == 6) { double d1 = Double.parseDouble(value1.toString()); double d2 = Double.parseDouble(value2.toString()); return sortAscending ? Double.compare(d1, d2) : Double.compare(d2, d1); } // 其他列按字符串排序 else { String s1 = value1.toString().toLowerCase(); String s2 = value2.toString().toLowerCase(); return sortAscending ? s1.compareTo(s2) : s2.compareTo(s1); } } catch (NumberFormatException e) { // 解析失败时按字符串比较 String s1 = value1.toString().toLowerCase(); String s2 = value2.toString().toLowerCase(); return sortAscending ? s1.compareTo(s2) : s2.compareTo(s1); } }; // 特殊处理初始未排序状态 if (columnIndex == -1) { // 直接复制数据而不排序 filteredTableRowsData.clear(); filteredTableRowsData.addAll(allTableRowsData); } else { Collections.sort(filteredTableRowsData, comparator); } // 刷新显示 refreshTableWithData(filteredTableRowsData); } /** * 表格数据动态添加 * * @param rowData */ private void addTableRow(Object[] rowData) { TableRow row = new TableRow(requireContext()); TableLayout.LayoutParams rowParams = new TableLayout.LayoutParams( TableLayout.LayoutParams.MATCH_PARENT, TableLayout.LayoutParams.WRAP_CONTENT ); row.setLayoutParams(rowParams); row.setMinimumHeight(dpToPx(36)); for (int i = 0; i < rowData.length; i++) { final Object data = rowData[i]; // 判断是否为操作列(最后一列) if (i == rowData.length - 1) { // 创建操作按钮 Button actionButton = new Button(requireContext()); actionButton.setText("操作"); actionButton.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14); actionButton.setBackgroundResource(R.drawable.btn_selector); // 自定义按钮样式 // 设置按钮点击监听器 actionButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { handleRowAction(rowData, v); } }); // 设置按钮布局参数 TableRow.LayoutParams btnParams = new TableRow.LayoutParams( 0, // 宽度由权重控制 TableRow.LayoutParams.WRAP_CONTENT, 0.5f ); btnParams.weight = 0.5f; int margin = dpToPx(1); btnParams.setMargins(margin, margin, margin, margin); actionButton.setLayoutParams(btnParams); actionButton.setHeight(11); row.addView(actionButton); } else { // 正常文本列的代码(保持原逻辑) HorizontalScrollTextView textView = new HorizontalScrollTextView(requireContext()); textView.setText(String.valueOf(data)); textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14); int padding = dpToPx(8); textView.setPadding(padding, padding / 2, padding, padding); textView.setMinWidth(dpToPx(50)); TableRow.LayoutParams colParams = null; // 设置背景边框 textView.setBackgroundResource(R.drawable.cell_border); if (data.toString().length() > 10) { colParams = new TableRow.LayoutParams( 0, // 宽度将由权重控制 TableRow.LayoutParams.MATCH_PARENT, 2.0f ); colParams.weight = 2; } else { colParams = new TableRow.LayoutParams( 0, // 宽度将由权重控制 TableRow.LayoutParams.MATCH_PARENT, 1.0f ); colParams.weight = 1; } textView.setLayoutParams(colParams); row.addView(textView); } } table.addView(row); } // 动态添加表头 (使用自定义TextView) private void addTableHeader(TableLayout table) { TableRow headerRow = new TableRow(requireContext()); headerRow.setLayoutParams(new TableLayout.LayoutParams( TableLayout.LayoutParams.MATCH_PARENT, TableLayout.LayoutParams.WRAP_CONTENT )); // 设置行背景颜色 headerRow.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.purple_500)); // 定义表头 // 更新表头数组(添加操作列) String[] headers = getResources().getStringArray(R.array.table_headers); List<String> headerList = new ArrayList<>(Arrays.asList(headers)); headerList.add("操作"); // 添加操作列标题 headers = headerList.toArray(new String[0]); // 更新权重数组(添加操作列权重) float[] weights = {1.0f, 1.0f, 1.0f, 1.0f, 2.0f, 1.0f, 1.0f, 0.5f}; // 新增操作列权重0.5 // 更新优先级数组(添加操作列优先级) boolean[] priority = {false, false, false, false, true, false, false, false}; for (int i = 0; i < headers.length; i++) { HorizontalScrollTextView headerView = new HorizontalScrollTextView(requireContext()); headerView.setText(headers[i]); headerView.setTextColor(Color.WHITE); headerView.setTypeface(null, Typeface.BOLD); headerView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 16); headerView.setPadding(dpToPx(8), dpToPx(8), dpToPx(8), dpToPx(8)); // 为优先级高的列设置最小宽度 if (priority[i]) { headerView.setMinWidth(dpToPx(220)); } // 设置布局参数 TableRow.LayoutParams colParams = new TableRow.LayoutParams( priority[i] ? TableRow.LayoutParams.WRAP_CONTENT : 0, TableRow.LayoutParams.MATCH_PARENT, priority[i] ? 0 : weights[i] // 优先级列不使用权重 ); headerView.setLayoutParams(colParams); final int columnIndex = i; headerView.setOnClickListener(v -> { // 排序并刷新表格 sortTableData(columnIndex, sortAscending); // 更新排序指示器(可选) showSortIndicator(headerView); }); headerRow.addView(headerView); } table.addView(headerRow); } // 添加排序指示器(可选) private void showSortIndicator(View header) { // 实现:在表头右侧添加↑或↓指示符 // 实现逻辑根据设计需求 // header.setTooltipText(new ); } /** * */ private void showScrollIndicator() { if (scrollIndicator == null || !isAdded()) return; // 使用成员变量而不是findViewById View indicator = this.scrollIndicator; isIndicatorVisible = true; // View indicator = getView().findViewById(R.id.scroll_indicator); if (scrollIndicatorAnimator != null && scrollIndicatorAnimator.isRunning()) { scrollIndicatorAnimator.cancel(); } indicator.setVisibility(View.VISIBLE); indicator.setAlpha(0f); scrollIndicatorAnimator = ObjectAnimator.ofFloat(indicator, "alpha", 0f, 0.8f); scrollIndicatorAnimator.setDuration(300); scrollIndicatorAnimator.start(); } /** * + */ private void hideScrollIndicator() { isIndicatorVisible = false; View indicator = getView().findViewById(R.id.scroll_indicator); if (scrollIndicatorAnimator != null && scrollIndicatorAnimator.isRunning()) { scrollIndicatorAnimator.cancel(); } scrollIndicatorAnimator = ObjectAnimator.ofFloat(indicator, "alpha", indicator.getAlpha(), 0f); scrollIndicatorAnimator.setDuration(300); scrollIndicatorAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { indicator.setVisibility(View.INVISIBLE); } }); scrollIndicatorAnimator.start(); } /** * @param currentScroll * @param maxScroll */ private void updateScrollIndicatorPosition(int currentScroll, int maxScroll) { if (scrollIndicator == null || !isAdded()) return; View indicator = this.scrollIndicator; // 使用成员变量 FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) indicator.getLayoutParams(); // 计算指示器位置(0-100%) float percentage = (float) currentScroll / maxScroll; int maxMargin = getResources().getDisplayMetrics().widthPixels - indicator.getWidth(); // 设置右边距(控制位置) params.rightMargin = (int) (maxMargin * percentage); indicator.setLayoutParams(params); } // 处理行操作的方法 private void handleRowAction(Object[] rowData, View anchorButton) { // 安全地从行数据中提取关键信息 String orderNumber = safeGetString(rowData[0]); // 订单号 String productId = safeGetString(rowData[1]); // 产品ID String componentName = safeGetString(rowData[3]); // 组件名称 // 安全地获取订购数量 double materialQuantity = 0.0; try { if (rowData[6] != null) { if (rowData[6] instanceof Number) { materialQuantity = ((Number) rowData[6]).doubleValue(); } else { materialQuantity = Double.parseDouble(rowData[6].toString()); } } } catch (Exception e) { Log.e("OrderFragment", "Failed to parse material quantity", e); } Context context = getContext(); if (context == null || anchorButton == null) { Log.w("PopupMenu", "Context or anchorButton is null"); return; } PopupMenu popupMenu = new PopupMenu(context, anchorButton); // 强制设置菜单在锚点视图下方显示(关键设置) popupMenu.setGravity(Gravity.BOTTOM); // 如果使用支持库,设置弹出方向 // 设置在锚点视图下方显示 // popupMenu.setOverlapAnchor(true); // 填充菜单项 popupMenu.getMenuInflater().inflate(R.menu.row_actions_menu, popupMenu.getMenu()); // 设置菜单项点击监听器 popupMenu.setOnMenuItemClickListener(item -> { int itemId = item.getItemId(); if (itemId == R.id.action_view_details) { showDetailDialog(orderNumber, productId); return true; } else if (itemId == R.id.action_edit) { editRowData(rowData); return true; } else if (itemId == R.id.action_delete) { deleteRowWithConfirm(rowData); return true; } return false; }); popupMenu.show(); } // 安全获取字符串值的方法 private String safeGetString(Object value) { if (value == null) return ""; if (value instanceof String) return (String) value; return value.toString(); } // 查看详情对话框 private void showDetailDialog(String orderNumber, String productId) { AlertDialog.Builder builder = new AlertDialog.Builder(requireContext()); builder.setTitle("订单详情") .setMessage("订单号: " + orderNumber + "\n产品ID: " + productId) .setPositiveButton("确定", null) .show(); } // 编辑行数据 private void editRowData(Object[] rowData) { // 实现编辑逻辑 // 这里创建包含表单的对话框 Toast.makeText(requireContext(), "编辑操作: " + rowData[0], Toast.LENGTH_SHORT).show(); } // 带确认的删除操作 private void deleteRowWithConfirm(Object[] rowData) { new AlertDialog.Builder(requireContext()) .setTitle("确认删除") .setMessage("确定要删除订单 " + rowData[0] + " 吗?") .setPositiveButton("删除", (dialog, which) -> { // 实际删除逻辑 deleteRow(rowData); }) .setNegativeButton("取消", null) .show(); } // 实际删除行数据 private void deleteRow(Object[] rowData) { // 1. 从allTableRowsData中移除对应行 for (Iterator<Object[]> iterator = allTableRowsData.iterator(); iterator.hasNext(); ) { Object[] row = iterator.next(); if (Arrays.equals(row, rowData)) { iterator.remove(); break; } } // 2. 从filteredTableRowsData中移除 filteredTableRowsData.removeIf(row -> Arrays.equals(row, rowData)); // 3. 刷新表格 refreshTableWithData(filteredTableRowsData); Toast.makeText(requireContext(), "已删除订单", Toast.LENGTH_SHORT).show(); } // DP转PX工具方法 private int dpToPx(int dp) { return (int) TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, dp, getResources().getDisplayMetrics() ); } /** * 数据组合 * * @param order * @param product * @param component * @param material * @return */ private Object[] createRowData(Dingdan order, Chanpin product, Dingdan_Chanpin dingdan_chanpin, Chanpin_Zujian component, Dingdan_chanpin_zujian material) { Bancai board = material.getBancai(); String boardInfo = board.TableText(); ; return new Object[]{ order.getNumber(), // 订单号 product.getBianhao(), // 产品编号 dingdan_chanpin.getShuliang(), // 产品数量 (根据需求调整) component.getZujian().getName(), // 组件名 boardInfo, // 板材信息 Math.round(component.getOne_howmany()), // 板材/组件 material.getShuliang(), // 订购数量 "操作" }; } // 初始化列选择器 private void initColumnSelector() { ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource( requireContext(), R.array.table_headers, android.R.layout.simple_spinner_item ); adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); columnSelector.setAdapter(adapter); // 添加"所有列"选项 columnSelector.setSelection(0); // 默认选择第一个选项(所有列) // 列选择变化监听 columnSelector.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { applySearchFilter(); } @Override public void onNothingSelected(AdapterView<?> parent) { } }); } // 应用搜索过滤 private void applySearchFilter() { String query = searchView.getQuery().toString().trim().toLowerCase(); int selectedColumn = columnSelector.getSelectedItemPosition(); filteredTableRowsData.clear(); if (query.isEmpty()) { // 没有搜索词,显示所有数据 filteredTableRowsData.addAll(allTableRowsData); } else { // 根据选择的列进行过滤 for (Object[] row : allTableRowsData) { // 如果选择"所有列"(位置0),检查所有列 if (selectedColumn == 0) { for (Object cell : row) { if (cell != null && cell.toString().toLowerCase().contains(query)) { filteredTableRowsData.add(row); break; } } } // 检查特定列 else if (selectedColumn >= 1 && selectedColumn <= row.length) { int columnIndex = selectedColumn - 1; // 调整索引(0=所有列,1=第一列) if (row[columnIndex] != null && row[columnIndex].toString().toLowerCase().contains(query)) { filteredTableRowsData.add(row); } } } } // 刷新表格显示 refreshTableWithData(filteredTableRowsData); } /** * 刷新表格显示 */ private void refreshTableWithData(Iterable<? extends Object[]> dataToShow) { // Log.d("TableRefresh", "Refreshing table with " + currentSortColumn + " rows"); // 添加调试信息 Log.d("TableRefresh", "Refreshing table with " + currentSortColumn + " rows"); removeAllRowsSafely(); int addedRows = 0; for (Object[] rowData : dataToShow) { addTableRow(rowData); addedRows++; } // 添加空数据提示 if (addedRows == 0) { addEmptyTableRow(); } } private void addEmptyTableRow() { TableRow row = new TableRow(requireContext()); TextView emptyView = new TextView(requireContext()); emptyView.setText("暂无数据"); emptyView.setGravity(Gravity.CENTER); emptyView.setLayoutParams(new TableRow.LayoutParams( TableRow.LayoutParams.MATCH_PARENT, TableRow.LayoutParams.WRAP_CONTENT )); row.addView(emptyView); table.addView(row); } private void removeAllRowsSafely() { // 移除除表头外的所有行(如果有表头) if (table.getChildCount() > 0) { // 保留表头(索引0) for (int i = table.getChildCount() - 1; i >= 1; i--) { View child = table.getChildAt(i); table.removeView(child); // 清理视图引用(非常重要!) cleanupRowViews((TableRow) child); } } } private void cleanupRowViews(TableRow row) { int childCount = row.getChildCount(); for (int i = 0; i < childCount; i++) { View view = row.getChildAt(i); // 解除视图的所有监听器 view.setOnClickListener(null); // 特别是操作按钮,需要取消所有监听器 if (view instanceof Button) { Button button = (Button) view; button.setOnClickListener(null); // 清空按钮的数据引用 button.setTag(null); } } // 从父视图中移除行 if (row.getParent() != null) { ((ViewGroup) row.getParent()).removeView(row); } } @Override public void onDestroyView() { super.onDestroyView(); // 清理视图引用 // 移除滚动监听器 if (horizontalScrollView != null) { ViewTreeObserver observer = horizontalScrollView.getViewTreeObserver(); if (observer.isAlive()) { observer.removeOnScrollChangedListener(scrollListener); } } scrollIndicator = null; horizontalScrollView = null; table = null; rootView = null; } }简化结构 并加入详细注解
06-14
package com.example.kucun2.ui.jinhuo; import android.app.AlertDialog; import android.app.DatePickerDialog; import android.app.ProgressDialog; import android.os.Bundle; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.Button; import android.widget.EditText; import android.widget.RadioGroup; import android.widget.SearchView; import android.widget.Spinner; import android.widget.Toast; import androidx.annotation.NonNull; import androidx.fragment.app.Fragment; import com.example.kucun2.R; import com.example.kucun2.entity.*; import com.example.kucun2.entity.data.Data; import com.example.kucun2.entity.data.ReflectionJsonUtils; import com.example.kucun2.function.Adapter; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.List; import java.util.Locale; import java.util.stream.Collectors; public class AddInventoryFragment extends Fragment { // UI组件声明 private Spinner spinnerDingdan, spinnerChanpin, spinnerZujian, spinnerBancai; private EditText etShuliang; private RadioGroup rgType; private Button btnNewDingdan, btnAddChanpin, btnAddZujian; private ProgressDialog progressDialog; // 添加进度条 // 当前选中项 private Dingdan selectedDingdan; private Chanpin selectedChanpin; private Zujian selectedZujian; private Bancai selectedBancai; private View rootView; // 日期格式化工具 private final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()); private static final String TAG = "AddInventoryFragment"; @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { rootView = inflater.inflate(R.layout.fragment_add_inventory, container, false); initViews(rootView); setupEventListeners(); // 初始化下拉框数据(使用已加载的数据) setupSpinnersWithExistingData(); return rootView; } /** 初始化UI组件 */ private void initViews(View view) { spinnerDingdan = view.findViewById(R.id.spinner_dingdan); spinnerChanpin = view.findViewById(R.id.spinner_chanpin); spinnerZujian = view.findViewById(R.id.spinner_zujian); spinnerBancai = view.findViewById(R.id.spinner_bancai); etShuliang = view.findViewById(R.id.et_shuliang); rgType = view.findViewById(R.id.rg_type); btnNewDingdan = view.findViewById(R.id.btn_new_dingdan); btnAddChanpin = view.findViewById(R.id.btn_add_chanpin); btnAddZujian = view.findViewById(R.id.btn_add_zujian); } /** * 使用已加载的数据初始化下拉框 */ private void setupSpinnersWithExistingData() { // 1. 设置订单下拉框 setupDingdanSpinner(); // 2. 如果存在选中的订单,设置产品下拉框 if (selectedDingdan != null) { setupChanpinSpinner(selectedDingdan); } // 3. 如果存在选中的产品,设置组件下拉框 if (selectedChanpin != null) { setupZujianSpinner(selectedChanpin); } // 4. 如果存在选中的产品组件,设置板材下拉框 if (selectedChanpin != null && selectedZujian != null) { setupBancaiSpinner(selectedChanpin, selectedZujian); } } // 修改 setupDingdanSpinner 方法,使用现有数据 private void setupDingdanSpinner() { // 过滤掉预置对象(ID=-1) List<Dingdan> validDingdans = Data.dingdans.stream() .filter(d -> d.getId() != null) .collect(Collectors.toList()); Adapter.setupDingdanSpinner(spinnerDingdan, validDingdans, getContext()); // 设置默认选中项(如果有) if (!validDingdans.isEmpty()) { spinnerDingdan.setSelection(0); selectedDingdan = validDingdans.get(0); } } /** 设置事件监听器 */ private void setupEventListeners() { // 新建订单按钮 btnNewDingdan.setOnClickListener(v -> showNewDingdanDialog()); // 添加产品按钮 btnAddChanpin.setOnClickListener(v -> showAddChanpinDialog()); // 添加组件按钮 btnAddZujian.setOnClickListener(v -> { if (selectedChanpin == null) { Toast.makeText(getContext(), "请先选择产品", Toast.LENGTH_SHORT).show(); return; } showCreateZujianDialog(); }); // 提交按钮 rootView.findViewById(R.id.btn_submit).setOnClickListener(v -> addInventoryRecord()); // 订单下拉框监听 spinnerDingdan.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { selectedDingdan = (Dingdan) parent.getItemAtPosition(position); setupChanpinSpinner(selectedDingdan); } @Override public void onNothingSelected(AdapterView<?> parent) {} }); } /** 设置产品下拉框 */ private void setupChanpinSpinner(Dingdan dingdan) { List<Chanpin> chanpins = new ArrayList<>(); if (dingdan != null && dingdan.getDingdanChanpin() != null) { for (Dingdan_Chanpin dc : dingdan.getDingdanChanpin()) { if (dc != null && dc.getChanpin() != null) { chanpins.add(dc.getChanpin()); } } } Adapter.setupChanpinSpinner(spinnerChanpin, chanpins, getContext()); spinnerChanpin.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { selectedChanpin = (Chanpin) parent.getItemAtPosition(position); if (selectedChanpin != null) { setupZujianSpinner(selectedChanpin); } } @Override public void onNothingSelected(AdapterView<?> parent) {} }); } /** 设置组件下拉框 */ private void setupZujianSpinner(Chanpin chanpin) { List<Zujian> zujians = new ArrayList<>(); for (Chanpin_Zujian cz : chanpin.getChanpinZujian()) { zujians.add(cz.getZujian()); } Adapter.setupZujianSpinner(spinnerZujian, zujians, getContext()); spinnerZujian.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { selectedZujian = (Zujian) parent.getItemAtPosition(position); setupBancaiSpinner(selectedChanpin, selectedZujian); } @Override public void onNothingSelected(AdapterView<?> parent) {} }); } /** 设置板材下拉框 */ private void setupBancaiSpinner(Chanpin chanpin, Zujian zujian) { List<Bancai> bancais = new ArrayList<>(); for (Chanpin_Zujian cz : chanpin.getChanpinZujian()) { if (cz.getZujian().getId().equals(zujian.getId())) { bancais.add(cz.getBancai()); } } Adapter.setupBancaiSpinners(spinnerBancai, bancais, getContext()); spinnerBancai.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { selectedBancai = (Bancai) parent.getItemAtPosition(position); Log.d(TAG, "Selected bancai: " + (selectedBancai != null ? selectedBancai.TableText() : "null")); } @Override public void onNothingSelected(AdapterView<?> parent) { selectedBancai = null; } }); // 设置默认选中项 if (!bancais.isEmpty()) { spinnerBancai.setSelection(0); selectedBancai = bancais.get(0); // 确保默认选中第一项 } } /** 添加库存记录 */ private void addInventoryRecord() { // 验证必填项 if (selectedDingdan == null || selectedChanpin == null || selectedZujian == null || selectedBancai == null) { Log.d("addInventoryRecord","selectedDingdan:"+selectedDingdan); Log.d("addInventoryRecord","selectedChanpin:"+selectedChanpin); Log.d("addInventoryRecord","selectedZujian:"+selectedZujian); Log.d("addInventoryRecord","selectedBancai:"+selectedBancai); Toast.makeText(getContext(), "请选择订单、产品、组件板材", Toast.LENGTH_SHORT).show(); return; } String shuliangStr = etShuliang.getText().toString().trim(); if (shuliangStr.isEmpty()) { Toast.makeText(getContext(), "请输入数量", Toast.LENGTH_SHORT).show(); return; } try { int shuliang = Integer.parseInt(shuliangStr); boolean isJinhuo = rgType.getCheckedRadioButtonId() == R.id.rb_jinhuo; User currentUser = new User(1, "当前用户", "user", "password", 1); createRecord(selectedDingdan, selectedChanpin, selectedZujian, selectedBancai, shuliang, isJinhuo, currentUser); Toast.makeText(getContext(), "记录添加成功", Toast.LENGTH_SHORT).show(); etShuliang.setText(""); } catch (NumberFormatException e) { Toast.makeText(getContext(), "请输入有效的数量", Toast.LENGTH_SHORT).show(); } } /** 创建库存记录 */ private void createRecord(Dingdan dingdan, Chanpin chanpin, Zujian zujian, Bancai bancai, int shuliang, boolean isJinhuo, User user) { if (isJinhuo) { Jinhuo jinhuo = new Jinhuo(); jinhuo.setId(Data.jinhuos.size() + 1); jinhuo.setDingdan(dingdan); jinhuo.setChanpin(chanpin); jinhuo.setZujian(zujian); jinhuo.setBancai(bancai); jinhuo.setShuliang(shuliang); jinhuo.setDate(new Date()); jinhuo.setUser(user); Data.jinhuos.add(jinhuo); } else { shuliang = -shuliang; } updateKucun(bancai, shuliang); } /** 更新库存 */ private void updateKucun(Bancai bancai, int changeAmount) { for (Kucun k : Data.kucuns) { if (k.getBancai().getId().equals(bancai.getId())) { k.setShuliang(k.getShuliang() + changeAmount); return; } } // 创建新库存记录 Kucun newKucun = new Kucun(); newKucun.setId(Data.kucuns.size() + 1); newKucun.setBancai(bancai); newKucun.setShuliang(Math.max(changeAmount, 0)); Data.kucuns.add(newKucun); } /** 显示添加产品对话框 */ private void showAddChanpinDialog() { if (selectedDingdan == null) { Toast.makeText(getContext(), "请先选择订单", Toast.LENGTH_SHORT).show(); return; } AlertDialog.Builder builder = new AlertDialog.Builder(requireContext()); builder.setTitle("添加产品到订单"); View dialogView = LayoutInflater.from(requireContext()).inflate(R.layout.dialog_add_chanpin, null); Spinner spinnerChanpinSelection = dialogView.findViewById(R.id.spinner_chanpin_selection); // 设置产品下拉框 if (Data.chanpins.isEmpty()) { Toast.makeText(getContext(), "没有可用的产品,请先创建产品", Toast.LENGTH_SHORT).show(); return; } Adapter.setupChanpinSpinner(spinnerChanpinSelection, Data.chanpins, getContext()); builder.setView(dialogView); builder.setPositiveButton("添加", (dialog, which) -> { Chanpin selected = (Chanpin) spinnerChanpinSelection.getSelectedItem(); if (selected != null) addChanpinToDingdan(selected); }); builder.setNegativeButton("取消", null); builder.show(); } /** 添加产品到订单 */ private void addChanpinToDingdan(Chanpin chanpin) { // 检查是否已添加 for (Dingdan_Chanpin dc : selectedDingdan.getDingdanChanpin()) { if (dc.getChanpin().getId().equals(chanpin.getId())) { Toast.makeText(getContext(), "该产品已添加到订单", Toast.LENGTH_SHORT).show(); return; } } // 创建关联对象 Dingdan_Chanpin dc = new Dingdan_Chanpin(); dc.setChanpin(chanpin); dc.setDingdan(selectedDingdan); selectedDingdan.getDingdanChanpin().add(dc); // 刷新UI setupChanpinSpinner(selectedDingdan); spinnerChanpin.setSelection(selectedDingdan.getDingdanChanpin().size() - 1); Toast.makeText(getContext(), "产品添加成功", Toast.LENGTH_SHORT).show(); } /** 显示新建订单对话框 */ private void showNewDingdanDialog() { AlertDialog.Builder builder = new AlertDialog.Builder(requireContext()); builder.setTitle("新建订单"); View dialogView = LayoutInflater.from(requireContext()).inflate(R.layout.dialog_new_dingdan, null); // 初始化视图 EditText etOrderNumber = dialogView.findViewById(R.id.et_order_number); EditText etOrderDate = dialogView.findViewById(R.id.et_order_date); EditText etdeliveryDate = dialogView.findViewById(R.id.et_delivery_date); Button btnPickOrderDate = dialogView.findViewById(R.id.btn_pick_order_date); Button btnPickdeliveryDate = dialogView.findViewById(R.id.btn_pick_delivery_date); // 设置默认日期 etOrderDate.setText(dateFormat.format(new Date())); etdeliveryDate.setText(dateFormat.format(new Date())); // 日期选择器 btnPickOrderDate.setOnClickListener(v -> showDatePicker(etOrderDate)); btnPickdeliveryDate.setOnClickListener(v -> showDatePicker(etdeliveryDate)); builder.setView(dialogView); builder.setPositiveButton("创建", (dialog, which) -> { String orderNumber = etOrderNumber.getText().toString().trim(); if (orderNumber.isEmpty()) { Toast.makeText(getContext(), "请输入订单号", Toast.LENGTH_SHORT).show(); return; } // 检查订单号是否重复 for (Dingdan d : Data.dingdans) { if (d.getNumber().equals(orderNumber)) { Toast.makeText(getContext(), "订单号已存在", Toast.LENGTH_SHORT).show(); return; } } try { Date orderDate = dateFormat.parse(etOrderDate.getText().toString()); Date deliveryDate = dateFormat.parse(etdeliveryDate.getText().toString()); createAndSaveDingdan(orderNumber, orderDate, deliveryDate); } catch (ParseException e) { Toast.makeText(getContext(), "日期格式错误", Toast.LENGTH_SHORT).show(); } }); builder.setNegativeButton("取消", null); builder.show(); } /** 创建并保存订单 */ private void createAndSaveDingdan(String orderNumber, Date orderDate, Date deliveryDate) { Dingdan newDingdan = new Dingdan(); newDingdan.setId(Data.dingdans.size() + 1); newDingdan.setNumber(orderNumber); newDingdan.setXiadan(orderDate); newDingdan.setJiaohuo(deliveryDate); newDingdan.setDingdanChanpin(new ArrayList<>()); Data.dingdans.add(newDingdan); // 刷新UI setupDingdanSpinner(); spinnerDingdan.setSelection(Data.dingdans.size() - 1); Toast.makeText(getContext(), "新建订单成功", Toast.LENGTH_SHORT).show(); } /** 显示日期选择器 */ private void showDatePicker(EditText targetField) { Calendar cal = Calendar.getInstance(); new DatePickerDialog(requireContext(), (view, year, month, dayOfMonth) -> { Calendar selected = Calendar.getInstance(); selected.set(year, month, dayOfMonth); targetField.setText(dateFormat.format(selected.getTime())); }, cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH) ).show(); } /** 显示创建组件对话框 */ private void showCreateZujianDialog() { AlertDialog.Builder builder = new AlertDialog.Builder(requireContext()); builder.setTitle("添加新组件"); View dialogView = LayoutInflater.from(requireContext()).inflate(R.layout.dialog_create_zujian_bancai, null); // 初始化视图 Spinner etZujianName = dialogView.findViewById(R.id.et_zujian_name); Spinner spinnerbancai = dialogView.findViewById(R.id.spinner_bancai); SearchView searchBancai = dialogView.findViewById(R.id.search_bancai); EditText numberOne_howmany=dialogView.findViewById(R.id.number_one_howmany); // 设置适配器 Adapter.setupZujianSpinner(etZujianName, Data.zujians, getContext()); Adapter.setupBancaiSpinners(spinnerbancai, Data.bancais, getContext()); // 设置搜索功能 searchBancai.setOnQueryTextListener(new SearchView.OnQueryTextListener() { @Override public boolean onQueryTextSubmit(String query) { return false; } @Override public boolean onQueryTextChange(String newText) { List<Bancai> filtered = new ArrayList<>(); for (Bancai b : Data.bancais) { if (b.TableText().toLowerCase().contains(newText.toLowerCase())) { filtered.add(b); } } Adapter.setupBancaiSpinners(spinnerbancai, filtered, getContext()); return true; } }); builder.setView(dialogView); builder.setPositiveButton("创建", (dialog, which) -> { Zujian zujian = (Zujian) etZujianName.getSelectedItem(); Bancai bancai = (Bancai) spinnerbancai.getSelectedItem(); String One_howmany= numberOne_howmany.getText().toString(); if (zujian == null || bancai == null) { Toast.makeText(getContext(), "请选择组件板材", Toast.LENGTH_SHORT).show(); return; } if (One_howmany==null){ Toast.makeText(getContext(), "请选择板材生产多少组件", Toast.LENGTH_SHORT).show(); return; } // 检查是否已存在相同的组合 boolean isDuplicate = false; for (Chanpin_Zujian existing : selectedChanpin.getChanpinZujian()) { if (existing.getZujian().equals(zujian) && existing.getBancai().equals(bancai)) { isDuplicate = true; break; } } if (isDuplicate) { Toast.makeText(getContext(), "该产品组件组合已存在,不能重复添加", Toast.LENGTH_SHORT).show(); return; } // 创建关联对象 Chanpin_Zujian cz = new Chanpin_Zujian(); cz.setZujian(zujian); cz.setBancai(bancai); cz.setChanpin(selectedChanpin); cz.setOne_howmany(Double.valueOf(One_howmany));//double类型 selectedChanpin.getChanpinZujian().add(cz); Dingdan_chanpin_zujian dz=new Dingdan_chanpin_zujian(); dz.setBancai(selectedBancai); dz.setZujian(cz); Data.Dingdan_chanpin_zujians.add(dz); // 刷新UI setupZujianSpinner(selectedChanpin); spinnerZujian.setSelection(selectedChanpin.getChanpinZujian().size() - 1); Toast.makeText(getContext(), "组件添加成功", Toast.LENGTH_SHORT).show(); }); builder.setNegativeButton("取消", null); builder.show(); } }package com.example.kucun2.ui.dingdan; import android.app.AlertDialog; import android.graphics.Color; import android.graphics.Typeface; import android.os.Bundle; import android.util.TypedValue; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.HorizontalScrollView; import android.widget.PopupMenu; import android.widget.SearchView; import android.widget.Spinner; import android.widget.TableLayout; import android.widget.TableRow; import android.widget.TextView; import android.widget.Toast; import androidx.core.content.ContextCompat; import androidx.fragment.app.Fragment; import com.example.kucun2.MainActivity; import com.example.kucun2.R; import com.example.kucun2.View.HorizontalScrollTextView; import com.example.kucun2.entity.Bancai; import com.example.kucun2.entity.Chanpin; import com.example.kucun2.entity.Chanpin_Zujian; import com.example.kucun2.entity.Dingdan; import com.example.kucun2.entity.Dingdan_chanpin_zujian; import com.example.kucun2.entity.Dingdan_Chanpin; import com.example.kucun2.entity.Zujian; import com.example.kucun2.entity.data.Data; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; /** * 订单展示Fragment - 用于展示订单、产品、组件板材的四级关系数据 * * 主要功能: * 1. 从全局数据源加载订单数据 * 2. 建立订单->产品->组件->板材的四级关系映射 * 3. 提供表格展示数据 * 4. 支持按列排序 * 5. 支持多列搜索过滤 * 6. 提供行操作(查看详情、编辑、删除) */ public class OrderDisplayFragment extends Fragment { //=== 视图组件 ===// private TableLayout table; // 表格布局,用于显示数据行 private HorizontalScrollView horizontalScrollView; // 水平滚动容器,容纳表格 private SearchView searchView; // 搜索框,用于数据过滤 private Spinner columnSelector; // 列选择器,指定搜索列 private View rootView; // Fragment的根视图 //=== 数据管理 ===// private List<Object[]> allTableRowsData = new ArrayList<>(); // 所有数据行(未过滤) private List<Object[]> filteredTableRowsData = new ArrayList<>(); // 过滤后的数据行 private boolean isDataLoaded = false; // 数据加载状态标志 //=== 排序状态 ===// private int currentSortColumn = -1; // 当前排序列索引(-1表示未排序) private boolean sortAscending = true; // 排序方向(true为升序) //=== Fragment生命周期方法 ===// /** * 创建Fragment视图 * * @param inflater 布局填充器 * @param container 父容器 * @param savedInstanceState 保存的状态 * @return 创建的视图 */ @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // 1. 填充布局 rootView = inflater.inflate(R.layout.fragment_order_display, container, false); // 2. 初始化视图组件 initViews(); // 3. 初始化列选择器 initColumnSelector(); // 4. 设置搜索功能 setupSearchFunctionality(); // 5. 数据加载逻辑 if (Data.dingdans.isEmpty()) { // 显示加载指示器 showLoadingIndicator(); // 设置数据加载监听器 setupDataLoadListener(); } else { // 填充表格数据 isDataLoaded = true; } return rootView; } /** * 销毁视图时清理资源 */ @Override public void onDestroyView() { super.onDestroyView(); // 1. 清除视图引用,防止内存泄漏 horizontalScrollView = null; table = null; rootView = null; } //=== 初始化方法 ===// /** * 初始化视图组件 */ private void initViews() { // 1. 查找视图组件 table = rootView.findViewById(R.id.orderTable); horizontalScrollView = rootView.findViewById(R.id.horizontalScrollContainer); searchView = rootView.findViewById(R.id.search_view); columnSelector = rootView.findViewById(R.id.column_selector); // 2. 添加表格表头 addTableHeader(table); } /** * 初始化列选择器(用于指定搜索列) */ private void initColumnSelector() { // 1. 创建适配器(使用资源数组) ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource( requireContext(), R.array.table_headers, // 表头字符串数组资源 android.R.layout.simple_spinner_item ); adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); // 2. 设置适配器默认选择 columnSelector.setAdapter(adapter); columnSelector.setSelection(0); // 默认选择第一项(全部列) // 3. 设置选择监听器 columnSelector.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) { // 当列选择变化时应用搜索过滤 applySearchFilter(); } @Override public void onNothingSelected(AdapterView<?> parent) { // 无操作 } }); } /** * 设置搜索功能 */ private void setupSearchFunctionality() { // 设置搜索查询监听器 searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() { @Override public boolean onQueryTextSubmit(String query) { // 提交查询时应用过滤 applySearchFilter(); return true; } @Override public boolean onQueryTextChange(String newText) { // 文本变化时实时应用过滤 applySearchFilter(); return true; } }); } //=== 数据加载与处理 ===// /** * 填充表格数据 - 核心数据重组逻辑 * 建立订单->产品->组件->板材的四级关系映射 */ private void fillTableData() { // 1. 创建映射关系提高查询效率 Map<Integer, List<Dingdan_Chanpin>> orderProductMap = new HashMap<>(); Map<Integer, List<Chanpin_Zujian>> productComponentMap = new HashMap<>(); Map<Integer, List<Dingdan_chanpin_zujian>> componentMaterialMap = new HashMap<>(); // 2. 构建映射关系 // 2.1 订单-产品映射 for (Dingdan_Chanpin op : Data.dingdan_chanpins) { if (op != null && op.getDingdan() != null) { orderProductMap.computeIfAbsent( op.getDingdan().getId(), k -> new ArrayList<>() ).add(op); } } // 2.2 产品-组件映射 for (Chanpin_Zujian cz : Data.chanpin_zujians) { if (cz.getChanpin() != null) { productComponentMap.computeIfAbsent( cz.getChanpin().getId(), k -> new ArrayList<>() ).add(cz); } } // 2.3 组件-板材映射 for (Dingdan_chanpin_zujian dm : Data.Dingdan_chanpin_zujians) { if (dm.getZujian() != null) { componentMaterialMap.computeIfAbsent( dm.getZujian().getId(), k -> new ArrayList<>() ).add(dm); } } // 3. 重组数据(四级关系遍历) for (Dingdan order : Data.dingdans) { List<Dingdan_Chanpin> products = orderProductMap.get(order.getId()); if (products != null) { for (Dingdan_Chanpin op : products) { Chanpin product = op.getChanpin(); if (product == null) continue; List<Chanpin_Zujian> components = productComponentMap.get(product.getId()); if (components == null) continue; for (Chanpin_Zujian cz : components) { Zujian component = cz.getZujian(); if (component == null) continue; List<Dingdan_chanpin_zujian> materials = componentMaterialMap.get(component.getId()); if (materials == null) continue; for (Dingdan_chanpin_zujian dm : materials) { // 创建行数据并添加到数据集 Object[] rowData = createRowData(order, product, op, cz, dm); allTableRowsData.add(rowData); filteredTableRowsData.add(rowData); } } } } } // 4. 初始排序(不指定列,保持原始顺序) sortTableData(-1, true); } /** * 创建表格行数据对象 * * @param order 订单对象 * @param product 产品对象 * @param dingdan_chanpin 订单-产品关联对象 * @param component 产品-组件关联对象 * @param material 组件-板材关联对象 * @return 行数据数组 */ private Object[] createRowData(Dingdan order, Chanpin product, Dingdan_Chanpin dingdan_chanpin, Chanpin_Zujian component, Dingdan_chanpin_zujian material) { Bancai board = material.getBancai(); return new Object[]{ order.getNumber(), // 订单号 product.getBianhao(), // 产品编号 dingdan_chanpin.getShuliang(),// 产品数量 component.getZujian().getName(), // 组件名 board != null ? board.TableText() : "N/A", // 板材信息 Math.round(component.getOne_howmany()), // 每组件所需板材数 material.getShuliang(), // 板材订购数量 "操作" // 操作按钮 }; } //=== 表格操作 ===// /** * 排序表格数据 * * @param columnIndex 列索引(-1表示初始状态) * @param ascending 是否升序 */ private void sortTableData(int columnIndex, boolean ascending) { // 1. 更新排序状态 if (columnIndex >= 0) { if (currentSortColumn == columnIndex) { // 同一列点击切换排序方向 sortAscending = !ascending; } else { // 不同列点击设为升序 currentSortColumn = columnIndex; sortAscending = true; } } // 2. 创建自定义比较器 Comparator<Object[]> comparator = (row1, row2) -> { if (currentSortColumn < 0) return 0; // 不排序 Object val1 = row1[currentSortColumn]; Object val2 = row2[currentSortColumn]; // 2.1 处理空值 if (val1 == null && val2 == null) return 0; if (val1 == null) return -1; if (val2 == null) return 1; try { // 2.2 数值列比较(第3、6、7列) if (currentSortColumn == 2 || currentSortColumn == 5 || currentSortColumn == 6) { double num1 = Double.parseDouble(val1.toString()); double num2 = Double.parseDouble(val2.toString()); return sortAscending ? Double.compare(num1, num2) : Double.compare(num2, num1); } // 2.3 字符串列比较 else { String str1 = val1.toString().toLowerCase(); String str2 = val2.toString().toLowerCase(); return sortAscending ? str1.compareTo(str2) : str2.compareTo(str1); } } catch (NumberFormatException e) { // 2.4 数值解析失败时按字符串比较 String str1 = val1.toString().toLowerCase(); String str2 = val2.toString().toLowerCase(); return sortAscending ? str1.compareTo(str2) : str2.compareTo(str1); } }; // 3. 执行排序或直接复制数据 if (columnIndex == -1) { // 初始状态:复制所有数据 filteredTableRowsData.clear(); filteredTableRowsData.addAll(allTableRowsData); } else { // 执行排序 Collections.sort(filteredTableRowsData, comparator); } // 4. 刷新表格显示 refreshTableWithData(filteredTableRowsData); } /** * 刷新表格显示 * * @param dataToShow 要显示的数据集合 */ private void refreshTableWithData(Iterable<? extends Object[]> dataToShow) { // 1. 清除旧行(保留表头) removeAllRowsSafely(); // 2. 添加新行 int addedRows = 0; for (Object[] rowData : dataToShow) { addTableRow(rowData); addedRows++; } // 3. 空数据处理 if (addedRows == 0) { addEmptyTableRow(); } } /** * 添加表格行(动态创建视图) * * @param rowData 行数据数组 */ private void addTableRow(Object[] rowData) { // 1. 创建行容器 TableRow row = new TableRow(requireContext()); TableLayout.LayoutParams rowParams = new TableLayout.LayoutParams( TableLayout.LayoutParams.MATCH_PARENT, TableLayout.LayoutParams.WRAP_CONTENT ); row.setLayoutParams(rowParams); row.setMinimumHeight(dpToPx(36)); // 设置最小高度 // 2. 遍历列数据 for (int i = 0; i < rowData.length; i++) { if (i == rowData.length - 1) { // 2.1 操作列(按钮) Button actionButton = new Button(requireContext()); actionButton.setText("操作"); actionButton.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14); actionButton.setBackgroundResource(R.drawable.btn_selector); // 自定义按钮样式 // 按钮布局参数 TableRow.LayoutParams btnParams = new TableRow.LayoutParams( 0, TableRow.LayoutParams.WRAP_CONTENT, 0.5f // 权重 ); btnParams.setMargins(dpToPx(1), dpToPx(1), dpToPx(1), dpToPx(1)); actionButton.setLayoutParams(btnParams); // 设置点击监听 actionButton.setOnClickListener(v -> handleRowAction(rowData, v)); row.addView(actionButton); } else { // 2.2 数据列(文本) HorizontalScrollTextView textView = new HorizontalScrollTextView(requireContext()); textView.setText(String.valueOf(rowData[i])); textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14); // 设置内边距 int padding = dpToPx(8); textView.setPadding(padding, padding/2, padding, padding); textView.setBackgroundResource(R.drawable.cell_border); // 单元格边框 // 根据内容长度设置权重(长内容占更多空间) float weight = rowData[i].toString().length() > 10 ? 2.0f : 1.0f; TableRow.LayoutParams colParams = new TableRow.LayoutParams( 0, TableRow.LayoutParams.MATCH_PARENT, weight ); textView.setLayoutParams(colParams); row.addView(textView); } } // 3. 将行添加到表格 table.addView(row); } //=== 用户交互处理 ===// /** * 处理行操作按钮点击 * * @param rowData 行数据 * @param anchorButton 锚点按钮(用于定位弹出菜单) */ private void handleRowAction(Object[] rowData, View anchorButton) { // 1. 创建弹出菜单 PopupMenu popupMenu = new PopupMenu(requireContext(), anchorButton); popupMenu.getMenuInflater().inflate(R.menu.row_actions_menu, popupMenu.getMenu()); popupMenu.setGravity(Gravity.BOTTOM); // 菜单显示在下方 // 2. 设置菜单项点击监听 popupMenu.setOnMenuItemClickListener(item -> { int itemId = item.getItemId(); if (itemId == R.id.action_view_details) { // 查看详情 showDetailDialog(rowData[0].toString(), rowData[1].toString()); return true; } else if (itemId == R.id.action_edit) { // 编辑行 editRowData(rowData); return true; } else if (itemId == R.id.action_delete) { // 删除行(带确认) deleteRowWithConfirm(rowData); return true; } return false; }); // 3. 显示菜单 popupMenu.show(); } /** * 删除行数据(带确认对话框) * * @param rowData 要删除的行数据 */ private void deleteRowWithConfirm(Object[] rowData) { new AlertDialog.Builder(requireContext()) .setTitle("确认删除") .setMessage("确定要删除订单 " + rowData[0] + " 吗?") .setPositiveButton("删除", (dialog, which) -> deleteRow(rowData)) .setNegativeButton("取消", null) .show(); } /** * 实际删除逻辑 * * @param rowData 要删除的行数据 */ private void deleteRow(Object[] rowData) { // 1. 从数据集中移除 allTableRowsData.removeIf(row -> Arrays.equals(row, rowData)); filteredTableRowsData.removeIf(row -> Arrays.equals(row, rowData)); // 2. 刷新表格 refreshTableWithData(filteredTableRowsData); // 3. 显示提示 Toast.makeText(requireContext(), "已删除订单", Toast.LENGTH_SHORT).show(); } //=== 辅助方法 ===// /** * 应用搜索过滤 */ private void applySearchFilter() { // 1. 获取搜索查询选择的列 String query = searchView.getQuery().toString().trim().toLowerCase(); int selectedColumn = columnSelector.getSelectedItemPosition(); // 2. 清空过滤数据集 filteredTableRowsData.clear(); if (query.isEmpty()) { // 3. 无查询时显示所有数据 filteredTableRowsData.addAll(allTableRowsData); } else { // 4. 有查询时过滤数据 for (Object[] row : allTableRowsData) { if (selectedColumn == 0) { // 4.1 全局搜索(所有列) for (Object cell : row) { if (cell != null && cell.toString().toLowerCase().contains(query)) { filteredTableRowsData.add(row); break; // 匹配任一列即可 } } } else if (selectedColumn > 0 && selectedColumn <= row.length) { // 4.2 特定列搜索 int colIndex = selectedColumn - 1; // 调整索引(0是"全部") if (row[colIndex] != null && row[colIndex].toString().toLowerCase().contains(query)) { filteredTableRowsData.add(row); } } } } // 5. 刷新表格显示 refreshTableWithData(filteredTableRowsData); } /** * 添加表格表头 * * @param table 表格布局 */ private void addTableHeader(TableLayout table) { // 1. 创建表头行 TableRow headerRow = new TableRow(requireContext()); headerRow.setLayoutParams(new TableLayout.LayoutParams( TableLayout.LayoutParams.MATCH_PARENT, TableLayout.LayoutParams.WRAP_CONTENT )); headerRow.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.purple_500)); // 紫色背景 // 2. 获取表头文本权重 String[] headers = getResources().getStringArray(R.array.table_headers); float[] weights = {1.0f, 1.0f, 1.0f, 1.0f, 2.0f, 1.0f, 1.0f}; // 各列权重 // 3. 创建表头单元格 for (int i = 0; i < headers.length; i++) { HorizontalScrollTextView headerView = new HorizontalScrollTextView(requireContext()); headerView.setText(headers[i]); headerView.setTextColor(Color.WHITE); // 白色文字 headerView.setTypeface(null, Typeface.BOLD); // 粗体 headerView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 16); headerView.setPadding(dpToPx(8), dpToPx(8), dpToPx(8), dpToPx(8)); // 内边距 // 单元格布局参数 TableRow.LayoutParams colParams = new TableRow.LayoutParams( 0, TableRow.LayoutParams.MATCH_PARENT, weights[i] // 使用预定义权重 ); headerView.setLayoutParams(colParams); // 设置点击排序监听 final int columnIndex = i; headerView.setOnClickListener(v -> sortTableData(columnIndex, sortAscending)); headerRow.addView(headerView); } // 4. 将表头行添加到表格 table.addView(headerRow); } /** * 安全移除所有数据行(保留表头) */ private void removeAllRowsSafely() { // 确保至少保留表头行 if (table.getChildCount() > 1) { // 从后向前移除(避免索引变化问题) for (int i = table.getChildCount() - 1; i >= 1; i--) { View child = table.getChildAt(i); if (child instanceof TableRow) { // 清理行内视图资源 cleanupRowViews((TableRow) child); } table.removeViewAt(i); } } } /** * 清理行视图资源 * * @param row 表格行 */ private void cleanupRowViews(TableRow row) { for (int i = 0; i < row.getChildCount(); i++) { View view = row.getChildAt(i); if (view instanceof Button) { // 清除按钮点击监听 ((Button) view).setOnClickListener(null); } } } /** * 添加空数据提示行 */ private void addEmptyTableRow() { TableRow row = new TableRow(requireContext()); TextView emptyView = new TextView(requireContext()); emptyView.setText("暂无数据"); emptyView.setGravity(Gravity.CENTER); emptyView.setLayoutParams(new TableRow.LayoutParams( TableRow.LayoutParams.MATCH_PARENT, TableRow.LayoutParams.WRAP_CONTENT )); row.addView(emptyView); table.addView(row); } /** * 显示详情对话框 * * @param orderNumber 订单号 * @param productId 产品ID */ private void showDetailDialog(String orderNumber, String productId) { new AlertDialog.Builder(requireContext()) .setTitle("订单详情") .setMessage("订单号: " + orderNumber + "\n产品ID: " + productId) .setPositiveButton("确定", null) .show(); } /** * 编辑行数据 * * @param rowData 行数据 */ private void editRowData(Object[] rowData) { // 实际项目中应实现编辑表单 Toast.makeText(requireContext(), "编辑: " + rowData[0], Toast.LENGTH_SHORT).show(); } /** * 显示加载指示器 */ private void showLoadingIndicator() { // 实际项目中应显示进度条 Toast.makeText(requireContext(), "加载中...", Toast.LENGTH_SHORT).show(); } /** * 设置数据加载监听器 */ private void setupDataLoadListener() { if (getActivity() instanceof MainActivity) { ((MainActivity) getActivity()).setOnDataLoadListener(new MainActivity.OnDataLoadListener() { @Override public void onDataLoaded() { // 在主线程更新UI requireActivity().runOnUiThread(() -> { hideLoadingIndicator(); isDataLoaded = true; fillTableData(); }); } @Override public void onDataError() { requireActivity().runOnUiThread(() -> { hideLoadingIndicator(); Toast.makeText(getContext(), "数据加载失败", Toast.LENGTH_SHORT).show(); }); } }); } } /** * 隐藏加载指示器 */ private void hideLoadingIndicator() { // 实际项目中应隐藏进度条 } /** * dp转px工具方法 * * @param dp 设备无关像素值 * @return 像素值 */ private int dpToPx(int dp) { return (int) TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, dp, getResources().getDisplayMetrics() ); } }package com.example.kucun2.entity.data; import android.content.Context; import android.os.Handler; import android.os.Looper; import android.util.Log; import com.example.kucun2.entity.*; import com.example.kucun2.function.MyAppFnction; import com.example.kucun2.function.SafeLogger; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; import java.io.IOException; import java.lang.reflect.*; import java.util.*; import java.util.stream.Collectors; import okhttp3.*; /** * 核心数据管理类 * 负责: * 1. 声明所有实体类的静态存储列表 * 2. 从服务器加载全量数据 * 3. 管理实体间的关联关系 * 4. 处理数据同步状态 */ public class Data { // ====================== 静态数据列表声明 ====================== public static SynchronizedList<Bancai> bancais = new SynchronizedList<>(Bancai.class); public static SynchronizedList<Caizhi> caizhis = new SynchronizedList<>(Caizhi.class); public static SynchronizedList<Mupi> mupis = new SynchronizedList<>(Mupi.class); public static SynchronizedList<Chanpin> chanpins = new SynchronizedList<>(Chanpin.class); public static SynchronizedList<Chanpin_Zujian> chanpin_zujians = new SynchronizedList<>(Chanpin_Zujian.class); public static SynchronizedList<Dingdan> dingdans = new SynchronizedList<>(Dingdan.class); public static SynchronizedList<Dingdan_Chanpin> dingdan_chanpins = new SynchronizedList<>(Dingdan_Chanpin.class); public static SynchronizedList<Dingdan_chanpin_zujian> Dingdan_chanpin_zujians = new SynchronizedList<>(Dingdan_chanpin_zujian.class); public static SynchronizedList<Kucun> kucuns = new SynchronizedList<>(Kucun.class); public static SynchronizedList<Zujian> zujians = new SynchronizedList<>(Zujian.class); public static SynchronizedList<User> users = new SynchronizedList<>(User.class); public static SynchronizedList<Jinhuo> jinhuos = new SynchronizedList<>(Jinhuo.class); private static User user; // 实体类型与列表的映射表 <实体类, 对应的同步列表> public static final Map<Class, SynchronizedList<SynchronizableEntity>> dataCollectionMap = new HashMap<>(); private static final Gson gson = GsonFactory.createWithIdSerialization(); private static OkHttpClient client; private static final String TAG = "DataLoader"; // 静态初始化:建立实体类型与列表的映射关系 static { try { // 通过反射获取所有SynchronizedList字段 for (Field field : Data.class.getDeclaredFields()) { if (SynchronizedList.class.equals(field.getType())) { SynchronizedList<?> list = (SynchronizedList<?>) field.get(null); if (list != null) { // 将实体类型与列表关联 dataCollectionMap.put(list.getEntityType(), (SynchronizedList<SynchronizableEntity>) list); } } } } catch (IllegalAccessException e) { throw new RuntimeException("初始化dataCollectionMap失败", e); } } // ====================== 核心数据加载方法 ====================== /** * 从服务器加载全量数据 * @param context Android上下文 * @param callback 加载结果回调 */ public static void loadAllData(Context context, LoadDataCallback callback) { // 主线程检查 if (Looper.myLooper() != Looper.getMainLooper()) { throw new IllegalStateException("必须在主线程调用Data.loadAllData"); } ensurePreservedObjects(); // 确保存在预置对象 // 使用传入的 Context 获取主线程 Handler Handler mainHandler = new Handler(context.getMainLooper()); // 确保使用安全的客户端 if (client == null) { client = MyAppFnction.getClient(); } SynchronizableEntity.setSyncEnabled(false); String url = MyAppFnction.getStringResource("string", "url") + MyAppFnction.getStringResource("string", "url_all"); Request request = new Request.Builder().url(url).build(); client.newCall(request).enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { Log.e(TAG, "Failed to load data", e); SynchronizableEntity.setSyncEnabled(true); mainHandler.post(() -> { if (callback != null) callback.onFailure(); }); } @Override public void onResponse(Call call, Response response) throws IOException { if (!response.isSuccessful()) { Log.e(TAG, "Unexpected response code: " + response.code()); SynchronizableEntity.setSyncEnabled(true); mainHandler.post(() -> { if (callback != null) callback.onFailure(); }); return; } String responseData = response.body().string(); SynchronizableEntity.setSyncEnabled(true); ensurePreservedObjects(); // 在主线程处理解析 mainHandler.post(() -> { parseAndAssignData(responseData, context, callback); }); } }); } // ====================== 数据处理私有方法 ====================== /** * 解析JSON数据并更新到实体列表 */ private static void parseAndAssignData(String jsonData, Context context, LoadDataCallback callback) { try { // 解析顶层响应结构 Type responseType = new TypeToken<Information<AllDataResponse>>() {}.getType(); Information<AllDataResponse> info = gson.fromJson(jsonData, responseType); // 验证响应有效性 if (info == null || info.getData() == null || info.getStatus() != 200) { throw new IllegalStateException("无效服务器响应"); } AllDataResponse allData = info.getData(); SafeLogger.d("data", "原始数据: " + gson.toJson(allData)); // 更新所有数据列表 updateAllLists(allData); automaticAssociation(); // 建立实体关联 setAllEntitiesState(SynchronizableEntity.SyncState.MODIFIED); // 设置状态 safeCallback(callback, true); // 成功回调 } catch (Exception e) { Log.e(TAG, "数据处理异常: " + e.getMessage()); safeCallback(callback, false); } finally { SynchronizableEntity.setSyncEnabled(true); // 重新启用同步 } } /** * 批量更新所有实体列表 */ private static void updateAllLists(AllDataResponse allData) { updateList(bancais, allData.bancais); updateList(caizhis, allData.caizhis); updateList(mupis, allData.mupis); updateList(chanpins, allData.chanpins); updateList(chanpin_zujians, allData.chanpin_zujians); updateList(dingdans, allData.dingdans); updateList(dingdan_chanpins, allData.dingdan_chanpins); updateList(Dingdan_chanpin_zujians, allData.Dingdan_chanpin_zujians); updateList(kucuns, allData.kucuns); updateList(zujians, allData.zujians); updateList(users, allData.users); updateList(jinhuos, allData.jinhuos); } /** * 安全更新单个列表(保留预置对象) */ private static <T extends SynchronizableEntity> void updateList( List<T> existingList, List<T> newList ) { if (newList == null) return; // 保留现有列表中的预置对象 List<T> preservedItems = existingList.stream() .filter(item -> item != null && item.isPreservedObject()) .collect(Collectors.toList()); // 清空后重新添加(预置对象 + 有效新数据) existingList.clear(); existingList.addAll(preservedItems); existingList.addAll(newList.stream() .filter(item -> item != null && item.getId() != null && item.getId() != -1) .collect(Collectors.toList()) ); } /** * 确保每个列表都有预置对象(用于表示空值/默认值) */ private static void ensurePreservedObjects() { // 为每个实体类型检查并添加预置对象 addIfMissing(bancais, Bancai.class); addIfMissing(caizhis, Caizhi.class); addIfMissing(mupis, Mupi.class); addIfMissing(chanpins, Chanpin.class); addIfMissing(chanpin_zujians, Chanpin_Zujian.class); addIfMissing(dingdans, Dingdan.class); addIfMissing(kucuns, Kucun.class); addIfMissing(zujians, Zujian.class); addIfMissing(Dingdan_chanpin_zujians, Dingdan_chanpin_zujian.class); addIfMissing(dingdan_chanpins, Dingdan_Chanpin.class); addIfMissing(jinhuos, Jinhuo.class); addIfMissing(users, User.class); } private static <T extends SynchronizableEntity> void addIfMissing( List<T> list, Class<T> clazz ) { if (!containsPreservedObject(list)) { list.add(createInstance(clazz)); } } /** * 检查列表是否包含预置对象 * * @param list 目标实体列表 * @return 是否包含预置对象 */ private static <T extends SynchronizableEntity> boolean containsPreservedObject(List<T> list) { return list.stream().anyMatch(item -> item != null && item.isPreservedObject() ); } /** * 自动建立实体间关联关系(通过反射实现) */ private static void automaticAssociation() { for (Class<?> entityClass : dataCollectionMap.keySet()) { try { associateEntities(dataCollectionMap.get(entityClass)); } catch (Exception e) { Log.e(TAG, entityClass.getSimpleName() + " 关联失败", e); } } } private static <T extends SynchronizableEntity> void associateEntities( SynchronizedList<T> list ) throws IllegalAccessException, ClassNotFoundException { for (T entity : list) { if (entity == null) continue; for (Field field : entity.getClass().getDeclaredFields()) { field.setAccessible(true); Class<?> fieldType = field.getType(); // 处理实体引用字段 if (SynchronizableEntity.class.isAssignableFrom(fieldType)) { associateSingleReference(entity, field); } // 处理实体列表字段 else if (List.class.isAssignableFrom(fieldType)) { associateReferenceList(entity, field); } // 处理基础类型字段 else { setDefaultPrimitiveValue(entity, field); } } } } // ====================== 关联辅助方法 ====================== private static void associateSingleReference( SynchronizableEntity entity, Field field ) throws IllegalAccessException { SynchronizableEntity ref = (SynchronizableEntity) field.get(entity); Class<?> targetType = field.getType(); // 查找目标实体 SynchronizableEntity target = findTargetEntity(ref, targetType); field.set(entity, target); } private static void associateReferenceList( SynchronizableEntity entity, Field field ) throws IllegalAccessException, ClassNotFoundException { // 获取列表泛型类型 Type genericType = field.getGenericType(); if (!(genericType instanceof ParameterizedType)) return; Class<?> itemType = Class.forName( ((ParameterizedType) genericType).getActualTypeArguments()[0].getTypeName() ); // 只处理实体列表 if (!SynchronizableEntity.class.isAssignableFrom(itemType)) return; List<SynchronizableEntity> refList = (List<SynchronizableEntity>) field.get(entity); if (refList == null) { refList = new ArrayList<>(); field.set(entity, refList); } // 清理空值并重建引用 refList.removeAll(Collections.singleton(null)); for (int i = 0; i < refList.size(); i++) { refList.set(i, findTargetEntity(refList.get(i), itemType)); } } /** * 查找关联实体(优先匹配ID,找不到则使用预置对象) */ private static SynchronizableEntity findTargetEntity( SynchronizableEntity ref, Class<?> targetType ) { SynchronizedList<SynchronizableEntity> targetList = dataCollectionMap.get(targetType); if (targetList == null) return null; // 无效引用时返回预置对象 if (ref == null || ref.getId() == null || ref.getId() < 0) { return targetList.stream() .filter(SynchronizableEntity::isPreservedObject) .findFirst().orElse(null); } // 按ID查找目标实体 return targetList.stream() .filter(e -> ref.getId().equals(e.getId())) .findFirst() .orElseGet(() -> targetList.stream() // 找不到时回退到预置对象 .filter(SynchronizableEntity::isPreservedObject) .findFirst().orElse(null) ); } // ====================== 工具方法 ====================== /** * 创建带默认值的实体实例(用作预置对象) */ public static <T> T createInstance(Class<T> clazz) { try { T instance = clazz.getDeclaredConstructor().newInstance(); // 设置基础字段默认值 for (Field field : clazz.getDeclaredFields()) { field.setAccessible(true); setDefaultFieldValue(instance, field); } // 设置特殊字段 clazz.getMethod("setId", Integer.class).invoke(instance, -1); clazz.getMethod("setState", SynchronizableEntity.SyncState.class) .invoke(instance, SynchronizableEntity.SyncState.PRESERVED); return instance; } catch (Exception e) { Log.e("Data", "创建实例失败: " + clazz.getName(), e); try { return clazz.newInstance(); // 回退创建 } catch (Exception ex) { throw new RuntimeException("无法创建实例", ex); } } } private static <T> void setDefaultFieldValue(T instance, Field field) throws IllegalAccessException { Class<?> type = field.getType(); if (type == String.class) field.set(instance, "无"); else if (type == Integer.class || type == int.class) field.set(instance, -1); else if (type == Double.class || type == double.class) field.set(instance, -1.0); else if (type == Boolean.class || type == boolean.class) field.set(instance, false); else if (type == Date.class) field.set(instance, new Date()); else if (SynchronizableEntity.class.isAssignableFrom(type)) { field.set(instance, getPreservedEntity((Class<?>) type)); } else if (List.class.isAssignableFrom(type)) { field.set(instance, new ArrayList<>()); } } private static SynchronizableEntity getPreservedEntity(Class<?> type) { return dataCollectionMap.get(type).stream() .filter(SynchronizableEntity::isPreservedObject) .findFirst().orElse(null); } private static void setDefaultPrimitiveValue( SynchronizableEntity entity, Field field ) throws IllegalAccessException { if (field.get(entity) != null) return; Class<?> type = field.getType(); if (type == String.class) field.set(entity, "无"); else if (type == Integer.class || type == int.class) field.set(entity, -1); else if (type == Double.class || type == double.class) field.set(entity, -1.0); else if (type == Boolean.class || type == boolean.class) field.set(entity, false); else if (type == Date.class) field.set(entity, new Date()); } /** * 主线程安全回调 */ private static void safeCallback(LoadDataCallback callback, boolean success) { new Handler(Looper.getMainLooper()).post(() -> { if (callback == null) return; if (success) callback.onSuccess(); else callback.onFailure(); }); } /** * 设置所有实体同步状态 */ private static void setAllEntitiesState(SynchronizableEntity.SyncState state) { dataCollectionMap.values().forEach(list -> list.forEach(entity -> { if (entity != null) entity.setState(state); }) ); } // ====================== 内部类/接口 ====================== public interface LoadDataCallback { void onSuccess(); void onFailure(); } /** JSON响应数据结构 */ public static class AllDataResponse { public List<Bancai> bancais; public List<Caizhi> caizhis; public List<Mupi> mupis; public List<Chanpin> chanpins; public List<Chanpin_Zujian> chanpin_zujians; public List<Dingdan> dingdans; public List<Dingdan_Chanpin> dingdan_chanpins; public List<Dingdan_chanpin_zujian> Dingdan_chanpin_zujians; public List<Kucun> kucuns; public List<Zujian> zujians; public List<User> users; public List<Jinhuo> jinhuos; } }在AddInventoryFragment中新建数据,在OrderDisplayFragment中不显示
06-15
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值