3

この例の形式に従って、一連のドラッグ可能なタブを実装しました: Java Swing を使用してドラッグ可能なタブを実装する方法は?

すべてが希望どおりに動作しているように見えますが、メイン パネルの外にドラッグすると、デスクトップが有効なドロップ ターゲットになります (結果のドロップは受け入れられ、成功としてマークされます)。

このドロップを傍受して、ルート ペインの外へのドロップに対応する方法はありますか? 検出するのは簡単ですが、外の世界が行う前にドロップを実際にキャプチャする方法は明確ではありません.

DragSourceListener の dragDropEnd が呼び出されるまでに、ドロップは既に実行されており、dragOver/Exit/Whatever でドラッグを終了する良い方法はないようです。

ええ、このようなものがうまくいくといいですね:

@Override
public void dragOver(DragSourceDragEvent dragEvent)
{
    DragEnabledTabTransferData data = getTabTransferData(dragEvent);
    DragSourceContext dragSourceContext = dragEvent.getDragSourceContext();
    if (data == null)
    {
        dragSourceContext.setCursor(DragSource.DefaultMoveNoDrop);
        return;
    }
    if (!data.getTabbedPane().getRootPane().getBounds().contains(dragEvent.getLocation()))
    {
        dragSourceContext.dragDropEnd(new DragSourceDropEvent(dragSourceContext, 999, true));
    }
}

代わりに、ドラッグは引きずり続けます。私はそうしますが、私の問題のためにdragDropEndを取得します。

何か案は?唯一の解決策は、ウィンドウ外のイベントをキャプチャするためのドロップ ターゲットとしてのみ機能する非表示の最大化されたグローバル ペインを使用することであると聞いて、非常に残念です。

これが実際の例です。たとえば、Linux のデスクトップにタブをドラッグすると、転送データを Serializable にキャストしようとしますが、うまくいきません。私が遊んでいたドラッグオーバーには、私が上で指摘したものに直接ジャンプしたい場合は、「これは私たちが何かを傍受できると思う場所です」とコメントされています.

/** "Simple" example of DnD tabbed panes. Sourced from Eugene Yokota:
 * http:stackoverflow.com/questions/60269/how-to-implement-draggable-tab-using-java-swing */
import java.awt.*;
import java.awt.datatransfer.*;
import java.awt.dnd.*;
import javax.swing.*;

public class DnDTabbedPane extends JTabbedPane {
    private static final String NAME = "TabTransferData";
    private final DataFlavor FLAVOR = new DataFlavor(DataFlavor.javaJVMLocalObjectMimeType, NAME);

    public DnDTabbedPane() {
        super();
        final DragSourceListener dsl = new DragSourceListener() {
            public void dragEnter(DragSourceDragEvent e) {
                e.getDragSourceContext().setCursor(DragSource.DefaultMoveDrop);
            }

            public void dragExit(DragSourceEvent e) {
                e.getDragSourceContext().setCursor(DragSource.DefaultMoveNoDrop);
            }

            /**
             * This is where I'd assume we'd be able to intercept stuff 
             * so drops don't happen where we don't want them to.
             */
            public void dragOver(DragSourceDragEvent e) {
                TabTransferData data = getTabTransferData(e);
                if (data == null) {
                    e.getDragSourceContext().setCursor(DragSource.DefaultMoveNoDrop);
                    return;
                }
                //This is where I ended up robokilling the drag via hackery
                e.getDragSourceContext().setCursor(DragSource.DefaultMoveDrop);
            }
            public void dragDropEnd(DragSourceDropEvent e) {}
            public void dropActionChanged(DragSourceDragEvent e) {}
        };

        final DragGestureListener dgl = new DragGestureListener() {
            public void dragGestureRecognized(DragGestureEvent e) {

                Point tabPt = e.getDragOrigin();
                int dragTabIndex = indexAtLocation(tabPt.x, tabPt.y);
                if (dragTabIndex < 0) {
                    return;
                }
                e.startDrag(DragSource.DefaultMoveDrop,new TabTransferable(DnDTabbedPane.this, dragTabIndex), dsl);
            }
        };

        new DropTarget(this, DnDConstants.ACTION_COPY_OR_MOVE, new CDropTargetListener(), true);
        new DragSource().createDefaultDragGestureRecognizer(this, DnDConstants.ACTION_COPY_OR_MOVE, dgl);
    }

