Process: com.example.kucun2, PID: 14726
java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.
at android.view.ViewGroup.addViewInner(ViewGroup.java:5362)
at android.view.ViewGroup.addView(ViewGroup.java:5176)
at android.widget.TableLayout.addView(TableLayout.java:427)
at android.view.ViewGroup.addView(ViewGroup.java:5116)
at android.widget.TableLayout.addView(TableLayout.java:409)
at android.view.ViewGroup.addView(ViewGroup.java:5088)
at android.widget.TableLayout.addView(TableLayout.java:400)
at com.example.kucun2.ui.dingdan.OrderDisplayFragment.addTableRow(OrderDisplayFragment.java:344)
at com.example.kucun2.ui.dingdan.OrderDisplayFragment.refreshTableWithData(OrderDisplayFragment.java:665)
at com.example.kucun2.ui.dingdan.OrderDisplayFragment.sortTableData(OrderDisplayFragment.java:259)
at com.example.kucun2.ui.dingdan.OrderDisplayFragment.fillTableData(OrderDisplayFragment.java:198)
at com.example.kucun2.ui.dingdan.OrderDisplayFragment.onCreateView(OrderDisplayFragment.java:138)
at androidx.fragment.app.Fragment.performCreateView(Fragment.java:3114)
at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:557)
at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:272)
at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:1943)
at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:1845)
at androidx.fragment.app.FragmentManager.execPendingActions(FragmentManagepackage com.example.kucun2.ui.dingdan;//package com.example.kucun2;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.app.AlertDialog;
import android.graphics.Color;
import android.graphics.Typeface;
import android.os.Bundle;
import android.util.TypedValue;
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.Toast;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
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_Bancai;
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.Iterator;
import java.util.List;
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<>();
/**
*加载初始化
* @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) {
View view = inflater.inflate(R.layout.fragment_order_display, container, false);
table = view.findViewById(R.id.orderTable);
horizontalScrollView = view.findViewById(R.id.horizontalScrollContainer);
View scrollIndicator = view.findViewById(R.id.scroll_indicator);
// 获取搜索控件
searchView = view.findViewById(R.id.search_view);
columnSelector = view.findViewById(R.id.column_selector);
// 初始化表头选择器
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 = view.findViewById(R.id.fixedSearchBar);
View placeholder = view.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);
}
}
);
// 添加表头
addTableHeader(table);
// 填充表格数据
fillTableData();
// 添加滚动监听
horizontalScrollView.getViewTreeObserver().addOnScrollChangedListener(() -> {
int maxScroll = horizontalScrollView.getChildAt(0).getWidth() -
horizontalScrollView.getWidth();
int currentScroll = horizontalScrollView.getScrollX();
if (currentScroll > 0 && maxScroll > 0) {
if (!isIndicatorVisible) {
showScrollIndicator();
}
// 更新滚动指示器位置
updateScrollIndicatorPosition(currentScroll, maxScroll);
} else {
hideScrollIndicator();
}
});
return view;
}
/**
* 获取数据
*/
private void fillTableData() {
List<Dingdan> orders = Data.dingdans;
List<Dingdan_Chanpin> orderProducts = Data.dingdanChanpins;
List<Dingdan_Bancai> orderMaterials = Data.dingdanBancais;
for (Dingdan order : orders) {
for (Dingdan_Chanpin orderProduct : orderProducts) {
if (orderProduct.getDingdan().getId().equals(order.getId())) {
Chanpin product = orderProduct.getChanpin();
for (Chanpin_Zujian component : product.getZujians()) {
for (Dingdan_Bancai material : orderMaterials) {
// 创建行数据但不立即添加到表格
Object[] rowData = createRowData(
order, product, component, material
);
allTableRowsData.add(rowData);
filteredTableRowsData.add(rowData);
// if (material.getZujian() != null &&
// material.getZujian().getId().equals(component.getId())) {
//
// addTableRow(createRowData(
// order, product, component, material
// ));
// }
}
}
}
}
}
// 初始排序
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);
}
};
// 刷新表格显示
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);
}
});
// 设置按钮布局参数
TableRow.LayoutParams btnParams = new TableRow.LayoutParams(
0, // 宽度由权重控制
TableRow.LayoutParams.WRAP_CONTENT,
0.5f
);
btnParams.weight = 0.5f;
int margin = dpToPx(4);
btnParams.setMargins(margin, margin, margin, margin);
actionButton.setLayoutParams(btnParams);
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 / 2);
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(200));
}
// 设置布局参数
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() {
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) {
View indicator = getView().findViewById(R.id.scroll_indicator);
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) {
// 从行数据中提取关键信息
String orderNumber = (String) rowData[0]; // 订单号
String productId = (String) rowData[1]; // 产品ID
String componentName = (String) rowData[3]; // 组件名称
double materialQuantity = (double) rowData[6]; // 订购数量
// 创建操作菜单
PopupMenu popupMenu = new PopupMenu(requireContext(), requireView());
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 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,
Chanpin_Zujian component, Dingdan_Bancai material) {
Bancai board = material.getBancai();
String boardInfo = board.TableText(); ;
return new Object[] {
order.getNumber(), // 订单号
product.getId(), // 产品编号
"1", // 产品数量 (根据需求调整)
component.getZujian().getName(), // 组件名
boardInfo, // 板材信息
Math.round(component.getOne_several()), // 板材/组件
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) {
// 移除除表头外的所有行
removeAllRowsSafely();
// 添加过滤后的行
for (Object[] rowData : dataToShow) {
addTableRow(rowData);
}
}
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);
}
}
}