java - 如何使用 Open Cloud in swing 以编程方式布局动态词云?

标签 java swing tag-cloud

我已经到了可以显示我的 OpenCloud 的阶段了Java 中的词云使用此 code与 Swing 。它从文件中逐行加载文本,吞噬它并吐出我想要显示的单词。这被传递到我的云,然后在每次读取和更新一批行时添加它们。目前我正在使用默认布局管理器,但它只是将新词附加到窗口底部,最终流过窗口底部。 这不是我想要的。

我追求的是更像 wordle 的东西:

enter image description here

理想情况下我希望它:

  • 瞄准 JPanel 的中心
  • 径向向外添加单词
  • “每行”允许多个单词(请注意在方式想要之间你有一个当前 在新行上。)
  • Trickier:修剪到达边缘时无法容纳的低分单词。

根据我链接到的解决方案,我将这些添加为 JLabels,并根据每个标签的权重使用字体。

我浏览了所有不同的 swing 布局管理器,但似乎没有一个适合编程。如果我想自定义我的布局,我唯一的选择,因为我坚持使用 Swing(是的,几乎完全坚持),似乎是使用 absolute layout positioning .这似乎是一头很难驯服的野兽。不幸的是,对于那些想要推荐一些很棒的非标准 LayoutManager 的人,我无法将任何包添加到我的开发机器/访问互联网。我想我需要做的是在页面的某处随机添加单词,然后对其他单词执行相同的操作并调整它们,以免它们在窗口边缘相互碰撞。

看来我需要知道:

  • 是否有一种“已知”的方法可以使用一种标准布局管理器来进行这种随机/延迟的布局调整?
  • 有没有一种方法可以在没有布局管理器的情况下进行这种布局?这更容易吗?
  • 编写我自己的布局管理器会更容易吗?这肯定是一项艰巨的任务,不是吗?

如果这看起来像是我只是在寻求一个解决方案,我很抱歉,我对 Java 有点陌生(我能说多久才能摆脱这种说法!)而且我不知道从哪里开始这类事情,以及任何帮助/提示,​​我们将不胜感激。

最佳答案

这是一个使用 WrapLayout 的示例,它似乎工作正常:

import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
import javax.swing.Timer;

import org.mcavallo.opencloud.Cloud;
import org.mcavallo.opencloud.Tag;

public class TestOpenCloud {

    private static final String[] WORDS = { "art", "australia", "baby", "beach", "birthday", "blue", "bw", "california", "canada", "canon",
            "cat", "chicago", "china", "christmas", "city", "dog", "england", "europe", "family", "festival", "flower", "flowers", "food",
            "france", "friends", "fun", "germany", "holiday", "india", "italy", "japan", "london", "me", "mexico", "music", "nature",
            "new", "newyork", "night", "nikon", "nyc", "paris", "park", "party", "people", "portrait", "sanfrancisco", "sky", "snow",
            "spain", "summer", "sunset", "taiwan", "tokyo", "travel", "trip", "uk", "usa", "vacation", "water", "wedding" };

