Оглавление

Растровые изображения
Загрузка и рисование
Класс Image
Ожидание загрузки
Видео
Аплет CDRotation

    Аплет CDRotation

    В этом разделе мы расскажем об аплете CDRotation, в окне которого вращается компакт-диск.

    В левом верхнем углу каждого кадра отображается его порядковый номер (рис. 1). Этот номер не нарисован в файлах кадров, а надписывается приложением после рисования очередного кадра. Такое невозможно, если располагать в документе HTML файл AVI или многосекционный файл GIF.

    pic01.gif (3268 bytes)

    Рис. 1. Изображение вращающегося компакт-диска в окне аплета CDRotation

    Исходные тексты приложения

    Главный файл исходных текстов приложения CDRotation представлен в листинге 1.

    Листинг 1. Файл CDRotation.java

    import 
    java.applet.*;import java.awt.*;
    public 
    class CDRotation extends Applet  implements Runnable{  Thread m_CDRotation = null; 
     private Graphics m_Graphics;  private Image m_Images[];  private int m_nCurrImage;
      private int m_nImgWidth  = 0;  private int m_nImgHeight = 0; 
     private boolean m_fAllLoaded = false;  private final int NUM_IMAGES = 11;
      public String getAppletInfo()  {    return "Name:
     CDRotation";  }
      private void 
    displayImage(Graphics g)  {    if (!m_fAllLoaded)      return;
        g.drawImage(m_Images[m_nCurrImage],     
      (size().width - m_nImgWidth)   / 2,       (size().height - m_nImgHeight) / 2,    
        null);
        g.drawString(   
       (new Integer(m_nCurrImage)).toString(),      (size().width - m_nImgWidth)  /2,     
    ((size().height - m_nImgHeight)/2)+     10);  }
      public void paint(Graphics g)  {   
     Dimension dimAppWndDimension = size();    g.setColor(Color.white);    
    g.fillRect(0, 0,       dimAppWndDimension.width  - 1,      
     dimAppWndDimension.height - 1);    g.setColor(Color.black);   
     g.drawRect(0, 0,       dimAppWndDimension.width  - 1,     
      dimAppWndDimension.height - 1);
        if (m_fAllLoaded)    {      displayImage(g);    }		
    
        else  
        g.drawString("Please, wait...",       
      10, dimAppWndDimension.height / 2);  }
      public void start()  {  
      if (m_CDRotation == null)    {      m_CDRotation = new Thread(this);  
        m_CDRotation.start();    }  }	
      public void stop()  {   
     if (m_CDRotation != null)    {      m_CDRotation.stop();   
       m_CDRotation = null;    }  }
      public void run()  {  
      m_nCurrImage = 0;
       
     if (!m_fAllLoaded)    {      repaint();      m_Graphics = getGraphics();
          m_Images   = new Image[NUM_IMAGES];
          MediaTracker tracker =      
       new MediaTracker(this);
        
      String strImage;
         
     for (int i = 0; i < NUM_IMAGES; i++)      {      
      strImage = "images/cdimg0" +         
      ((i < 10) ? "0" : "") + i + ".gif";
            m_Images[i] = getImage(         
     getDocumentBase(), strImage);
      
          tracker.addImage(m_Images[i], 0);      }
          try      {	tracker.waitForAll();
    	m_fAllLoaded = !tracker.isErrorAny();      }  
        catch (InterruptedException e)      {      }	
          if (!m_fAllLoaded)      {        stop();    
        m_Graphics.drawString(          "Load error", 10,         
      size().height / 2);			  
         
       return;      }
          m_nImgWidth  =     
        m_Images[0].getWidth(this);      m_nImgHeight =         m_Images[0].getHeight(this);    }
    	
        repaint();
        while (true)    {      try      {      
      displayImage(m_Graphics);	m_nCurrImage++;
    	if(m_nCurrImage == NUM_IMAGES)	
    m_nCurrImage = 0;
            
    Thread.sleep(30);      }
        
      catch (InterruptedException e)      {	stop();      }    }  }}

    Листинг 2 содержит исходный текст документа HTML, созданного для аплета CDRotation.

    Листинг 2. Файл CDRotation.tmp.html

    
    <applet name="CDRotation"  code="CDRotation"   
    codebase="file:/e:/sun/articles/vol13/src/CDRotation" 
     width="500"  height="600"  align="Top" 
     alt="If you had a java-enabled browser, you would see an applet
     here."></applet>

    Описание исходных текстов

    Рассмотрим наиболее важные методы нашего аплета.

    Метод start

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

    if (m_CDRotation == 
    null){  m_CDRotation = new Thread(this);  m_CDRotation.start();}

    Поток создается как объект класса Thread, причем конструктору передается ссылка на главный класс аплета. Поэтому при запуске потока управление получит метод run, определенный в классе аплета.

    Метод stop

    Метод stop останавливает работу потока, когда окно аплета исчезает с экрана:

    if(m_CDRotation != null){ 
     m_CDRotation.stop();  m_CDRotation = null;}

    Для остановки вызывается метод stop.

    Метод paint

    Сразу после получения управления, метод paint закрашивает окно аплета белым цветом и рисует вокруг него черную рамку.

    Затем метод проверяет содержимое флага m_fAllLoaded. Этот флаг установлен в значение true, когда все кадры видеофильма загружены и сброшен в значение false, когда загрузка кадров еще не завершена. Последняя ситуация возникает всегда при первом вызове метода paint.

    Если все изображения загружены, метод paint вызывает метод displayImage, определенный в нашем приложении:

    if(m_fAllLoaded){  displayImage(g);}

    Этот метод, о котором мы еще расскажем подробнее, отображает в окне аплета текущий кадр видеофильма.

    Если же кадры видеофильма еще не загружены, в окне аплета отображается соответствующее сообщение:

    else  g.drawString("Please, wait...",    
     10, dimAppWndDimension.height / 2);

    Метод run

    Метод run работает в рамках отдельного потока. Он занимается последовательным рисованием кадров нашего видеофильма.

    Прежде всего метод run записывает нулевое значение в поле m_nCurrImage, хранящее номер текущего отображаемого кадра:

    m_nCurrImage = 0;

    Далее выполняется проверка, загружены ли все кадры видеофильма, для чего анализируется содержимое флага m_fAllLoaded.

    Если изображения не загружены (а в самом начале так оно и есть) метод run перерисовывает окно аплета и получает контекст отображения для этого окна. Затем создается массив объектов Image для хранения кадров видеофильма:

    m_Images   =
     new Image[NUM_IMAGES];

    Метод run создает также объект класса MediaTracker для ожидания загрузки всех кадров видеофильма:

    MediaTracker tracker =   new MediaTracker(this);

    Далее метод run в цикле загружает изображения и добавляет их в объект класса MediaTracker для того чтобы можно было дождаться загрузки всех кадров:

    for (int i = 0; i < NUM_IMAGES; i++){  strImage = 
    "images/cdimg0" +   ((i < 10) ? "0" : "") + i + "
    .gif";  m_Images[i] = getImage(    getDocumentBase(), strImage);  
    tracker.addImage(m_Images[i], 0);}

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

    Имена файлов, составляющих отдельные кадры, начинаются с префикса cdimg0, вслед за которым идет номер кадра (00, 01, 02, и так далее), и расширение имени .gif.

    Ожидание загрузки кадров выполняется с помощью метода waitForAll, о котором мы вам уже рассказывали:

    try{  
    tracker.waitForAll();  m_fAllLoaded = !tracker.isErrorAny();}catch (InterruptedException e)
    {}

    После окончания ожидания флаг завершения загрузки устанавливается только в том случае, если метод isErrorAny вернул значение false, то есть если не было никаких ошибок.

    Если же произошла ошибка, в окне аплета отображается соответствующее сообщение, после чего работа метода run (и, следовательно, работа созданного для него потока) заканчивается:

    if(!m_fAllLoaded){  stop();  m_Graphics.drawString(  
      "Load error",    10, size().height / 2);  return;}

    В случае удачной загрузки всех кадров метод run получает ширину и высоту первого кадра видеофильма и сохраняет эти значения в переменных m_nImgWidth и m_nImgHeight:

    m_nImgWidth  =  
     m_Images[0].getWidth(this);m_nImgHeight =   m_Images[0].getHeight(this);

    Далее окно аплета перерисовывается:

    repaint();

    При этом метод paint отображает в окне аплета первый кадр видеофильма.

    На следующем этапе работы метода run запускается цикл отображения кадров фильма:

    while (true){  try  {    displayImage(m_Graphics); 
       m_nCurrImage++;    if(m_nCurrImage == NUM_IMAGES)      m_nCurrImage = 0;  
      Thread.sleep(30);  }  catch (InterruptedException e)  {    stop();  }  }

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

    Между отображением кадров выполняется задержка величиной 30 миллисекунд.

    Метод displayImage

    Метод displayImage вызывается из двух мест - из метода paint при перерисовке окна аплета и из метода run (периодически).

    Если кадры видеофильма не загружены, содержимое флага m_fAllLoaded равно false и метод displayImage просто возвращает управление, ничего не делая:

    if(!m_fAllLoaded)  return;

    Если же загрузка изображений завершена, этот метод рисует в центре окна текущий кадр видеофильма, вызывая для этого знакомый вам метод drawImage:

    g.drawImage(m_Images[m_nCurrImage], 
     (size().width - m_nImgWidth)   / 2,  (size().height - m_nImgHeight) / 2, 
      null);

    После того как кадр нарисован, мы надписываем на нем его порядковый номер, вызывая для этого метод drawString:

    g.drawString((new Integer(  m_nCurrImage)).toString(), 
     (size().width - m_nImgWidth)   / 2,  ((size().height - m_nImgHeight) / 2) + 10);