首页 > 解决方案 > 如何在 Swing 中制作适当的表格,并使用适当的布局将所有元素放在各自的位置?

问题描述

我正在开发一个网络爬虫并分几个阶段实施它。

此阶段获取网站上提到的所有链接,并在 JTable 中显示带有标题的网页的有效链接。

但是,我希望表格在我输入要解析的 TextField 中的 URL 之前已经存在(这是此分配的要求)。

另外,有没有办法修复当前布局(Macintosh 窗口的第一张图片) Current Swing Window ,以便我可以在指定位置正确显示所有 JFrame 元素,如下图所示:

所需的摆动窗口,其中所有元素

在此处输入图像描述

我曾尝试使用空布局来解决布局问题,它只会让任务成为一场噩梦,而且桌子会在我眼前消失。

在我开始输入链接之前,我找不到让表格存在的方法。

package crawler;

import javax.swing.*;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableModel;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class WebCrawler extends JFrame {
    public WebCrawler() {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setTitle("Web Crawler");
        setLocationRelativeTo(null);
        setSize(600, 400);

        JTextField urlText = new JTextField();
        urlText.setName("UrlTextField");
//        urlText.setBounds(10,10,300,20);
        urlText.setSize(300,20);

        JLabel title = new JLabel();
        JLabel titleLabel = new JLabel();
        title.setFont(new Font("Helvetica Nueve", Font.BOLD, 12));
        title.setLocation(10,30);
        title.setName("TitleLabel");

        titleLabel.setFont(new Font("Helvetica Nueve", Font.BOLD, 12));
        titleLabel.setText("Title:\t");

        JButton extract = new JButton();
        extract.setText("Parse");
        extract.setName("RunButton");
        extract.setSize(40,20);
        var LINE_SEPARATOR = System.getProperty("line.separator");
        extract.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                try {
                    String url = urlText.getText()/* Get url from JTextField */;
                    url = url.replaceAll("^\"+ \"+$", "");

                    final InputStream inputStream = new URL(url).openStream();
                    final BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
                    final StringBuilder stringBuilder = new StringBuilder();

                    String nextLine;
                    while ((nextLine = reader.readLine()) != null) {
                        stringBuilder.append(nextLine);
                        stringBuilder.append(LINE_SEPARATOR);
                    }


                    final String siteText = stringBuilder.toString();

                    Pattern pattern = Pattern.compile("<title>(.+?)</title>", Pattern.CASE_INSENSITIVE);
                    Matcher matcher = pattern.matcher(siteText);

                    String titleString = matcher.find()?matcher.group(1):"null";
                    title.setText(titleString);

                    SortedMap<String,String> links = new TreeMap<>();

                    Pattern pTag = Pattern.compile("(?i)<a([^>]+)>(.+?)</a>",Pattern.CASE_INSENSITIVE);
                    Pattern pLink = Pattern.compile("\\s*(?i)href\\s*=\\s*(\"([^\"]*\")|'[^']*'|([^'\">\\s]+))",Pattern.CASE_INSENSITIVE);

                    Matcher mTag = pTag.matcher(siteText);


                    try {
                        while (mTag.find()) {

                            String href = mTag.group(1);     // get the values of href
                            String linkElem = mTag.group(2); // get the text of link Html Element

                            Matcher mLink = pLink.matcher(href);

                            while (mLink.find()) {

                                String link = mLink.group(1);

                                link = link.substring(1,link.length()-1);

                                pattern = Pattern.compile("http",Pattern.CASE_INSENSITIVE);
                                matcher = pattern.matcher(link);
                                if(!matcher.find())
                                    link = String.join("",url,link);

                                try {
                                    URL urlValidator = new URL(link);

                                    if(urlValidator.getContent().equals("text/html"));
                                        links.put(link, linkElem);
                                } catch(Exception ex){
                                    System.out.println("Exception encountered at " + link );
                                }

                            }

                        }

                        JTable table = new JTable(toTableModel(links));
                        table.setName("TitlesTable");
                        JScrollPane scrollPane = new JScrollPane(table);
                        table.disable();
                        add(scrollPane, BorderLayout.CENTER);
                        table.clearSelection();
                    }
                    catch (Exception ex){
                        System.out.println(ex.getMessage());
                    }
                }
                catch(Exception ex){ System.out.println(ex.getMessage());}
            }
        });
        add(urlText, BorderLayout.PAGE_START);
        add(extract, BorderLayout.PAGE_END);
        add(titleLabel, BorderLayout.WEST);
        add(title, BorderLayout.LINE_START);
        //TODO: change design and layout

        setVisible(true);
    }

    public TableModel toTableModel(Map<?,?>map){
        DefaultTableModel model = new DefaultTableModel (
                new Object[] { "URL", "Title" }, 0
        );

        for (Iterator it = map.entrySet().iterator(); it.hasNext();) {
            Map.Entry entry = (Map.Entry)it.next();
            model.addRow(new Object[] { entry.getKey(), entry.getValue() });
        }

        return model;
    }
}

当我运行作业的测试工具给出的测试时,它显示“找不到合适的表格元素”。

发生这种情况是因为它甚至在将测试用例 URL 输入文本字段进行解析之前就需要检测活动表。

标签: javaswingjtablelayout-manager

解决方案


我希望表格在我输入 URL 之前已经存在

然后,您需要在创建并将所有其他组件添加到框架的同时创建一个空表并将其添加到框架。

您需要为您的 JTable 创建一个实例变量才能修改其模型。

将表格添加到框架的代码类似于:

add(title, BorderLayout.LINE_START);
DefaultTableModel model = new DefaultTableModel (new Object[] { "URL", "Title" }, 0);
table = new JTable( model );
add(new JScrollPane(table), BorderLayout.CENTER);

因此,现在您将在启动应用程序时看到一个只有列的空表。

toTableModel(...)方法中的代码现在变为:

//DefaultTableModel model = new DefaultTableModel (new Object[] { "URL", "Title" }, 0);
DefaultTableModel model = (DefaultTableModel)table.getModel();
model.setRowCount(0);

这只是删除了模型中的所有行,因此您的循环代码可以添加每一行新数据。

当然,您还将删除当前创建 JTable 和 JScrollPane 的 ActionListener 中的代码,因为不再需要它。

另外,有没有办法修复当前的布局

您的嵌套面板具有不同的布局管理器,以实现您想要的效果。

例如,您希望表格能够根据表格中的数据增长/缩小,因此应将其添加到CENTER您的BorderLayout.

但是顶部到两行是固定的,它们应该添加到子面板中。然后这个面板将被添加到PAGE_START你的BorderLayout. Maybe a verticalBoxLayout` 中。

然后每一行都是另一个子面板。第一行可以是一个水平面板,BoxLayout第二行是一个面板FlowLayout

阅读Layout Manager上的 Swing 教程,然后使用适当的布局管理器对子面板上的组件进行逻辑分组。


推荐阅读