    private TabTransferData getTabTransferData(DropTargetDropEvent a_event) {       
        try {
            return (TabTransferData) a_event.getTransferable().getTransferData(FLAVOR);             
        } catch (Exception e) {}

        return null;
    }

    private TabTransferData getTabTransferData(DropTargetDragEvent a_event) {
        try {
            return  (TabTransferData) a_event.getTransferable().getTransferData(FLAVOR);                
        } catch (Exception e) {}

        return null;
    }

    private TabTransferData getTabTransferData(DragSourceDragEvent a_event) {
        try {
            return (TabTransferData) a_event.getDragSourceContext().getTransferable().getTransferData(FLAVOR);              
        } catch (Exception e) {}

        return null;        
    }

    class TabTransferable implements Transferable {
        private TabTransferData m_data = null;
        private DataFlavor[] flavors = {FLAVOR};
        public TabTransferable(DnDTabbedPane a_tabbedPane, int a_tabIndex) {
            m_data = new TabTransferData(DnDTabbedPane.this, a_tabIndex);
        }

        public Object getTransferData(DataFlavor flavor) {
            return m_data;
        }

        public DataFlavor[] getTransferDataFlavors() {
            return flavors;
        }

        public boolean isDataFlavorSupported(DataFlavor flavor) {
            return flavor.getHumanPresentableName().equals(NAME);
        }       
    }

    class TabTransferData {
        DnDTabbedPane m_tabbedPane = null;
        int m_tabIndex = -1;

        public TabTransferData(DnDTabbedPane a_tabbedPane, int a_tabIndex) {
            m_tabbedPane = a_tabbedPane;
            m_tabIndex = a_tabIndex;
        }
    }

    class CDropTargetListener implements DropTargetListener {
        public void dragEnter(DropTargetDragEvent e) {
            if (isDragAcceptable(e)) {
                e.acceptDrag(e.getDropAction());
            } else {
                e.rejectDrag();
            }
        }

        public void drop(DropTargetDropEvent a_event) {
            if (isDropAcceptable(a_event)) {
                convertTab(getTabTransferData(a_event),
                getTargetTabIndex(a_event.getLocation()));
                a_event.dropComplete(true);
            } else {
                a_event.dropComplete(false);
            }
        }

        private boolean isTransferableGood(Transferable t, DataFlavor flavor)
        {
            return t == null || t.isDataFlavorSupported(flavor);
        }

        private boolean isDataGood(TabTransferData data)
        {
            if (DnDTabbedPane.this == data.m_tabbedPane && data.m_tabIndex >= 0) {
                return true;
            }
            return false;
        }

        public boolean isDragAcceptable(DropTargetDragEvent e) {
            Transferable t = e.getTransferable();
            if (!isTransferableGood(t, e.getCurrentDataFlavors()[0])) {
                return false;
            }
            return isDataGood(getTabTransferData(e));
        }

        public boolean isDropAcceptable(DropTargetDropEvent e) {
            Transferable t = e.getTransferable();
            if (!isTransferableGood(t, e.getCurrentDataFlavors()[0])) {
                return false;
            }
            return isDataGood(getTabTransferData(e));
        }

        public void dragExit(DropTargetEvent e) {}
        public void dropActionChanged(DropTargetDragEvent e) {}
        public void dragOver(final DropTargetDragEvent e) {}
    }

