Обновление моей графики

Я только начинаю изучать программирование на Java и совсем недавно с графикой, и столкнулся с некоторыми проблемами. Я хочу создать игру, похожую на гномью крепость, с цветным текстом вместо изображений.

Это то, что у меня есть до сих пор:

public void paint(Graphics g) {
    super.paint(g);
    g.setColor(Color.red);

    for (int i = 0; i < 50; i++) {
        g.drawString("[Game goes here]", 100, 150);
        g.dispose();
        System.out.println(i);
    }
    g.dispose();
}

public GTest() {
    setSize(Toolkit.getDefaultToolkit().getScreenSize().width / 3, Toolkit
            .getDefaultToolkit().getScreenSize().width / 3 + 50);
    setDefaultCloseOperation(EXIT_ON_CLOSE);
    setVisible(true);
}

public static void main(String[] args) {
    GTest myWindow = new GTest();
}

Я хочу, чтобы графика обновлялась по таймеру, но я не совсем уверен, как это сделать.

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

РЕДАКТИРОВАТЬ:

Поэтому я добавил этот бит:

String[] letters = new String[10];
float fontsize = Toolkit.getDefaultToolkit().getScreenSize().width / 30;

public void paint(Graphics g) {

    g.setColor(textColor);
    setFont(getFont().deriveFont(fontsize));
    for(int i = 0; i < 10; i++){
        if((int) (Math.random() * 100) > 97){
            letters[i] = "w";
            textColor = new Color(0, 0, 100);

        }else{
            letters[i] = "l";
            textColor = new Color(0, 100, 0);
        }
        g.drawString(letters[i], i * 3, 10);
    }

Но теперь вообще не отображается. Я добавил sysout после g.drawString, и он выполнил его, поэтому я не уверен, в чем проблема.


person Sam    schedule 10.09.2013    source источник
comment
Не переопределяйте paint() JFrame. Это НЕ то, как вы рисуете на заказ. Прочтите руководство по Swing на странице Custom Painting, чтобы узнать о правильном подходе.   -  person camickr    schedule 11.09.2013


Ответы (2)


При выполнении чего-либо на основе таймера в Swing javax.swing.Timer< /a> класс — ваш лучший друг.

Это JavaDocs довольно хорошо объясняет его использование, так что вот пример.

public class GTest extends JFrame implements ActionListener {
    private Color textColor = Color.BLACK;
    private Random random = new Random();

    @Override
    public void paint(Graphics g) {
        super.paint(g);
        g.setColor(textColor);
        g.drawString("[Game goes here]", 100, 150);
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        textColor = new Color(random.nextInt(0x00ffffff));
        repaint();
    }

    public GTest() {
        setSize(Toolkit.getDefaultToolkit().getScreenSize().width / 3,
                Toolkit.getDefaultToolkit().getScreenSize().width / 3 + 50);
        setDefaultCloseOperation(EXIT_ON_CLOSE);

        Timer timer = new Timer(500, this);
        timer.setInitialDelay(0);
        timer.start();

        setVisible(true);
    }

    public static void main(String[] args) {
        GTest myWindow = new GTest();
    }
}

Это снова ваш класс GTest, но на этот раз цвет текста автоматически меняется каждые 0,5 секунды.

Обратите внимание на два основных изменения:

  1. Конструктор теперь устанавливает новый экземпляр Timer без начальной задержки, период 500 мс, а его прослушиватель настроен на this. Это означает, что этот сконструированный экземпляр будет прослушивать тики этого таймера.
  2. Для этого нам нужно было реализовать интерфейс ActionListener. Это заставляет нас написать метод actionPerformed(), который вызывается каждый раз, когда тикает таймер. В этом методе мы меняем цвет текста на случайный и вызываем repaint(), который впоследствии вызывает наш метод paint() и записывает текст.

P.S. Вам не нужно каждый раз dispose() выполнять Graphics объект. На самом деле это также сказано в его JavaDoc (подчеркните мое):

Графические объекты, которые предоставляются в качестве аргументов методам paint() и update() компонентов, автоматически освобождаются системой при возвращении этих методов. Для повышения эффективности программисты должны вызывать dispose() после завершения использования объекта Graphics, только если он был создан непосредственно из компонента или другого объекта Graphics.

person Petr Janeček    schedule 10.09.2013
comment
Не стесняйтесь задавать любые вопросы. - person Petr Janeček; 11.09.2013
comment
Это действительно полезно. Также интересно, как бы вы изменили размер текста? Я попробовал два параметра для drawText, но это не сработало. - person Sam; 11.09.2013
comment
@Sam Из этот вопрос, например, setFont(getFont().deriveFont(18f)); должно работать в JFrame. Попробуйте поискать в Google java swing font size, чтобы получить больше ответов. - person Petr Janeček; 11.09.2013
comment
@sam Чтобы изменить размер текста, вам нужно изменить шрифт. - person MadProgrammer; 11.09.2013
comment
Если бы вы использовали setFont(getFont... как бы вы изменили размер на основе переменной или screenSize или не могли бы. - person Sam; 11.09.2013
comment
@Sam Часть 18f в моем последнем комментарии представляет собой число с плавающей запятой (равное 18,0). Это размер, который я установил. Однако это может быть переменная или любая другая дробь, если это float. - person Petr Janeček; 11.09.2013
comment
Хорошо, я вижу. Большое спасибо. - person Sam; 11.09.2013
comment
Не переопределяйте paint() JFrame. Это НЕ то, как вы рисуете на заказ. Прочтите руководство по Swing на странице Custom Painting, чтобы узнать о правильном подходе. - person camickr; 11.09.2013

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

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

Вместо этого я бы использовал что-то вроде JPanel и переопределил его метод paintComponent. Это обеспечит вам автоматическую двойную буферизацию.

Поскольку Swing является однопоточной структурой, ожидается, что все обновления пользовательского интерфейса будут выполняться в контексте потока переноса событий.

Это немного усложняет жизнь, когда дело доходит до таких вещей, как ожидание и синхронизация обновлений краски.

К счастью, Swing предоставляет javax.swing.Timer, который позволяет запланировать событие на какое-то время в будущем. Это также может быть настроено на повторение и регулярные интервалы.

Timer timer = new Timer(40, new ActionListener() {
    public void actionPerformed(ActionEvent e) {
        // perform your required actions here
    }
});

Теперь будьте осторожны, метод actionPerformed выполняется с контекстом EDT, это означает, что любая длительная/отнимающая много времени обработка, которую вы здесь выполняете, может привести к тому, что ваш пользовательский интерфейс перестанет рисовать.

Взгляни на

Больше подробностей...

person MadProgrammer    schedule 10.09.2013
comment
@ Сэм Это. Знание EDT имеет решающее значение в программировании на Swing. Кроме того, о рисовании непосредственно в JFrame - каждый JFrame имеет панель содержимого, которая по умолчанию является JPanel. Либо замените свой собственный JPanel, либо нарисуйте прямо на предоставленном. Посмотрите на JFrame#setContentPane() и getContentPane(). - person Petr Janeček; 11.09.2013