Страницы

Поиск по вопросам

вторник, 16 июля 2019 г.

Работа с графикой

Добрый день. Наткнулся на вот такой пример в учебнике:
public class Tester { public static void main(String[] args) { Frame frame = new Frame(); frame.start(); } } class Frame { private JFrame frame; private Paint oval; private int x,y; Frame(){ x = y = 120; frame = new JFrame(); oval = new Paint(); } public void start() { frame.getContentPane().add(oval); frame.setSize(700, 700); frame.setVisible(true);
for(int count = 0;count <400;count++) { x++; y++; oval.repaint();
try { Thread.sleep(25); } catch (Exception ex) {
} } } class Paint extends JPanel { public void paintComponent(Graphics g) { g.setColor(Color.WHITE); g.fillRect(0, 0, this.getWidth(), this.getHeight()); g.setColor(Color.ORANGE); g.fillOval(x,y, 100, 100); } } }
Данный код рисует окружность в окне и используя цикл перерисовывает её, перемещая по диагонали. Так вот, изначально автор не добавлял эти две строки кода:
g.setColor(Color.WHITE); g.fillRect(0, 0, this.getWidth(), this.getHeight());
То есть не зарисовывал всё окно белым цветом перед рисованием фигуры на новой позиции. В результате фигура расплывалась. Как по мне это костыль. Когда я писал этот пример, я в учебник не смотрел, и метод repaint(); вызывал не у класса Paint, а у формы JFrame, у меня сразу всё нормально отображалось. Так вот собственно вопрос, какой из методов вы считаете целесообразней использовать? Вот мой код:
public class Tester { public static void main(String[] args) { Frame frame = new Frame(); frame.start(); } } class Frame { private JFrame frame; private Paint oval; private int x,y; Frame() { x = y = 120; frame = new JFrame(); oval = new Paint(); } public void start() { frame.getContentPane().add(oval); frame.setSize(700, 700); frame.setVisible(true);
for (int count = 0;count <400;count++) { x++; y++; **frame.repaint();**
try { Thread.sleep(25); } catch (Exception ex) {
} } } class Paint extends JPanel { public void paintComponent(Graphics g) { g.setColor(Color.ORANGE); g.fillOval(x,y, 100, 100); } } }


Ответ

Вообще ошибка в том, что в переопределённом методе paintComponent необходимо вызвать родительский метод:
public void paintComponent(Graphics g){ super.paintComponent(g); // <<< g.setColor(Color.ORANGE); g.fillOval(x,y, 100, 100); }
Он в частности и очистит фон и тогда вызов oval.repaint() будет работать как положено. Про это хорошо написано в документации к родительскому методу:
Further, if you do not invoke super's implementation you must honor the opaque property, that is if this component is opaque, you must completely fill in the background in a non-opaque color. If you do not honor the opaque property you will likely see visual artifacts.
Что примерно переводится как:
Далее, если вы не вызовете родительскую реализацию, вы должны учесть непрозрачность компонента, а именно, если он непрозрачный, вам необходимо полностью залить фон непрозрачным цветом. Если вы этого не сделаете, вы увидите визуальные артефакты.
Что мы и наблюдаем. Так что либо вызвать super.paintComponent(g), либо залить вручную (как у вас в коде). А как раз замена на frame.repaint() — это костыль, который скрывает истинную проблему и делает лишнюю работу (вам же не нужно перерисовывать всё окошко, нужно только содержимое перерисовать).

Комментариев нет:

Отправить комментарий