    protected void initUI() {
        JFrame frame = new JFrame(TestOpenCloud.class.getSimpleName());
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        final JPanel panel = new JPanel(new WrapLayout());
        Cloud cloud = new Cloud();
        final Random random = new Random();
        for (String s : WORDS) {
            for (int i = random.nextInt(50); i > 0; i--) {
                cloud.addTag(s);
            }
        }
        for (Tag tag : cloud.tags()) {
            final JLabel label = new JLabel(tag.getName());
            label.setOpaque(false);
            label.setFont(label.getFont().deriveFont((float) tag.getWeight() * 10));
            panel.add(label);
        }
        JScrollPane scrollPane = new JScrollPane(panel);
        scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
        scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
        frame.add(scrollPane);
        frame.setSize(500, 550);
        frame.setVisible(true);
        Timer t = new Timer(1000, new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {

                final JLabel label = new JLabel(WORDS[random.nextInt(WORDS.length)]);
                label.setOpaque(false);
                int fontSize = random.nextInt(20) + 8;
                label.setFont(label.getFont().deriveFont((float) fontSize));
                panel.add(label);
                panel.revalidate();
                panel.repaint();
            }
        });
        t.start();
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                new TestOpenCloud().initUI();
            }
        });
    }

    public class WrapLayout extends FlowLayout {
        private Dimension preferredLayoutSize;

        /**
         * Constructs a new <code>WrapLayout</code> with a left alignment and a default 5-unit horizontal and vertical gap.
         */
        public WrapLayout() {
            super();
        }

        /**
         * Constructs a new <code>FlowLayout</code> with the specified alignment and a default 5-unit horizontal and vertical gap. The value
         * of the alignment argument must be one of <code>WrapLayout</code>, <code>WrapLayout</code>, or <code>WrapLayout</code>.
         * 
         * @param align
         *            the alignment value
         */
        public WrapLayout(int align) {
            super(align);
        }

        /**
         * Creates a new flow layout manager with the indicated alignment and the indicated horizontal and vertical gaps.
         * <p>
         * The value of the alignment argument must be one of <code>WrapLayout</code>, <code>WrapLayout</code>, or <code>WrapLayout</code>.
         * 
         * @param align
         *            the alignment value
         * @param hgap
         *            the horizontal gap between components
         * @param vgap
         *            the vertical gap between components
         */
        public WrapLayout(int align, int hgap, int vgap) {
            super(align, hgap, vgap);
        }

        /**
         * Returns the preferred dimensions for this layout given the <i>visible</i> components in the specified target container.
         * 
         * @param target
         *            the component which needs to be laid out
         * @return the preferred dimensions to lay out the subcomponents of the specified container
         */
        @Override
        public Dimension preferredLayoutSize(Container target) {
            return layoutSize(target, true);
        }

        /**
         * Returns the minimum dimensions needed to layout the <i>visible</i> components contained in the specified target container.
         * 
         * @param target
         *            the component which needs to be laid out
         * @return the minimum dimensions to lay out the subcomponents of the specified container
         */
        @Override
        public Dimension minimumLayoutSize(Container target) {
            Dimension minimum = layoutSize(target, false);
            minimum.width -= getHgap() + 1;
            return minimum;
        }

        /**
         * Returns the minimum or preferred dimension needed to layout the target container.
         * 
         * @param target
         *            target to get layout size for
         * @param preferred
         *            should preferred size be calculated
         * @return the dimension to layout the target container
         */
        private Dimension layoutSize(Container target, boolean preferred) {
            synchronized (target.getTreeLock()) {
                // Each row must fit with the width allocated to the containter.
                // When the container width = 0, the preferred width of the container
                // has not yet been calculated so lets ask for the maximum.

                int targetWidth = target.getSize().width;

                if (targetWidth == 0) {
                    targetWidth = Integer.MAX_VALUE;
                }

                int hgap = getHgap();
                int vgap = getVgap();
                Insets insets = target.getInsets();
                int horizontalInsetsAndGap = insets.left + insets.right + hgap * 2;
                int maxWidth = targetWidth - horizontalInsetsAndGap;

                // Fit components into the allowed width

                Dimension dim = new Dimension(0, 0);
                int rowWidth = 0;
                int rowHeight = 0;

                int nmembers = target.getComponentCount();

                for (int i = 0; i < nmembers; i++) {
                    Component m = target.getComponent(i);

                    if (m.isVisible()) {
                        Dimension d = preferred ? m.getPreferredSize() : m.getMinimumSize();

                        // Can't add the component to current row. Start a new row.

                        if (rowWidth + d.width > maxWidth) {
                            addRow(dim, rowWidth, rowHeight);
                            rowWidth = 0;
                            rowHeight = 0;
                        }

                        // Add a horizontal gap for all components after the first

                        if (rowWidth != 0) {
                            rowWidth += hgap;
                        }

                        rowWidth += d.width;
                        rowHeight = Math.max(rowHeight, d.height);
                    }
                }

                addRow(dim, rowWidth, rowHeight);

                dim.width += horizontalInsetsAndGap;
                dim.height += insets.top + insets.bottom + vgap * 2;

                // When using a scroll pane or the DecoratedLookAndFeel we need to
                // make sure the preferred size is less than the size of the
                // target container so shrinking the container size works
                // correctly. Removing the horizontal gap is an easy way to do this.

                Container scrollPane = SwingUtilities.getAncestorOfClass(JScrollPane.class, target);

                if (scrollPane != null) {
                    dim.width -= hgap + 1;
                }

                return dim;
            }
        }

        /*
         *  A new row has been completed. Use the dimensions of this row
         *  to update the preferred size for the container.
         *
         *  @param dim update the width and height when appropriate
         *  @param rowWidth the width of the row to add
         *  @param rowHeight the height of the row to add
         */
        private void addRow(Dimension dim, int rowWidth, int rowHeight) {
            dim.width = Math.max(dim.width, rowWidth);

            if (dim.height > 0) {
                dim.height += getVgap();
            }

            dim.height += rowHeight;
        }
    }
}

关于java - 如何使用 Open Cloud in swing 以编程方式布局动态词云?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12796788/

相关文章:

java - 如果我想学习 Java 和 wicket 框架,从哪里开始

java - 在单独的文件中保存和加载 iCalendar 事件数据

ruby-on-rails - 带标签云的 Rails 分页

twitter - Twitter 热点话题提取

ruby-on-rails - 如何按 DESC 限制订单,然后按 ( :id)?

java - 如果任何场景失败,如何停止执行 Cucumber 功能

java - "SocketException: Unexpected end of file from server"来自 servlet 但不是来自独立应用程序

java - 检测损坏的 OGG 文件

java - 将 JProgressBar 与 Java Mail 结合使用(了解 Transport.send() 之后的进度)

java - 将 Action 监听器添加到在 java 循环中创建并从另一个类调用的新 jbutton