You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现仅允许在绿色区域放置拖拽的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();

关键修改说明

  1. 坐标转换:将按钮内的相对坐标转换为Form内容面板的相对坐标,解决组件嵌套导致的拖拽起始位置偏差问题。
  2. 统一放置逻辑:将放置监听移到父容器centercnt上,避免重复逻辑,提升代码可维护性。
  3. 区域校验:通过isDropOnBlueArea递归检查释放位置所属区域,精准判断是否允许放置。
  4. 排序实现:通过获取容器索引,移除并重新插入拖拽容器,完成排序后调用revalidate()更新界面。

内容的提问来源于stack exchange,提问作者James N

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 16:07:08