    private int getTargetTabIndex(Point a_point) {
        for (int i = 0; i < getTabCount(); i++) {
            Rectangle r = getBoundsAt(i);
            r.setRect(r.x - r.width / 2, r.y, r.width, r.height);
            if (r.contains(a_point)) {
                return i;
            }  
        }
        return -1;
    }

    private void convertTab(TabTransferData a_data, int a_targetIndex) {
        DnDTabbedPane source = a_data.m_tabbedPane;
        int sourceIndex = a_data.m_tabIndex;
        if (sourceIndex < 0) {
            return;
        }  

        Component cmp = source.getComponentAt(sourceIndex);
        String str = source.getTitleAt(sourceIndex);

        if (a_targetIndex < 0 || sourceIndex == a_targetIndex) {
            return;
        } 
        source.remove(sourceIndex);
        if (a_targetIndex == getTabCount()) {
            addTab(str, cmp);
        } else if (sourceIndex > a_targetIndex) {
            insertTab(str, null, cmp, null, a_targetIndex);
        } else {
            insertTab(str, null, cmp, null, a_targetIndex - 1);
        }
    }

    public static void main(String[] args)
    {
        JFrame window = new JFrame();
        DnDTabbedPane tabbedPane = new DnDTabbedPane();
        for(int i=0; i< 5; i++)
        {
            tabbedPane.addTab("I'm tab "+i, new JLabel("I'm tab "+i));
        }
        window.add(tabbedPane);
        window.setSize(400, 200);
        window.setVisible(true);
    }
}

これまでのところ、私ができる最善のことは、親から飛び出すときにこの効果のために何かを呼び出すことです。

    Component rootPane = SwingUtilities.getRoot(component);
    Rectangle bounds = rootPane.getBounds();
    if (!bounds.contains(location))
    {
        Robot robot = null;
        try
        {
            robot = new Robot();
        } catch (AWTException e)
        {
            return;
        }
        robot.keyPress(KeyEvent.VK_ESCAPE);
        robot.keyRelease(KeyEvent.VK_ESCAPE);
    }

これは完全なハックであり、私の問題は解決しません。最終的なドロップ イベントをインターセプトし、それがフレームの外側にあるかどうかを確認し、独自の JFrame でタブを生成したいと考えています。

NetBeans、MyDoggy、または Eclipse フレームワークを使用していた場合、これはすべて魔法のように処理されると思います。ああ。

4

3 に答える 3

1

ドラッグを直接キャンセルする方法はありません。http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4502185を参照してください。

カーソルを変更して、デスクトップへのドロップが許可されていないことをユーザーに示したいと思います。

DragSourceListenerdslには、dragOver メソッドに DragSourceDragEvent があり、これは、ターゲット アクションがデスクトップ上で NONE であることを示します。

これに変更します。

public void dragOver(DragSourceDragEvent e) {

    TabTransferData data = getTabTransferData(e);

    if( data == null || e.getTargetActions() == DnDConstants.ACTION_NONE ) {
        e.getDragSourceContext().setCursor( DragSource.DefaultMoveNoDrop );
        return;
    }

    e.getDragSourceContext().setCursor( DragSource.DefaultMoveDrop);
}

本当にキャンセルしたい場合は、ESC ソリューションなどを使用する必要があります。

    try {
        new Robot().mouseRelease( InputEvent.BUTTON1_MASK ); // if Button1 was the only Button to start a Drag
    } catch( AWTException e1 ) {
    }
于 2013-01-11T08:17:52.393 に答える
0

@oliholz で確認されているように、キーストロークで強制的にキャンセルする必要なしにそれを行う方法はありません。

ただし、ティアオフ タブを作成するという私のニーズに対しては、それ自体がドロップ ターゲット リスナーであるフローティング ペインを作成することが、最もクリーンなソリューションのように感じられることがわかりました。

package com.amish.whatever;

import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetDragEvent;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.dnd.DropTargetEvent;
import java.awt.dnd.DropTargetListener;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JLabel;
import javax.swing.JWindow;
import javax.swing.Timer;

