Reddit-подобный GUI

Я не очень знаком с Swing, но пытаюсь создать графический интерфейс Reddit. Наконец-то у меня все заработало, как и ожидалось, но я должен признать, что это довольно обыденно и не очень приятно смотреть. Я хочу добавить к нему несколько визуально привлекательных вещей, но, как я уже сказал, я новичок в Swing. Мне помогли сделать компоненты и тому подобное для моей идеи, так что я немного ошеломлен.

В настоящее время программа извлекает заголовки из верхней части субреддитов, вводимых пользователем. Как видно на скриншоте, смотреть на то, что я сказал, не очень приятно. Любые советы по дизайну будут оценены по достоинству!

Скриншот программы

PS Это определенно продвинуто для меня, поэтому просто хочу отметить, что я каким-то образом не создавал это с нуля без посторонней помощи. Также с тех пор исправили кривую иконку Reddit.

package com.Will.me;
 
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.select.Elements;
 
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
 
public class Reddit extends JFrame {
 
 
    static JPanel mainPanel = new JPanel();
    static JTextField textField = new JTextField(20);
    static JButton search = new JButton("Search");
    static JButton[] numbers = new JButton[28];
    static JButton[] headlines = new JButton[28];
    static JLabel[] votes = new JLabel[28];
    static JLabel title = new JLabel("tHeadlines from r/");
 
    public Reddit(){
        mainPanel.setLayout(null);
        setUpComponents();
 
        title.setVisible(false);
 
        search.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
 
                title.setVisible(true);
                String str = textField.getText();
                title.setText("");
                title.setText("tHeadline from r/"+str);
 
                getSubReddit(str);
 
 
            }
        });
 
 
        add(mainPanel);
        setTitle("Reddit");
        setSize(900,600);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setVisible(true);
    }
 
    public static void setUpComponents(){
 
        JLabel redditIcon = new JLabel();
        redditIcon.setBounds(10, 10, 100, 100);
        resizeRedditLogo(redditIcon);
        mainPanel.add(redditIcon);
 
        textField.setBounds(160, 20, 250, 30);
        search.setBounds(415, 23, 85, 25);
        textField.setBackground(Color.decode("#8B0000"));
 
        search.setOpaque(true);
        search.setBorderPainted(false);
        search.setBackground(Color.decode("#8B0000"));
        search.setForeground(Color.WHITE);
        search.setBorder(BorderFactory.createRaisedBevelBorder());
 
        int y = 120;
 
        for (int i = 0; i < numbers.length;i++){
            numbers[i] = new JButton(String.valueOf(i+1));
            numbers[i].setBounds(20, y, 40,30);
            numbers[i].setBackground(Color.decode("#8B0000"));
            numbers[i].setOpaque(true);
            numbers[i].setForeground(Color.WHITE);
            numbers[i].setBorder(BorderFactory.createLoweredSoftBevelBorder());
 
            y+= 40;
 
            numbers[i].setBorderPainted(false);
            mainPanel.add(numbers[i]);
        }
 
        int y2 = 120;
 
        for (int i = 0; i < headlines.length;i++){
 
            headlines[i] = new JButton("");
            headlines[i].setBounds(70, y2, 650,30);
            headlines[i].setBackground(Color.decode("#8B0000"));
            headlines[i].setOpaque(true);
            headlines[i].setForeground(Color.WHITE);
            headlines[i].setBorder(BorderFactory.createLoweredSoftBevelBorder());
            y2+= 40;
            headlines[i].setBorderPainted(false);
            mainPanel.add(headlines[i]);
 
        }
 
        int y3 = 120;
 
        JLabel voteTitle = new JLabel("# Votes");
        voteTitle.setFont(new Font("Arial", Font.BOLD, 30));
        voteTitle.setForeground(Color.WHITE);
        voteTitle.setBounds(755, 70, 120, 30);
        mainPanel.add(voteTitle);
 
        for (int i = 0; i < votes.length;i++){
 
            votes[i] = new JLabel("");
            votes[i].setBounds(750, y3, 120,30);
            votes[i].setForeground(Color.WHITE);
            votes[i].setBorder(BorderFactory.createRaisedBevelBorder());
            y3+= 40;
            votes[i].setBorder(BorderFactory.createRaisedBevelBorder());
            mainPanel.add(votes[i]);
        }
 
 
        textField.setForeground(Color.WHITE);
 
        title.setBorder(BorderFactory.createRaisedBevelBorder());
        title.setBounds(105, 70, 605, 30);
        title.setFont(new Font("Arial", Font.BOLD, 18));
        title.setForeground(Color.WHITE);
        mainPanel.add(title);
        mainPanel.setBackground(Color.RED);
        mainPanel.add(textField);
        mainPanel.add(search);
 
    }
 
    public static void getSubReddit(String sub){
 
 
        try
        {
            Document doc = Jsoup.connect("https://old.reddit.com/r/"+sub).get();
            Elements el = doc.select("p.title");
            Elements score = doc.select("div.score.unvoted");
            int i,j;
 
            for (i=0,j=0;i<el.size() && j<score.size();i++,j++){
 
                headlines[i].setText(el.get(i).text());
                votes[j].setText(score.get(j).text());
            }
 
        } catch (Exception e) {
            System.out.println(e.toString());
        }
 
    }
 
    public static void resizeRedditLogo(JLabel label){
 
        try {
            ImageIcon imageIcon = new ImageIcon(new ImageIcon("../RedditGUI/src/reddit1.png").getImage().getScaledInstance(120, 120, Image.SCALE_DEFAULT));
            label.setIcon(imageIcon);
        } catch (Exception e) {
            e.printStackTrace();
        }
 
 
 
    }
    public static void main(String[] args) {
        new Reddit();
    }
 
 

