如何实现仅允许在绿色区域放置拖拽的Codename One容器?
实现拖拽释放区域区分(绿色区域允许放置,蓝色区域拒绝)
核心思路是统一在滚动父容器上处理放置逻辑,通过递归判断释放位置所属区域,决定是否执行容器排序:
- 移除每个子容器的独立放置监听,改为在父容器
centercnt上统一处理 - 拖拽释放时校验目标区域,若为蓝色容器则取消放置
- 修正坐标转换逻辑,确保拖拽起始位置准确
- 实现容器位置交换逻辑,完成排序操作
修改后的完整代码
Form formDnD = new Form("Drag and Drop", new BorderLayout()); formDnD.getToolbar().setBackCommand("", e -> mainForm.showBack()); Container northcnt = new Container(new BoxLayout(BoxLayout.Y_AXIS)); Container centercnt = new Container(new BoxLayout(BoxLayout.Y_AXIS)); centercnt.setScrollableY(true); centercnt.setDropTarget(true); Container southcnt = new Container(new BoxLayout(BoxLayout.Y_AXIS)); Style a = UIManager.getInstance().getComponentStyle("Icon"); FontImage iconDnD = FontImage.createMaterial(FontImage.MATERIAL_DRAG_HANDLE, a, 6); // 查找最上层的绿色容器(GreenCont) private Container getMasterContainer(Component cmp) { while(cmp != null && !"GreenCont".equals(cmp.getUIID())) { cmp = cmp.getParent(); } return (Container)cmp; } // 判断释放位置是否在蓝色区域(MyButtonBlue)内 private boolean isDropOnBlueArea(Component dropTarget) { Component temp = dropTarget; while(temp != null) { if("MyButtonBlue".equals(temp.getUIID())) { return true; } temp = temp.getParent(); } return false; } for(int i = 0; i <= 5; i++){ Container c_Master = new Container(new BoxLayout(BoxLayout.Y_AXIS)); c_Master.setUIID("GreenCont"); Container c = new Container(new BoxLayout(BoxLayout.Y_AXIS)); c.setUIID("MyButtonBlue"); c_Master.add(c); Button b1 = new Button(); b1.setUIID("Icon"); b1.setIcon(iconDnD); c.add(new Label("Label 1")); c.add(b1); c.add(new Label("Label 2")); c.add(new Label("Label 3")); b1.addLongPressListener(actionEvent -> { c_Master.setDraggable(true); // 将按钮内的坐标转换为Form内容面板的相对坐标 int btnAbsX = b1.getAbsoluteX(); int btnAbsY = b1.getAbsoluteY(); int contentAbsX = formDnD.getContentPane().getAbsoluteX(); int contentAbsY = formDnD.getContentPane().getAbsoluteY(); int formX = actionEvent.getX() + btnAbsX - contentAbsX; int formY = actionEvent.getY() + btnAbsY - contentAbsY; formDnD.pointerPressed(formX, formY); // 触发微小拖拽动作,启动拖拽状态 formDnD.pointerDragged(formX + 1, formY + 1); }); centercnt.add(c_Master); } // 在滚动容器上添加统一放置监听 centercnt.addDropListener(actionEvent -> { Component dragged = actionEvent.getDragComponent(); // 校验拖拽组件是否为绿色容器 if(!(dragged instanceof Container) || !"GreenCont".equals(dragged.getUIID())) { dragged.setDraggable(false); return; } Container draggedMaster = (Container)dragged; // 释放到蓝色区域时取消放置 if(isDropOnBlueArea(actionEvent.getDropTarget())) { draggedMaster.setDraggable(false); return; } // 找到释放目标对应的绿色容器 Container targetMaster = getMasterContainer(actionEvent.getDropTarget()); if(targetMaster != null && draggedMaster != targetMaster) { // 获取容器在父容器中的索引 int draggedIndex = centercnt.getComponentIndex(draggedMaster); int targetIndex = centercnt.getComponentIndex(targetMaster); // 移除并重新插入容器,完成排序 centercnt.removeComponent(draggedMaster); centercnt.addComponent(targetIndex, draggedMaster); // 更新UI确保位置生效 centercnt.revalidate(); } // 重置拖拽状态 draggedMaster.setDraggable(false); }); formDnD.add(BorderLayout.NORTH, northcnt).add(BorderLayout.CENTER, centercnt).add(BorderLayout.SOUTH, southcnt); formDnD.show();
关键修改说明
- 坐标转换:将按钮内的相对坐标转换为Form内容面板的相对坐标,解决组件嵌套导致的拖拽起始位置偏差问题。
- 统一放置逻辑:将放置监听移到父容器
centercnt上,避免重复逻辑,提升代码可维护性。 - 区域校验:通过
isDropOnBlueArea递归检查释放位置所属区域,精准判断是否允许放置。 - 排序实现:通过获取容器索引,移除并重新插入拖拽容器,完成排序后调用
revalidate()更新界面。
内容的提问来源于stack exchange,提问作者James N
相关产品推荐
相关产品推荐