public class TearAwayTab extends JWindow {
    MousePoller mousePoller = new MousePoller();
    public TearAwayTab() {
        this.add(new JLabel("FLONT"));
        this.pack();
        new DropTarget(this, DnDConstants.ACTION_COPY_OR_MOVE, new EasyDropTarget(), true);
        this.setVisible(false);
    }

    private void center(Point location)
    {
        Point center = new Point();
        center.setLocation(location.x-this.getWidth()/2, location.y-this.getHeight()/2);
        TearAwayTab.this.setLocation(center);
    }

    public void attach(Point location)
    {
        center(location);
        mousePoller.start();
        this.setVisible(true);
    }

    public void detach()
    {
        mousePoller.stop();
        this.setVisible(false);
    }

    private int DELAY = 10;
    private class MousePoller extends Timer{
        public MousePoller(){
            super(DELAY, new ActionListener() {
                private Point lastPoint = MouseInfo.getPointerInfo().getLocation();
                @Override
                public void actionPerformed(ActionEvent e) {
                    Point point = MouseInfo.getPointerInfo().getLocation();

                    if (!point.equals(lastPoint)) {
                        center(point);
                    }

                    lastPoint = point;
                }
            });
        }
    }

    private class EasyDropTarget implements DropTargetListener
    {

        @Override
        public void dragEnter(DropTargetDragEvent dtde) {
            dtde.acceptDrag(dtde.getDropAction());
        }

        @Override
        public void dragOver(DropTargetDragEvent dtde) {}

        @Override
        public void dropActionChanged(DropTargetDragEvent dtde) {}

        @Override
        public void dragExit(DropTargetEvent dte) {}

        @Override
        public void drop(DropTargetDropEvent dtde) {
            dtde.dropComplete(true);
            detach();
            System.out.println("DROP Intercepted");
        }
    }
}

MousePoller のビットは、マウス リスナーが確実に位置を更新するには、マウスのスクラブが速すぎることを回避します。モーション リスナーを試してみたところ、フローターの境界を簡単に回避することができました。

最初の例に戻ると、ティア アウェイ タブをタブ付きペインのプライベート メンバーとして含め、ドロップ領域を出入りするときに attach と detach を呼び出します。

        final DragSourceListener dsl = new DragSourceListener() {
            public void dragEnter(DragSourceDragEvent e) {
                e.getDragSourceContext().setCursor(DragSource.DefaultMoveDrop);
                Rectangle bounds = SwingUtilities.getRoot(DnDTabbedPane.this).getBounds();

                if(bounds.contains(e.getLocation())){
                    tearTab.detach(); 
                }
            }

            public void dragExit(DragSourceEvent e) {
                e.getDragSourceContext().setCursor(DragSource.DefaultMoveNoDrop);
                tearTab.attach(e.getLocation());
            }
            ...

これには、ドラッグアウトしてから戻す場合に DnD 操作が維持されるという追加の利点もあります。

入力していただきありがとうございます。他のアイデアやコメントがあれば、私はすべて聞いています。

于 2013-01-14T19:26:59.197 に答える
0

これはタブとは直接関係ありませんが、ドラッグをデスクトップにドラッグできないようにする方法の 1 つは、ドラッグしているものをカスタム ラッパー クラスでラップすることです。次に、TransferHandler を作成するときに、DataFlavor localFlavor = new ActivationDataFlavor(YourWrapperClass.class, DataFlavor.javaJVMLocalObjectMimeType, "description"); を作成します。次に、createTransferable メソッドをオーバーライドして、new DataHandler(passedInComponent, localFlavor.getMimeType()); を指定します。そして、すべてのメソッドをオーバーライドして localFlavor のみを持つ新しい Transferable を返します。最後に、importData メソッドで、必ず localFlavor タイプとしてデータをインポートしてください。これにより、定義したフレーバーが JVM に対してローカルであるため、deaktop へのドラッグが防止されます。

于 2013-01-17T19:00:31.487 に答える