Java Swing - 创建类似标签的动态宽度按钮

时间:2013-11-07 11:55:55

标签: java swing user-interface

我正在使用桌面GUI设计迈出第一步。我想要实现的是在一个表列中显示。我希望它们具有视觉吸引力,也可以点击。

首先,我认为可以使用JButton做一些黑客攻击,如建议in this thread。但后来我意识到我不知道如何管理标签的动态宽度和拉伸图形以100%填充按钮。

我想要实现类似于下图的内容。在HTML + CSS中,每个标记都非常简单:

  • 将左边缘设为图形
  • 具有可重复的1px宽背景
  • 右边缘为图形

但是如何在Java中做到这一点?

info:我不在乎这是不是JButton,只要它很好,可点击和可拖动。

tag graphics

更新

下面的样机代表了我想要做的事情。它涉及JTable或多个JLists以及在该字旁边浮动的那些标记。在HTML + CSS中很容易做到,但我几乎不可能用Java做到这一点。

mockup

4 个答案:

答案 0 :(得分:5)

我毫不怀疑有很多方法可以实现这一点,更好的方法是简单地编写一个UI委托,但让我们暂时保持基本...

enter image description here

(我已更新按钮代码,因此我已将其包含在更新中)

你遇到的问题是JTable并不是为了做你想做的事。分组行和可变行高不容易实现。

这只是一个例子,但我所做的是生成一系列“模仿”你的基本要求的复合组件......

enter image description here

