11

現在、このコードを使用して JDialog を作成しています。

package com.kamuara.reposync.window;

import java.awt.Dialog;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.UIManager;

public class SheetDialog {

    private JFrame _windowFrame;

    public static void main(String[] args) {
        System.setProperty("apple.awt.documentModalSheet", "true");
        System.setProperty("apple.awt.brushMetalLook", "true");

        try {
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
            new SheetDialog();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public SheetDialog() {
        _windowFrame = new JFrame();
        _windowFrame.setResizable(false);
        _windowFrame.setBounds(100, 100, 451, 320);
        _windowFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        _windowFrame.getContentPane().setLayout(null);
        _windowFrame.setVisible(true);

        JButton showDialogButton = new JButton("Show Dialog");
        showDialogButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                showSheetDialog(_windowFrame, "Test", "This should be a sheet dialog", "Oke");
            }
        });
        showDialogButton.setBounds(328, 263, 117, 29);
        _windowFrame.getContentPane().add(showDialogButton);
    }

    public void showSheetDialog(JFrame owner, String title, String message, String button) {
        final JDialog messageDialog = new JDialog(owner, title, Dialog.ModalityType.DOCUMENT_MODAL);
        messageDialog.setBounds(30, 0, owner.getWidth() - 60, 130);

        // TODO: only when os is osx
        messageDialog.getRootPane().putClientProperty("apple.awt.documentModalSheet", "true");
        messageDialog.setLayout(null);

        int offsetX = 25;

        JLabel titleLabel = new JLabel(title);
        titleLabel.setFont(new Font("Lucida Grande", Font.BOLD, 13));
        titleLabel.setBounds(offsetX, 10, 100, 25);
        messageDialog.getContentPane().add(titleLabel);

        JLabel messageLabel = new JLabel(message);
        messageLabel.setVerticalTextPosition(JLabel.TOP);
        messageLabel.setHorizontalTextPosition(JLabel.LEFT);
        messageLabel.setFont(new Font("Lucida Grande", Font.PLAIN, 11));
        messageLabel.setBounds(offsetX, 10, messageDialog.getWidth() - 10, messageDialog.getHeight() - 60);
        messageDialog.getContentPane().add(messageLabel);

        JButton okButton = new JButton(button);
        okButton.setBounds(messageDialog.getWidth() - 105, messageDialog.getHeight() - 35, 100, 25);
        okButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent arg0) {
                messageDialog.dispose();
            }
        });
        messageDialog.getContentPane().add(okButton);

        messageDialog.setVisible(true);
    }
}

以前は Java 6 を使用してアプリケーションをコンパイルし、clientProperty を設定すると、apple.awt.documentModalSheetOSX でダイアログを「シート」として表示するのに完全に機能していましたが、Java 7 (更新 25) の使用を開始し、ダイアログがシートとして表示されなくなりました。これに関する更新ドキュメントが見つからないようです。彼らはこれについて何か変更しましたか? どうすればこれを解決できますか? 現在のインターフェイス デザインは、ダイアログよりもシートの方が見栄えがします。

アップデート

私が経験しているのと同じ問題と思われる次のバグ レポートを見つけました。

http://bugs.sun.com/view_bug.do?bug_id=8010197

これを解決する方法を知っている人はいますか?QuaQuaなどのライブラリを調べましたが、シート機能が必要なだけなので、ライブラリを使用しないことをお勧めします。

更新 2

QuaQua を試してみましたが、Java 7 でコンパイルすると、現在ライブラリにまったく同じ問題があります。回避策はありますか?

アップデート 3