2 ответа
2

Что касается самого кода, вы должны попытаться отделить представление от «логики», применив один из различных шаблонов (наиболее часто используемые MVC и MVP), как сказал Гилбер Ла Блан в своем комментарии:

Что касается визуальной части, то проще создавать компоненты, которые обеспечивают функциональность (-ы) высокого уровня и которые вы используете для составления основного представления.

Для «разметки» почему бы не использовать JList с настраиваемым средством визуализации для отображения каждой строки. Если вам нужны интерактивные кнопки (которые, насколько я вижу в вашем коде, не реализованы), вы можете использовать Коробка или BoxLayout.

    Вступление. Извините, я перескочил на этот вопрос, не удосужившись прочитать его в деталях. Поскольку вы новичок, вам нужно больше сказать, чтобы дать полезный ответ. И я предпочитаю создать новый, а не редактировать первый.


    Легкий путь,

    Если вы не хотите так сильно меняться:

    1. Вы можете попробовать удалить статические поля. В идеале ничего не должно быть статичным вместо main точка входа.

    2. Вы также должны попытаться извлечь синтаксический анализ в специальный класс. Это приведет к лучшему разделению задач и инкапсуляции и позволит вам протестировать или поменять местами позже.

    3. Создайте компоненты. Отдельные компоненты приведут в порядок и прояснят конструкцию вашего пользовательского интерфейса.

    4. Используйте менеджеры компоновки для размещения компонентов.


    Трудный путь,

    Так да. Как сказано в моем более простом ответе и Жильбер Ле Блан вам следует попытаться реализовать шаблон, чтобы отделить пользовательский интерфейс от «логики». В MVC шаблон является распространенным в приложениях с графическим интерфейсом пользователя, он также используется расширенными компонентами Swing.

    Тогда почему бы не попробовать создать три класса:

    1. А модель которые представляют состояние вашего приложения; список результатов.
    2. А Посмотреть который содержит графические компоненты, используемые для отображения результатов модели.
    3. А контролер чтобы получать действия из представления и соответствующим образом обновлять модель.

    Модель

    Ваша модель едва ли List<Subreddit> где Subreddit это DTO с title и score. Но ваше представление должно быть уведомлено при изменении этого списка, поэтому вам нужно создать для него специальный класс и добавить некоторый шаблон для регистрации и уведомления одного (или нескольких) слушателей.

    Вид

    Ваше мнение — это ваше JFrame (или любой Container отображается внутри JFrame). Это представление должно прослушивать регистрацию слушателя в модели, чтобы он мог обновлять его при изменении результатов.

    class RedditAppView extends JFrame {
      public RedditAppView(RedditAppModel model) {
        model.addModelListener(new RedditAppModel.ModelAdapter(){
          public void onResultsChanged(List<Subbreddit> results) {
            refresh();
          }
        });
      }
    }
    

    Проще использовать LayoutManager чем использование абсолютной компоновки в ваших представлениях, потому что менеджер компоновки позаботится о многих частях при изменении размера вашего приложения и при изменении компонента. я люблю GridBagLayout, это подробный макет (но Java и Swing тоже подробны), но с его помощью можно добиться почти всего, и об этом довольно легко рассуждать. Однако создание и поддержание большого вида может быть очень сложным. Вот почему мы обычно создаем компоненты для инкапсуляции функциональности. В вашем случае вы можете создать:

    • А Logo который сам позаботится об изменении размера.
    • А SearchForm который содержит JTextField а также JButton добавить приемник одного события, чтобы получать уведомления, когда пользователь нажимает кнопку, или нажимает клавишу ввода, или подождите 3 секунды, .. это одно из преимуществ компонентности; вы можете развивать одну часть своего приложения, не затрагивая остальной код.
    • А список результатов. Это может быть JList внутри JScrollPane. Вы можете настроить пользовательский рендерер в этом списке для рендеринга Subreddit более привлекательным способом, чем по умолчанию toString.

    Ваша иерархия представлений может выглядеть так:

    RedditAppView
      |- Container
      |    |- Logo
      |    |- SearchForm
      |    |- ResultList
      |    |    |- JLabel ("# Votes")
      |    |    |- JScrollPane
      |    |    |    |- JList with Renderer
    

    Если вы используете предложенную модель, вы можете создать класс, который адаптирует ваш RedditAppModel к JListModel. Делая это, вам не нужно refresh самостоятельно список результатов будет обновлен автоматически:

    class ResultListModelAdapter extends DefaultListModel<Subreddit> {
      private final RedditAppModel model;
      private ResultListModelAdapter(RedditAppModel model) {
        this.model = model;
        this.model.addModelListener(new RedditAppModel.ModelAdapter() {
          @Override
          public void onResultsChanged(List<Subreddit> results) {
            int end = Math.max(getSize(), results.size()); // Not sure it is needed
            fireContentsChanged(this, 0, end);
          }
        });
      }
    
      @Override
      public int getSize() {
        return model.getResultsCount()
      }
    
      @Override
      public Subreddit getElementAt(int index) {
        return model.getResultAt(index);
      }
    }
    

    Контроллер

    Класс, который будет обрабатывать действия пользователя и отражать их в модели. У вашего контроллера будет один search действие вызывается при получении представления.

    class RedditAppController {
      private final RedditAppModel model;
    
      public RedditAppController(RedditAppModel model, RedditApi api) {
        this.model = model;
        this.api = api;
      }
    
      void onSearch(String term) {
        List<Subreddit> results = // Jsoup things
        model.setResults(results);
      }
    }
    

      Добавить комментарий

      Ваш адрес email не будет опубликован. Обязательные поля помечены *