Почему часы мерцают?

Следующий код представляет собой анимацию часов.

Какие изменения надо было бы внести в программу, чтобы она не мерцала?

Почему для отображения раздач инициация поля должна происходить в функции addNotify()?

Вы можете скопировать все это в один файл .java, и он должен работать (мерцание;)).

import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.Calendar;
import java.util.GregorianCalendar;

public class ac extends Frame implements Runnable {

    // for double buffering
    Graphics og;
    Image oi;
    Thread t = null;
    int width, height;
    int timeH, timeM, timeS, radius = 50, lenH, lenM, lenS,
        lenIn, cx, cy, x1, y1, x2, y2;
    private boolean timeToQuit;

    ac() {

        addWindowListener(new WindowAdapter() {

            public void windowClosing(WindowEvent we) {
                stopRunning();
                dispose();
            }

        });

        setMinimumSize(new Dimension(300, 300));

        EventQueue.invokeLater(new Runnable() {

            public void run() {
                setVisible(true);
            }
        });
    }

    public void addNotify() {
        super.addNotify();

        width = getBounds().width;
        height = getBounds().height;
        setSize(width, height);

        lenH = 5 * radius / 10;
        lenM = 6 * radius / 10;
        lenS = 7 * radius / 10;
        lenIn = 8 * radius / 10;
        cx = width / 2;
        cy = height / 2;

        // for double buffering
        oi = createImage(width, height);

        og = oi.getGraphics();

    }

    public static void main(String [] args) {
        ac clock = new ac();
        clock.start();
    }

    public void start() {
        if (t == null) {
            t = new Thread(this);
            t.start();
        }
    }

    public void stopRunning() {
        timeToQuit = true;
    }

    public void run() {

        Thread.currentThread().setPriority(Thread.NORM_PRIORITY - 1);
        do {
            repaint();
            try {
                Thread.sleep(500);
            }
            catch (InterruptedException e) {
            }
        }
        while (!timeToQuit);
        System.out.println("Quitting");

    }

    public void paint(Graphics g) {
        og.setColor(new Color(170, 170, 170));
        og.fillRect(0, 0, width, height);
        Calendar cal = new GregorianCalendar();
        timeH = cal.get(Calendar.HOUR);
        timeM = cal.get(Calendar.MINUTE);
        timeS = cal.get(Calendar.SECOND);

        if (timeH >= 12)
            timeH -= 12;

        for (int i = 1 ; i < 13 ; i++) {
            og.setColor(new Color(20, 20, 20));
            x2 = (int) (cx + radius * Math.sin(i * 2 * 3.14159f / 12));
            y2 = (int) (cy - radius * Math.cos(i * 2 * 3.14159f / 12));
            if (i % 3 != 0) {
                x1 = (int) (cx + 0.9f * radius * Math.sin(i * 2 * 3.14159f / 12));
                y1 = (int) (cy - 0.9f * radius * Math.cos(i * 2 * 3.14159f / 12));
            }
            else {
                x1 = (int) (cx + 0.8f * radius * Math.sin(i * 2 * 3.14159f / 12));
                y1 = (int) (cy - 0.8f * radius * Math.cos(i * 2 * 3.14159f / 12));
            }
            og.drawLine(x1, y1, x2, y2);
            og.setColor(new Color(230, 230, 230));
            og.drawLine(x1 - 1, y1 - 1, x2 - 1, y2 - 1);
        }

        og.setColor(new Color(20, 20, 20));
        x2 = (int) (cx + lenH * Math.sin((timeH + timeM / 60.0f + timeS / 3600.0f)
            * 2 * 3.14159f / 12));
        y2 = (int) (cy - lenH * Math.cos((timeH + timeM / 60.0f + timeS / 3600.0f)
            * 2 * 3.14159f / 12));
        og.drawLine(cx, cy, x2, y2);
        og.setColor(Color.red);
        og.drawLine(cx - 1, cy - 1, x2 - 1, y2 - 1);

        og.setColor(new Color(20, 20, 20));
        x2 = (int) (cx + lenM * Math.sin((timeM + timeS / 60.0f) * 2 * 3.14159f
            / 60));
        y2 = (int) (cy - lenM * Math.cos((timeM + timeS / 60.0f) * 2 * 3.14159f
            / 60));
        og.drawLine(cx, cy, x2, y2);
        og.setColor(Color.green);
        og.drawLine(cx - 1, cy - 1, x2 - 1, y2 - 1);

        og.setColor(new Color(20, 20, 20));
        x2 = (int) (cx + lenS * Math.sin(timeS * 2 * 3.14159f / 60));
        y2 = (int) (cy - lenS * Math.cos(timeS * 2 * 3.14159f / 60));
        og.drawLine(cx, cy, x2, y2);
        og.setColor(Color.blue);
        og.drawLine(cx - 1, cy - 1, x2 - 1, y2 - 1);

        og.setColor(new Color(20, 20, 20));
        og.drawOval((width - 2 * radius) / 2, (height - 2 * radius) / 2,
            2 * radius, 2 * radius);
        og.setColor(new Color(230, 230, 230));
        og.drawOval((width - 2 * radius) / 2 - 1, (height - 2 * radius) / 2 - 1,
            2 * radius, 2 * radius);

        g.drawImage(oi, 0, 0, this);
    }
}

person user2433771    schedule 30.05.2013    source источник
comment
нарисуйте его дважды на одних и тех же размерах, и он не будет filck   -  person Muhammad Bekette    schedule 30.05.2013
comment
Извините, что вы подразумеваете под дважды на одних и тех же размерах?   -  person user2433771    schedule 30.05.2013


Ответы (2)


Здесь используется awt, который устарел и рисует на основном фрейме — не лучший способ сделать качающийся пользовательский интерфейс. Вы можете попробовать это:

  1. сделайте таймер - количество сна меньше или больше, попробуйте 300 или 800
  2. используйте панель для рисования, добавьте панель в рамку
  3. используйте свинг (используйте JComponent, он автоматически выполняет двойную буферизацию, а JFrame

Для 2 и 3 вы можете сохранить исходный класс для создания Frame/JFrame и новый класс для java.awt.Panel или javax.swing.JComponent, который добавляется к первому.

Не мерцает в моей системе Mac, поэтому я думаю, что это проблема ОС + время ее обновления. Это означает, что предложение 1 может сработать — сначала попробуйте его. Говоря о строке кода:

Thread.sleep(800);
person tgkprog    schedule 30.05.2013
comment
Спасибо за ответ, я полон решимости сделать это в awt, чтобы лучше понять его механизмы. Изменение таймера - сон не работал. Если он не мерцает в вашей системе, попробуйте увеличить окно, переместить и т. д., и, вероятно, это произойдет. - person user2433771; 31.05.2013

Хотите верьте, хотите нет, но проблема была решена добавлением функции:

    public void update(Graphics g)

       {

            paint(g);

       }

Но остается вопрос, почему? И почему не отображаются руки, когда вы инициируете поля класса вне функции addNotify()?

person user2433771    schedule 31.05.2013
comment
обновление по умолчанию очищает экран, а затем вызывает краску. Это в книгах 101. забыл об этом после всех этих лет. поэтому он побеждает двойную буферизацию. когда вы сделали это, текущее содержимое экрана остается до тех пор, пока вы не подготовите новый экран в буфере вне экрана и не нарисуете его одним изображением для рисования. - person tgkprog; 31.05.2013