コードを実際のサンプルに置き換えました ( http://pastebin.com/PJ8VGdPb )

更新 4

SWT には SWT.SHEET という名前のシェル クラスのスタイルがあり、Java7 でも動作することがわかりました。SWT のようなライブラリを使用することは好みませんが、それが唯一の解決策のようです。

4

3 に答える 3

2

JDK がバグを修正する前に、シートを自分で実装する必要があるようです。

重要なポイントは次のとおりです。

  1. ウィンドウ フレームのガラス ペインを使用してシート ダイアログを保持します。
  2. GridBagLayout (NORTH アンカー付き) を使用して、ダイアログをペインの上部 | 中央に配置します。
  3. ダイアログを繰り返しペイントすることで、表示/非表示時にシートをアニメーション化します。そのたびに、ダイアログのより多く/より少ない部分をペイントします

以下はサンプルコードです

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsEnvironment;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;

import javax.swing.Box;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.border.LineBorder;

public class SheetableJFrame extends JFrame implements ActionListener {

    public static final int INCOMING = 1;
    public static final int OUTGOING = -1;
    public static final float ANIMATION_DURATION = 1000f;
    public static final int ANIMATION_SLEEP = 50;

    JComponent sheet;
    JPanel glass;
    Sheet animatingSheet;
    boolean animating;
    int animationDirection;
    Timer animationTimer;
    long animationStart;
    BufferedImage offscreenImage;

    public SheetableJFrame() {
        super();
        glass = (JPanel) getGlassPane();
        glass.setLayout(new GridBagLayout());
        animatingSheet = new Sheet();
        animatingSheet.setBorder(new LineBorder(Color.black, 1));
    }

    public JComponent showJDialogAsSheet(JDialog dialog) {
        sheet = (JComponent) dialog.getContentPane();
        sheet.setBorder(new LineBorder(Color.black, 1));
        glass.removeAll();
        animationDirection = INCOMING;
        startAnimation();
        return sheet;
    }

    public void hideSheet() {
        animationDirection = OUTGOING;
        startAnimation();
    }

    private void startAnimation() {
        glass.repaint();
        // clear glasspane and set up animatingSheet
        animatingSheet.setSource(sheet);
        glass.removeAll();
        GridBagConstraints gbc = new GridBagConstraints();
        gbc.anchor = GridBagConstraints.NORTH;
        glass.add(animatingSheet, gbc);
        gbc.gridy = 1;
        gbc.weighty = Integer.MAX_VALUE;
        glass.add(Box.createGlue(), gbc);
        glass.setVisible(true);

        // start animation timer
        animationStart = System.currentTimeMillis();
        if (animationTimer == null) animationTimer = new Timer(ANIMATION_SLEEP, this);
        animating = true;
        animationTimer.start();
    }

    private void stopAnimation() {
        animationTimer.stop();
        animating = false;
    }

    // used by the Timer
    public void actionPerformed(ActionEvent e) {
        if (animating) {
            // calculate height to show
            float animationPercent = (System.currentTimeMillis() - animationStart) / ANIMATION_DURATION;
            animationPercent = Math.min(1.0f, animationPercent);
            int animatingHeight = 0;

            if (animationDirection == INCOMING) {
                animatingHeight = (int) (animationPercent * sheet.getHeight());
            } else {
                animatingHeight = (int) ((1.0f - animationPercent) * sheet.getHeight());
            }
            // clip off that much from sheet and put it into animatingSheet
            animatingSheet.setAnimatingHeight(animatingHeight);
            animatingSheet.repaint();

            if (animationPercent >= 1.0f) {
                stopAnimation();
                if (animationDirection == INCOMING) {
                    finishShowingSheet();
                } else {
                    glass.removeAll();
                    glass.setVisible(false);
                    glass.setLayout(new GridBagLayout());
                    animatingSheet = new Sheet();
                }
            }
        }
    }

    private void finishShowingSheet() {
        glass.removeAll();
        GridBagConstraints gbc = new GridBagConstraints();
        gbc.anchor = GridBagConstraints.NORTH;
        glass.add(sheet, gbc);
        gbc.gridy = 1;
        gbc.weighty = Integer.MAX_VALUE;
        glass.add(Box.createGlue(), gbc);
        glass.revalidate();
        glass.repaint();
    }

    class Sheet extends JPanel {
        Dimension animatingSize = new Dimension(0, 1);
        JComponent source;
        BufferedImage offscreenImage;

        public Sheet() {
            super();
            setOpaque(true);
        }

        public void setSource(JComponent source) {
            this.source = source;
            animatingSize.width = source.getWidth();
            makeOffscreenImage(source);
        }

        public void setAnimatingHeight(int height) {
            animatingSize.height = height;
            setSize(animatingSize);
        }

        private void makeOffscreenImage(JComponent source) {
            GraphicsConfiguration gfxConfig = GraphicsEnvironment.getLocalGraphicsEnvironment()
                    .getDefaultScreenDevice().getDefaultConfiguration();
            offscreenImage = gfxConfig.createCompatibleImage(source.getWidth(), source.getHeight());
            Graphics2D offscreenGraphics = (Graphics2D) offscreenImage.getGraphics();
            source.paint(offscreenGraphics);
        }

        public Dimension getPreferredSize() {
            return animatingSize;
        }

        public Dimension getMinimumSize() {
            return animatingSize;
        }

        public Dimension getMaximumSize() {
            return animatingSize;
        }

        public void paint(Graphics g) {
            // get the bottom-most n pixels of source and paint them into g, where n is height
            BufferedImage fragment = offscreenImage.getSubimage(0, offscreenImage.getHeight() - animatingSize.height,
                    source.getWidth(), animatingSize.height);
            g.drawImage(fragment, 0, 0, this);
        }
    }
}

テストコード

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

import javax.swing.JDialog;
import javax.swing.JOptionPane;

public class SheetTest extends Object implements PropertyChangeListener {

    JOptionPane optionPane;
    SheetableJFrame frame;

    public static void main(String[] args) {
        new SheetTest();
    }

    public SheetTest() {
        frame = new SheetableJFrame();
        // build JOptionPane dialog and hold onto it
        optionPane = new JOptionPane("Do you want to close?", JOptionPane.QUESTION_MESSAGE, JOptionPane.CANCEL_OPTION);
        frame.setSize(640, 480);
        frame.setVisible(true);
        optionPane.addPropertyChangeListener(this);

        JDialog dialog = optionPane.createDialog(frame, "irrelevant");
        frame.showJDialogAsSheet(dialog);
    }

    public void propertyChange(PropertyChangeEvent pce) {
        if (pce.getPropertyName().equals(JOptionPane.VALUE_PROPERTY)) {
            System.out.println("Selected option " + pce.getNewValue());
            frame.hideSheet();
        }
    }
}

参照
http://oreilly.com/pub/h/4852
http://book.javanb.com/swing-hacks/swinghacks-chp-6-sect-6.html

于 2013-08-03T18:38:28.397 に答える
2

私の知る限り、Apple は JDK 7 のバージョンを正式にリリースしていません。Apple が OS X 用に最適化した JDK の最新バージョンは、まだ JDK 6 です。これが、Java の更新が AppStore の更新タブから提供される理由でもあります。これらの更新は、Oracle から直接提供されるものではありません。

JDK 7 を Oracle から直接ダウンロードした場合、これはより一般的な、微調整されていないバージョンです。

したがって、Apple が OS X に最適化された JDK 7 をリリースするのを待つ必要があると思います。

Oracle からダウンロードすると、OS X の多くの機能が動作しないことを経験しました。

  • トラックパッドのジェスチャー
  • UIManager を使用して手動で設定しようとしても、Native Aqua Look'n'Feel が機能しません。
  • JOptionPane の使用時にアプリケーション アイコンが機能しない。
  • JMenu は、画面の上部に移動するのではなく、JFrame 自体に固定されます。
于 2013-08-03T00:21:08.583 に答える
0

これは、JDKが設定を忘れているように見えるフラグを設定し、ウィンドウを適切な場所に手動で配置する、私が思いついた非常に危険なハックです。まだ影が欠けているので、誰かがそれを改善できるのだろうか. ;)