以下内容使用自己的camrik和`WrapLayout'

编写的SwingLabs, SwingX libraries
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.LinearGradientPaint;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Path2D;
import java.util.ArrayList;
import java.util.List;
import javax.swing.AbstractButton;
import javax.swing.DefaultButtonModel;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.Border;
import javax.swing.border.MatteBorder;
import org.jdesktop.swingx.*;

public class TestTagButton {

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

    public TestTagButton() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }

                JPanel wordsPane = new JPanel(new VerticalLayout());
                wordsPane.setBackground(Color.WHITE);

                Word word = new Word("Hand");
                word.addTag("body parts", 55);
                List<String> translations = new ArrayList<>(3);
                translations.add("Reka");
                translations.add("Dion");
                translations.add("Garsec");

                wordsPane.add(new WordGroupPane(word, translations));

                word = new Word("Roof");
                word.addTag("house", 17);
                word.addTag("architecture", 8);
                translations = new ArrayList<>(3);
                translations.add("Dach");
                translations.add("Zadaszenie");

                wordsPane.add(new WordGroupPane(word, translations));

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
//                frame.add(new JScrollPane(wordsPane));
                frame.add(wordsPane);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public static class WordGroupPane extends JPanel {

        public WordGroupPane(Word word, List<String> translations) {
            setOpaque(false);
            setLayout(new GridLayout(0, 2));

            add(new WordPane(word));
            add(new TranslationsPane(translations));
        }

    }

    public static class TranslationsPane extends JPanel {

        protected static final Border SPLIT_BORDER = new MatteBorder(0, 0, 1, 0, Color.GRAY);

        public TranslationsPane(List<String> translations) {
            setOpaque(false);
            setLayout(new GridLayout(0, 1));
            for (String translation : translations) {
                JLabel lbl = new JLabel(translation);
                lbl.setHorizontalAlignment(JLabel.LEFT);
                lbl.setBorder(SPLIT_BORDER);
                add(lbl);
            }
        }

    }

    public static class WordPane extends JPanel {

        public WordPane(Word word) {
            setBorder(new MatteBorder(0, 0, 1, 1, Color.GRAY));
            setOpaque(false);
            setLayout(new GridBagLayout());
            GridBagConstraints gbc = new GridBagConstraints();
            gbc.gridx = 0;
            gbc.gridy = 0;
            gbc.anchor = GridBagConstraints.NORTH;
            gbc.insets = new Insets(10, 8, 10, 8);
            JLabel label = new JLabel(word.getWord());
            add(label, gbc);

            gbc.gridx = 1;
            gbc.weightx = 1;
            gbc.fill = GridBagConstraints.HORIZONTAL;
            gbc.insets = new Insets(0, 0, 0, 0);
            JPanel tagsPane = new JPanel(new WrapLayout(WrapLayout.LEFT));
            tagsPane.setOpaque(false);
            for (Tag tag : word.getTags()) {
                TagButton tb = new TagButton(tag.getText());
                Font font = tb.getFont();
                font = font.deriveFont(font.getSize() - 3f);
                tb.setFont(font);
                tb.setTag(Integer.toString(tag.getCount()));
                tb.setSelected(true);
                tagsPane.add(tb);
            }
            add(tagsPane, gbc);
        }

    }

    public class Word {

        private String word;
        private List<Tag> tags;

        public Word(String word) {
            this.word = word;
            this.tags = new ArrayList<>(25);
        }

        public void addTag(String text, int count) {
            addTag(new Tag(text, count));
        }

        public String getWord() {
            return word;
        }

        public List<Tag> getTags() {
            return tags;
        }

        public void addTag(Tag tag) {
            tags.add(tag);
        }

    }

    public class Tag {

        private String text;
        private int count;

        public Tag(String text, int count) {
            this.text = text;
            this.count = count;
        }

        public String getText() {
            return text;
        }

        public int getCount() {
            return count;
        }

    }

    public static class TagButton extends AbstractButton {

        private JLabel renderer;
        private String tag;

        public TagButton(String text) {
            this();
            setText(text);
        }

        public TagButton() {
            setModel(new DefaultButtonModel());
            setMargin(new Insets(8, 8, 8, 8));
            addMouseListener(new MouseAdapter() {

                @Override
                public void mousePressed(MouseEvent e) {
                    getModel().setPressed(true);
                }

                @Override
                public void mouseReleased(MouseEvent e) {
                    getModel().setPressed(false);
                    getModel().setSelected(!isSelected());
                }

            });
            setFont(UIManager.getFont("Button.font"));
        }

        public void setTag(String value) {
            if (tag == null ? value != null : !tag.equals(value)) {

                String old = tag;
                tag = value;
                firePropertyChange("tag", old, tag);
                revalidate();

            }
        }

        public String getTag() {
            return tag;
        }

        protected JLabel getRenderer() {

            if (renderer == null) {

                renderer = new JLabel(getText());

            }

            return renderer;

        }

        @Override
        public Dimension getPreferredSize() {

            Insets margin = getMargin();
            Dimension size = new Dimension();
            size.width = margin.left + margin.right;
            size.height = margin.top + margin.bottom;

            JLabel renderer = getRenderer();
            renderer.setText(getText());
            size.width += renderer.getPreferredSize().width;
            size.height += renderer.getPreferredSize().height;

            size.width += getTagWidth();

            return size;
        }

        protected int getTagWidth() {

            JLabel renderer = getRenderer();
            renderer.setText(getTag());
            renderer.setFont(getFont());

            return renderer.getPreferredSize().width + 16;

        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();
            g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
            g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
            g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
            g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
            g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
            g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);

//            int fullWidth = getTagWidth() + 8;
            int width = getTagWidth() + 8;
            int height = getHeight() - 3;
            int tagWidth = getWidth() - 1 - width;

            Shape insert = new TagInsert(width, height);
            int x = getWidth() - width - 1;
            if (!isSelected()) {
                x -= getTagWidth();
            }

            x -= 4;

            g2d.translate(x, 1);
            g2d.setPaint(new Color(242, 95, 0));
            g2d.fill(insert);
            g2d.setPaint(new Color(222, 83, 0));
            g2d.draw(insert);

            Stroke stroke = g2d.getStroke();
            BasicStroke stitch = new BasicStroke(1f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10f, new float[]{3f}, 0f);
            g2d.setStroke(stitch);
            g2d.setColor(new Color(167, 65, 1));
            g2d.drawLine(0, 2, width, 2);
            g2d.drawLine(0, height - 2, width, height - 2);
            g2d.setColor(new Color(249, 127, 50));
            g2d.drawLine(0, 3, width, 3);
            g2d.drawLine(0, height - 1, width, height - 1);
            g2d.setStroke(stroke);

            if (isSelected()) {

                JLabel renderer = getRenderer();
                renderer.setFont(getFont());
                renderer.setText(getTag());
                renderer.setSize(width - 8, renderer.getPreferredSize().height);
                renderer.setForeground(Color.WHITE);
                renderer.setHorizontalAlignment(JLabel.CENTER);
                int xPos = 4;//((tagWidth - renderer.getWidth()) / 2);
                int yPos = (height - renderer.getHeight()) / 2;
                g2d.translate(xPos, yPos);
                renderer.printAll(g2d);
                g2d.translate(-xPos, -yPos);

            }

            g2d.translate(-x, -1);

            height = getHeight() - 1;

            Shape baseShape = new TagShape(tagWidth, height);

            LinearGradientPaint lgpFill = new LinearGradientPaint(
                            new Point(0, 0),
                            new Point(0, getHeight() - 1),
                            new float[]{0f, 1f},
                            new Color[]{new Color(248, 248, 248), new Color(241, 241, 241)}
            );
            g2d.setPaint(lgpFill);
            g2d.fill(baseShape);

            LinearGradientPaint lgpOutline = new LinearGradientPaint(
                            new Point(0, 0),
                            new Point(0, getHeight() - 1),
                            new float[]{0f, 1f},
                            new Color[]{UIManager.getColor("Button.shadow"), UIManager.getColor("Button.darkShadow")}
            );
            g2d.setPaint(lgpOutline);
            g2d.draw(baseShape);

            JLabel renderer = getRenderer();
            renderer.setFont(getFont());
            renderer.setText(getText());
            renderer.setSize(renderer.getPreferredSize());
            renderer.setForeground(getForeground());

            x = (tagWidth - renderer.getWidth()) / 2;
            int y = (height - renderer.getHeight()) / 2;
            renderer.setLocation(x, y);
            g2d.translate(x, y);
            renderer.printAll(g2d);
            g2d.translate(-x, -y);

            g2d.setColor(Color.RED);
//            g2d.drawRect(0, 0, getWidth() - 1, getHeight() - 1);

            g2d.dispose();
        }
    }

    protected static class TagInsert extends Path2D.Float {

        public TagInsert(float width, float height) {

            float gap = 3;

            float radius = (height - (gap * 5)) / 4f;
            moveTo(0, 0);
            lineTo(width, 0);

            float yPos = 0;
            lineTo(width, 1);
            float topY = gap;
            for (int index = 0; index < 4; index++) {

                float bottomY = topY + radius;
                float x = width - (radius / 2);

                lineTo(width, topY);
                curveTo(x, topY, x, bottomY, width, bottomY);
                topY += radius;
                topY += gap;
                lineTo(width, topY);

            }

            lineTo(width, height);
            lineTo(0, height);
            lineTo(0, 0);

        }

    }

    protected static class TagShape extends Path2D.Float {

        protected static final float RADIUS = 8;

        public TagShape(float width, float height) {

            moveTo(RADIUS, 0);
            lineTo(width, 0);

            float clip = RADIUS / 2f;
            float topY = (height / 2f) - clip;
            float bottomY = (height / 2f) + clip;
            lineTo(width, topY);
            curveTo(width - clip, topY, width - clip, bottomY, width, bottomY);
            lineTo(width, height);
            lineTo(RADIUS, height);

            curveTo(0, height, 0, height, 0, height - RADIUS);
            lineTo(0, RADIUS);
            curveTo(0, 0, 0, 0, RADIUS, 0);

        }

    }
}

答案 1 :(得分:1)

使用javafx按钮而不是摆动。 javafx按钮具有css属性。

答案 2 :(得分:1)

根据您的要求,似乎JButton似乎是一个很好的起点,因为它拥有您想要的大部分内容。

就宽度而言,您很可能需要继承JButton,以便您可以更新构造函数中的宽度,如下所示:

private static class FancyButton extends JButton{
    public FancyButton(String name) {
        super(name);
        //Calculate new width
        setPreferredSize(new Dimension(newWidth, 30));
    }       
}  

或者设置一个方法来设置确定和设置宽度的按钮。

答案 3 :(得分:1)

由于您希望在JTable中使用这些内容,因此您需要一个自定义renderer来绘制带有或不带数字的按钮。数值和状态应存储在TableModel中。要获取按钮的行为,您需要一个自定义editor,可能需要一个返回JToggleButton的实例。在此example中,JCheckBoxJToggleButton的子类)的实例用于显示列中的状态和值。