Двойная буферизация

Я уверен, что вы заметили в первом апплете, что круг мерцает. Это происходит по одной простой причине: каждый раз, когда вызывается метод paint вызывается repaint(), экран апплета полностью очищается. Из-за этого мы видим пустой экран. У нас есть три возможности suppress этот феномен.

1. Не очищать экран вовсе
2. Очищать экран только в том месте, где произошли изменения
3. Использовать двойную буферизацию

Не очищать экран вовсе

Эта идея, как кажется, решает все проблемы - однако это заблуждение. В нашем случае это означает, что будет рисовать толстую красную линию вдоль апплета, так как экран остается красным на каждой позиции, где побывал шар. В некоторых ситуациях это приемлемо, но мы хотим видеть двигающийся шар, а не толстую красную линию. Поэтому такя техника полезна только для объектов, которые не двигаются.
Во-первых, важно знать одну вещь. Вызов repaint() не вызывает метод paint() в одно и то же время. Вместо этього вызывается метод update(). Если он не переписывает этот метод, update() очищает весь экран и затем вызывает paint(), который рисует задний фон и наш круг снова. Чтобы избежать очистки экрана нам следует переписать метод update(). Наш новый метод update() больше не очищает экран, а только вызывает paint(). Это делается с помощью пары строк кода:

public void update(Graphics g)
{

    paint(g);
}

Как я уже говорил выше, это не решение для нашего апплета. Но это поможет вам понять, что repaint() вызывает не paint(), а update(), который затем вызывает paint().

Очищать экран только в том месте, где произошли изменения

Это решение основано на идее перерисовывать только те части апплета, где что-то изменилось. Такая концепция очень подходяща для игр типа Snake (змейка). Если последняя часть вашей змеи окрашена в цвет, совпадающий с цветом заднего фона, эта часть перекрашивает части змеи там, где она уже была. Я не хочу обсуждать это решение в деталях, так как оно годится только для очень специальных ситуаций. Так что давайте поговорим о двойной буферизации, которая является действительно хорошим и эффективным решением для избежания мерцания экрана. Вы можете использовать его в любом апплете точно так же, как я сейчас продемонстрирую, так что пусть эта проблема никогда вас больше не волнует!

Двойная буферизация

Я сказал, и не отказываюсь от своих слов, что вы можете применять двойную буферизацию очень легким путем. Двойная буферизация - это .... Если все, что должно быть прорисовано, уже прорисовано, то эта картинкакопируется на экран апплета. В деталях метод делает следующее:

1. Генерирует новое изображение вне экрана, используя createImage и хранит эту картинку в экземпляре переменной (= генерирует пустую картинку)
2. Вызывает GetGraphic, чтобы получить графическое содержимое для картинки
3. Рисует все (включая очистку экрана полностью) в картинке вне экрана (= рисует картинку на заднем плане)
4. Когда все готово, помещает эту картинку над картинкой, расположеной на экране (= рисует картинку на переднем плане)

Эта техника означает, что изображение создается еще до того, как попадает на экран. Когда происходит его копирование на передний план, старые пиксели замещаютя новыми. В результате мерцания не будет - ведь пустого экрана мы не увидим даже в течение миллисекунды!
Единственный недостаток двойной буферизации состоит в том, что в течение этого процесса производится много данных, а каждое изображение рисуется два раза - сначала вне экрана, затем на нем. Однако в большинстве случаев и на быстрых компьютерах это намного лучше траты времени на поиск других решений.
Теперь, после этой теоретической части я покажу вам как применить метод двойной буферизации в наш апплет с двигающимся шаром (основной код приведен в предыдущей главе):

Двойная буферизация: код

// объявляем две переменных в заголовке программы
private Image dbImage;
private Graphics dbg;

... другой код...

/** Update - метод, реализующий двойную буферизацию */
public void update (Graphics g)
{

    // initialize buffer
    if (dbImage == null)
    {
      dbImage = createImage (this.getSize().width, this.getSize().height);
      dbg = dbImage.getGraphics ();
    }

    // очищаем экран на заднем плане
    dbg.setColor (getBackground ());
    dbg.fillRect (0, 0, this.getSize().width, this.getSize().height);

    // рисуем элементы на заднем плане
    dbg.setColor (getForeground());
    paint (dbg);

    // рисуем картинку на экране
    g.drawImage (dbImage, 0, 0, this);
}

Как я сказал ранее, вы можете копировать и вставлять этот код в каждый апплет, использующий анимацию!

Скачать исходный код апплета
Скачать исходный код двойной буферизации
Запустить апплет

Следующая глава:
Мяч скачет и меняет направление

Fabian Birzele, 2001-2003.
перевод и веб-дизайн: В.Мурзагалин, 2004.