これは内部クラスとプライベート フィールドを台無しにするため、JDK の特定の新しいリリースでは機能しなくなる可能性がありますが、8u5 では引き続き動作します。おそらく、これらの内部 AWT クラスがどのように構成されているかについての洞察が得られるでしょう。

public static void makeSheet(Dialog dialog) {
    dialog.addNotify();
    ComponentPeer peer = dialog.getPeer();

    // File dialogs are CFileDialog instead. Unfortunately this means this hack
    // can't work for those. :(
    if (peer instanceof LWWindowPeer) {
        LWWindowPeer windowPeer = (LWWindowPeer) dialog.getPeer();
        //XXX: Should check this before casting too.
        CPlatformWindow platformWindow = (CPlatformWindow) windowPeer.getPlatformWindow();
        try {
            Method method = CPlatformWindow.class.getDeclaredMethod(
                "setStyleBits", int.class, boolean.class);
            method.setAccessible(true);
            method.invoke(platformWindow, 64 /* CPlatformWindow.SHEET */, true);

            Window parent = dialog.getOwner();
            dialog.setLocation(dialog.getLocation().x,
                               parent.getLocation().y + parent.getInsets().top);
        } catch (Exception e) {
            Logger.getLogger(SheetHack.class.getName())
                .log(Level.WARNING, "Couldn't call setStyleBits", e);
        }
    }
}
于 2014-05-26T11:19:07.807 に答える