Java-апплеты и интеллектуальный баннер-3

После всего того, что мы уже сделали единственное, что нам осталось, чтобы наш баннер стал полностью работоспособным это написать несколько небольших функций, которые также как, например и функция run будут стандартно вызваться Java-системой в различные моменты работы нашего апплета и первой из этих функций будет у нас функция init, которая хотя мы и отложили ее почти на конец, вообще говоря, является в апплете самой первой вызываемой Java-системой, и которая в силу этого служит в основном для присваивания начальных значений используемым в дальнейшем переменным. Именно для этого воспользуемся ей и мы, но сначала напишем ее заголовок:

  public void init()

  {

Такой заголовок функций стандартно вызываемых Java-системой стал для нас уже традиционным, поэтому сразу перейдем к тому, что мы и собирались сделать в этой функции – присваиванию начальных значений переменным и начнем его (хотя этот выбор и достаточно произволен) с номера текста (iText) который будет у нас «написан» в баннере первым.

Вполне логично конечно выбрать в качестве такого самый первый текст, входящий в массив strTexts и положить соответственно значение iText равным нулю, однако поскольку в функции run еще до всех операций с выбранным текстом этот номер будет сразу же увеличен на единицу, нам придется для достижения желаемого эффекта в качестве начального значения номера текста выбрать текст «первее первого» и положить для этого значение переменной iText равным минус единице:

    iText = -1;

Далее присвоим начальные значения переменным размера шрифта (nFont) и счетчика циклов (nFactor) и эти значения, для того чтобы наш апплет начал работать сразу правильно действительно должны быть равны нулю:

    nFont = (nFactor = 0);

Также должны быть равны нулю (только другому – «длинному») и начальные значения «угла поворота» (rot), «скорости» (dx) и «ускорения» (ddx) наших «звезд»:

    rot = (dx = (ddx = 0.0D));

Ну а переменные «большой картинки» (imgFull) и шрифта которым мы будем в ней писать наши тексты (fntTexts) поскольку они не являются числами, должны получить не нулевые а «нулевые» (null) значение, указывающее на то, что ни то, ни другое в данный момент еще не создано:

    imgFull = null;

    fntTexts = null;

На этом исчерпываются все переменные требующие присваивания им значений до начала работы алгоритма нашего баннера, а значит, завершается и функция init:

  }

и мы переходим к следующей нашей функции называемой update служащей для обновления изображения нашего апплета и имеющей заголовок:

  public void update(Graphics g)

  {

Этот заголовок несколько менее традиционен для нас чем, например заголовок функции init, однако он с точностью до собственно имени функции совпадает с заголовком функции paint и поэтому также не требует комментариев, но прежде чем перейти к телу нашей функции, остановимся вот на каком моменте: Вообще говоря, описывать эту функцию нам было бы и необязательно, поскольку любой апплет и наш не исключение «комплектуется» ее стандартным вариантом, который очищает изображение апплета и вызывает функцию paint для его новой прорисовки. Однако операция очистки изображения при выводе динамической картинки (что и имеет место в нашем случае) может вызвать эффект мелькания изображения поэтому переопределим ее сведя просто к вызову функции paint:

    paint(g);

и это вся наша функция update:

  }

и мы переходим к последней на данном этапе функции в некотором роде парной ранее рассмотренной нами функции start и имеющей заголовок:

  public void stop()

  {

а поскольку он опять-таки традиционен для нас, без комментариев по его поводу перейдем к телу функции, которое будет построено исходя из уже упомянутой парности этой функции к функции start. Эта парность заключается в том, что если функция start служила для создания и запуска потока предназначенного для динамического изменения картинки нашего баннера, то рассматриваемая в данный момент функция stop наоборот – для его остановки и уничтожения, поэтому начнем с того, что проверим, а есть ли у нас в данный момент такой поток:

    if(trdPict != null) {

и если такой поток в данный момент существует, остановим его, вызвав его стандартную функцию stop():

      trdPict.stop();

и «обнулим» его переменную trdPict показывая, что такого потока у нас больше нет:

      trdPict = null;

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

    }

  }

а с ее завершением завершается и вся «рисовательная» часть нашего баннера нужная нам на данный момент и Вы можете полюбоваться как летящими на Вас «звездами» так и «наезжающими» на Вас Вашими питомцами.

Предлагаю немного передохнуть. Отвлечемся от серьезного разговора. У меня радость последний месяц снова нормально работает беспроводной интернет. Если кто не знает, я вынужден постоянно и всюду иметь соединение с интернет, так как 80% обмена информацией с сотрудниками, партнерами и заказчиками у меня происходит через сеть. У Yota интернет был всегда один из самых качественных, пока они не стали переходить на новый стандарт связи. Сделали они это вовремя, но из-за неизбежных при таком сложном процессе перебоев мне пришлось брать запасной модем от другого провайдера. Однако последний месяц Yota сумела все нормализовать и теперь у меня скорость на новом модеме (заменили бесплатно, за что им большое спасибо) доходит до 25 мБ/сек! Я вам еще не надоел со своими восторгами? Давайте вернемся к нашей основной теме.

Однако не надо забывать, что все это мы делали вовсе не ради того чтобы Вы сами или кто-либо другой могли полюбоваться интересной и/или красивой картинкой и перечнем Ваших любимцев, а для того чтобы привлечь новых посетителей на Ваш сайт этим самым любимцам и посвященный, поэтому наш баннер должен делать как минимум тоже, что делает любой нормальный баннер – при «клике» по нему открывать Вашу страничку и к реализации этого действие мы сейчас и перейдем, однако предварительно на минуточку вернемся к функции init и дополним ее следующим оператором:

    setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));

который путем вызова стандартной функции setCursor устанавливает, что при попадании курсора в площадь нашего баннера он будет приобретать вид, определяемый стандартной функцией getPredefinedCursor класса «Курсор» (Cursor) для стандартной константы этого же класса HAND_CURSOR, а проще говоря, хорошо знакомый нам всем вид руки с вытянутым пальцем, приглашающим на наш баннер «кликнуть».

Ну вот, а теперь как раз и займемся вопросом: «Что же мы будем делать, если кто-либо этому приглашению последует?».

В Java существуют специальные средства для реакции на «телодвижения» мыши, однако во многих случаях оказываться более эффективным и удобным описать свою функцию реакции на любые «внешние раздражители» для нашего апплета называемые в Java «событиями» и именно так мы и поступим, начав как всегда с заголовка этой функции:

  public boolean  handleEvent(Event event)

  {

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

Конечно же, эта функция описана как «публичная» (public) поскольку она вызывается Java-системой, а вот ее тип отличается от всех остальных такого рода функций, с которыми мы раньше имели дело. Все предыдущие функции имели тип «пустой» (void) т.е. не вырабатывали никакого значения, поскольку Java-системе «не было никакого дела» до результатов их выполнения в данном же случае наша функция имеет тип «булевский» (boolean) поскольку по возвращаемому ею логическому значению Java-система «будет судить» о том, как мы «отнеслись» к происшедшему событию: Если мы его обработали, то мы будем возвращать значение «истина» (true) если же мы это событие проигнорировали, то мы будем возвращать значение «ложь» (false) «говоря» Java-системе чтобы она сама «предприняла необходимые действия». Ну и последнее что нужно отметить в отношении заголовка этой функции так это то, что ее единственный параметр имеет тип «Событие» (Event) и определяет «по какому поводу» наша функция была вызвана.

Теперь «разобравшись» с заголовком нашей функции перейдем к ее телу, которое логично оформить в виде оператора переключателя (switch) который в зависимости от того, какое событие произошло, будет выбирать нашу реакцию на него, а поскольку значение, характеризующее происшедшее событие хранится в стандартной переменной id класса «Событие» то мы пишем:

    switch(event.id) {

Первое что мы определим среди всех возможных вариантов действия нашей функции это то «что мы будем делать, когда не будем делать ничего» т.е. ее реакцию по умолчанию (default) на те события, которые мы обрабатывать не будем. С учетом того, что уже было сказано про заголовок функции совершенно ясно, что в этом случае мы просто должны вернуть Java-системе значение «ложь»:

      default:

        return(false);

Единственное же событие, которое нас будем интересовать на данный момент – это «клик» мышкой по нашему баннеру. Событие, происходящее при этом, характеризуется стандартной константой класса «Событие» MOUSE_DOWN, поэтому мы и напишем соответствующий «случай» (case) нашего переключателя:

      case Event.MOUSE_DOWN:

В этом «случае» мы сначала перерисуем наш баннер, используя нашу «старую знакомую» - функцию DrawFull():

        DrawFull();

а затем сделаем именно то, ради чего все это и затевалось – откроем страницу посвященную Вашему «живому уголку» для чего при помощи стандартной функции getAppletContext() получим контекст апплета и вызовем из него его стандартную функцию showDocument, которая как раз открытием HTML-страниц и «занимается» при этом передадим ей в качестве первого параметра специально для этого созданный унифицированный указатель ресурсов (URL) указывающий на Ваш сайт, а в качестве второго параметра – строковую константу "_blank", которая «говорит» этой функции о том, что Вашу страницу нужно открыть в новом окне браузера. Обрамление же try-catch, в которое будет заключен этот оператор, указывает на то, что если при создании унифицированного указателя ресурсов в переданной для этого в качестве параметра строке никакой допустимый протокол не будет найден или ее вообще не удастся проанализировать (хотя в нашем случае и то и другое крайне маловероятно) ненужно делать ничего:

        try {

          getAppletContext().showDocument(new URL("http://www.mypets.ru"), "_blank");

        } catch(MalformedURLException e) {

        }

И теперь нам остается только выйти из переключателя:

    }

вернуть Java-системе значение «истина» показывая ей, что с мышиным «кликом» мы «разобрались»:

    return(true);

завершить нашу функцию обработки событий:

  }

а заодно и весь наш класс MyBanner:

}

Ну вот теперь наш баннер полностью «готов к употреблению». Он делает все, что положено нормальному баннеру: Показывает картинки и текст «заманивая» посетителей на Ваш сайт, а когда эта его «миссия» увенчается успехом и тот, кого он заинтересует «кликнет» по нему мышкой он это самый сайт и откроет.

«Но позвольте» - спросите Вы – «именно что у нас и получился просто нормальный баннер! Всего того же можно было бы достичь без всех этих хлопот со сложностями Java путем элементарной Gif-анимации, ну может быть при несколько большем объеме файла самого баннера, нам же был обещан «интеллектуальный» баннер! Где же этот обещанный «интеллект» у нашего баннера?». И будете полностью правы! Я совершенно умышленно построил изложение именно таким образом чтобы, по возможности не отвлекаясь ни на что другое, сосредоточится на вопросах прорисовки картинки баннера, работы с потоками ну и на всех остальных которые мы рассмотрели и теперь, когда все эти вопросы остались позади, можно перейти ко второй части нашей работы – «интеллектизации» нашего баннера.

«Интеллектуальность» нашего баннера будет заключаться в том, что он будет реагировать не только на мышиный «клик» но и на любое попадание на него курсора, приглашая при этом нажать на него. Конечно, вообще говоря, аналогичного эффекта можно добиться при помощи хорошо известной комбинации HTML+JavaScript называемой ролловером, однако совершенно не факт что тот, кто предоставит Вам место на своем сайте для Вашего баннера, захочет с этим возиться, во всяком случае, за время немалых «странствований» по Интернету я таких баннеров не видел. Вы же воспользовавшись способом, описанным в этой статье, получите все сразу «в одном флаконе» или как теперь принято писать на кофе, компактах и других товарах «2 в 1», а я ведь обещал Вам только «интеллектуальный» но никак не «гениальный» баннер.

Для того чтобы приступить к этому этапу работы нам нужно будет дополнить описание переменных в начале нашего класса еще несколькими новыми описаниями:

  private boolean fOut, fBlink;

  private String  strRegards;

а также в уже имеющиеся описания добавить несколько новых переменных в частности в описание «целых» переменных (private int) - переменную nBlink, а в описание шрифтов (private Font) - переменную fntOver. Дополним мы также и описание «целых» констант (private final int) новой константой nBlinkFactor = 8.

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

Для начала определим значение переменной fntOver, которая будет хранить шрифт, для «написания» приглашение нажать на наш баннер, а поскольку это приглашение будет «спокойно стоять» а не «наезжать» на зрителя как перечисление всех Ваших питомцев т.е. его шрифт не будет меняться в процессе просмотра баннера то этот шрифт логичнее всего создать сразу же, как только становятся известны размеры нашего баннера т.е. в функции paint вместе с нашей «большой картинкой» но для этого мы сначала введем в этой функции описания нескольких локальных для нее переменных:

    int n;

    Font  newFont;

    FontMetrics newMetr;

Теперь сразу же, как только мы создали нашу «большую картинку» и определили координаты всех наших «звезд» но до прорисовки этой «большой картинки» при помощи функции DrawFull определимся со шрифтом для «написания» приглашения, для чего вначале положим равным нулю значение локальной для функции paint «целой» переменной n, которая будет у нас «играть роль» размера шрифта:

    n = 0;

Сам алгоритм создания шрифта для «написания» приглашения аналогичен алгоритму создания шрифтов для «написания» наших текстов, который мы использовали в функции run с той лишь разницей, что последний был «растянут во времени» поскольку мы использовали его для того чтобы сделать наши тексты имеющими разный размер сейчас же мы ограничимся тем, что сразу определим шрифт наибольшего размера, при котором наше приглашение впишется в наш баннер.

Будем определять наш шрифт для вывода приглашения в цикле (while) при каждом его повторении создавая новый шрифт (new Font) с именем strFont и стилем Font.BOLD аналогичными шрифту для вывода текстов и размером n увеличиваемым каждый раз на единицу и запоминать этот шрифт в локальной для функции paint переменной newFont. Для каждого созданного шрифта при помощи стандартной функции getFontMetrics будем определять его метрику, запоминая ее в локальной переменной newMetr, а из нее в свою очередь при помощи стандартной для этого класса функции getHeight() будем определять высоту созданного шрифта и сравнивать ее с высотой нашего баннера (hFull). Если оказалось что высота созданного шрифта больше высоты баннера, завершим цикл, в противном случае при помощи стандартной все для того же класса «Метрика Шрифта» функции stringWidth определим ширину «написанной» данным шрифтом строки приглашения (strRegards) и сравним ее с шириной нашего баннера (wFull). Если оказалось что ширина «написанной» данным шрифтом строки приглашения больше ширины баннера опять-таки завершим цикл, в противном случае выполним операторы, составляющие тело цикла:

    while(((newMetr = getFontMetrics(newFont = new Font(strFont, Font.BOLD, ++n))).getHeight() <= hFull) && (newMetr.stringWidth(strRegards) <= wFull))

каковое в данном случае очень просто и состоит в перезапоминании шрифта созданного на этом повторении цикла в переменной fntOver:

      fntOver = newFont;

Совершенно ясно, что при таком подходе после выхода из цикла ее значением будет последний шрифт при «написании», которым приглашения оно помещается в площади нашего баннера.

На этом завершаются изменения, которые нам было необходимо внести в функцию paint, и мы переходим собственно к «написанию» нашего приглашения т.е. к изменениям в функции прорисовки нашей «большой картинки» DrawFull.

Очевидно, что в этой функции изменениям подвергнется та ее часть, которая «занимается» «написанием» текстов и действительно теперь после установки для «большой картинки» цвета, которым будет «писаться» текст мы не будем сразу же устанавливать для нее шрифт, которым это «написание» будет осуществляться и выбирать текст для него из массива strTexts, а проверим сначала значение «булевской» переменной fOut, которая показывает, не находится ли курсор в площади нашего баннера и выполним вышеуказанные операторы, только если курсор находится вне его:

      if(fOut) {

        gFull.setFont(fntTexts);

        S = strTexts[iText];

в противном же случае т.е. если курсор находится в площади нашего баннера:

      } else {

установим для «большой картинки» в качестве шрифта, которым будет «писаться» текст шрифт для «написания» приглашения:

        gFull.setFont(fntOver);

Кроме того, для того, для того чтобы приглашение нажать на наш баннер в большей степени обращало на себя внимание, сделаем его мерцающим, а поскольку перерисовка текста путем вызова функции DrawFull из цикла модификации «звезд» в функции run происходит слишком часто, для того чтобы мерцание не было раздражающим, нам понадобится счетчик, который будет показывать, с какой периодичностью при вызове этой функции будет происходить переход от «видимости» нашего приглашения к его «невидимости». При каждом вызове функции DrawFull, когда курсор находится в площади баннера, мы будем уменьшать на единицу значение этого счетчика (переменной nBlink) и проверять, не стало ли оно неположительным:

        if(--nBlink <= 0) {

Если это произошло, положим значение переменной nBlink равным значению константы nBlinkFactor, возобновляя тем самым цикл «пересчета»:

          nBlink = nBlinkFactor;

и инвертируем значение переменной fBlink являющейся «флагом невидимости» приглашения сменяя тем самым его «фазу видимости» на «фазу невидимости» или наоборот:

          fBlink = !fBlink;

На этом та часть нашего алгоритма, которая «занимается» управлением «видимостью»-«невидимостью» завершена:

        }

и нам осталось только в зависимости от «флага невидимости» установить, какой же конкретно текст будет выводиться – пустая строка в том случае если наше приглашение находится в «фазе невидимости» или собственно текст приглашения в противном случае:

        S = (fBlink ? "" : strRegards);

А на этом завешена и вся та часть нашего апплета, которая занимается «написанием» нашего приглашения:

      }

поскольку собственно операторы «написания» текста по сравнению с предыдущим вариантом нашего апплета не изменились.

После того как была упомянута переменная fOut, которая управляет тем, какой именно текст - посвященный ли Вашему «живому уголку» или являющийся приглашением нажать на наш баннер будет на нем «написан» становится ясным что следующей «кандидатурой» на изменение у нас является функция реакции на события handleEvent, поскольку попадание курсора в площадь баннера и выход его из нее в зависимости от которых эта переменная изменяется как раз такими событиями и является.

Поскольку мы описали выбор реакции на события в виде переключателя (switch) добавлять к уже обрабатываемым событиям новые очень легко, просто вводя в этот переключатель новые «случаи» (case) и первым из таких «случаев» у нас будет «случай» попадания курсора в площадь нашего баннера который обозначается стандартной константой класса «Событие» MOUSE_ENTER:

      case Event.MOUSE_ENTER:

Когда это событие произойдет, мы положим значение «флагом невидимости» - переменной fBlink равным логической константе «истина»:

        fBlink = true;

а значение счетчика «пересчета» – переменной nBlink равным нулю:

        nBlink = 0;

переведя тем самым наше приглашение в «фазу видимости» и начав новый цикл «пересчета» и не выходя из переключателя, перейдем к обработке еще двух событий: Перемещению курсора по площади нашего баннера обозначаемому стандартной константой класса «Событие» MOUSE_MOVE и тому же самому, но осуществляемому при нажатой клавише мыши обозначаемому стандартной константой класса «Событие» MOUSE_DRAG:

      case Event.MOUSE_MOVE:

      case Event.MOUSE_DRAG:

Для этих событий мы просто положим признак нахождения курсора вне нашего баннера – переменную fOut равной логической константе «лож» показывая тем самым, что курсор находится в его площади:

        fOut = false;

и выйдем из переключателя (break):

        break;

Обрабатывать нам нужно и еще одно событие – событие обратное событию MOUSE_ENTER (попаданию курсора в площадь нашего баннера) – выход курсора за пределы нашего баннера, которое обозначается стандартной константой класса «Событие» MOUSE_EXIT:

      case Event.MOUSE_EXIT:

Наша реакция на это событие также будет очень проста – мы положим переменную fOut равной логической константе «истина» показывая тем самым, что курсор находится вне площади нашего баннера:

        fOut = true;

и опять-таки выйдем из переключателя:

        break;

На этом все наши изменения в функции handleEvent завершены и нам осталось очень немного – в функции init присвоить начальные значения тем нашим новым переменным, для которых это необходимо. Таковых переменных у нас всего две: Мы положим начальное значение переменной fOut равным логической константе «истина» показывая, что изначально курсор находится вне площади нашего баннера:

    fOut = true;

ну а переменной strRegards мы присвоим тот текст, который будет являться приглашением нажать на наш баннер и который будет очень коротким:

    strRegards = "Жми!";

И теперь наша работа над нашим баннером полностью завершена: Он, как и любой другой нормальный баннер показывает картинку – летящее на зрителя звездное небо, текст – перечисление всех Ваших пернатых, хвостатых, мохнатых и других питомцев, а когда все это заинтересует очередного зрителя и он «кликнет» по нему (конечно же, зритель по баннеру, но никак не наоборот) он ему (а вот тут как раз наоборот баннер зрителю, но никак не зритель баннеру) покажет Вашу страничку. К тому же наш баннер проявляет некоторого рода «интеллект» приглашая зрителя, как только курсор окажется в его площади (разумеется, баннера, а не зрителя), не тянуть с этим делом (в смысле с «кликом»).

На этом можно было бы, и завершить статью.

Но...

Как мы уже говорили «интеллектуальность» нашего баннера не превосходит того, чего можно было бы достичь и некоторыми другими средствами, однако Java инструмент настолько мощный, что позволяет делать многие вещи, которые в Интернете, пожалуй, никакими другими способами достичь нельзя и одной из них мы сейчас и займемся.

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

К тому же мы будем выводить это приветствие в отличие от всех текстов, которые мы раньше «писали» в нашем баннере не в одну, а в две строки, что также повлечет некоторое усложнение нашего апплета, и для начала мы изменим описание строковых переменных в начале нашего класса, сделав strRegards массивом и введя еще один вспомогательный массив strBuf состоящий из одного элемента, в результате чего оно приобретет следующий вид:

  private String  strRegards[], strBuf[] = new String[1];

Поскольку как я уже сказал, наше приветствие будет выводиться теперь не в одну строку, в первую очередь претерпит изменение та часть нашего апплета, которая в функции paint создает шрифт для его «написания» но сначала добавим в описание ее локальных «целых» переменных еще две I и L и после «обнуления» размера шрифта (переменной n) запомним в одной из них – в переменной L количество строк в нашем приветствии:

    L = strRegards.length;

Теперь перейдем к циклу выбора подходящего размера шрифта, и оставим в его заголовке только проверку, помещаются ли строки приветствия в наш баннер по высоте, причем будем при этом учитывать суммарную высоту всех строк приветствия т.е. произведение высоты одной строки на их общее количество которое мы запомнили в переменной L:

    while(((newMetr = getFontMetrics(newFont = new Font(strFont, Font.BOLD, ++n))).getHeight() * L) <= hFull) {

«Потеря интереса» к ширине выводимых строк со стороны заголовка цикла связана с тем, что поскольку этих строк стало несколько, делать такую проверку нужно для всех них и этим мы как раз, и займемся, перебирая все строки нашего приветствия во вложенном цикле по нашей новой переменной I. Условием окончания этого цикла будет либо полное исчерпание всех строк приветствия т.е. достижение переменной I значения не меньшего чем значение переменной L либо превышение шириной очередной из просматриваемых строк ширины нашего баннера т.е. получение значения ширины строки strRegards[I] вычисленного при помощи стандартной функции класса «Метрика Шрифта» stringWidth большего, чем ширина нашего баннера (wFull):

      for(I = 0; (I < L) && (newMetr.stringWidth(strRegards[I]) <= wFull); I++);

После выхода из этого вложенного цикла нам необходимо проанализировать причину, по которой этот выход осуществился. Если это произошло вследствие полного исчерпания всех строк нашего приветствия, значение переменной I будет как минимум равно L в противном случае:

      if(I < L)

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

        break;

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

      fntOver = newFont;

и переходим к следующему его повторению:

    }

На этом и завершаются наши изменения в функции paint, и мы от создания шрифта  fntOver переходим к его использованию т.е. «написанию» им нашего приветствия в функции DrawFull, но сначала добавим в описание ее локальных переменных типа «Строка» описание массива T[]. Что же касается собственно изменений в «написании» наших текстов то они затронут не только часть нашего апплета «занимающуюся» «написанием» приветствия, но и часть, связанную с перечислением Ваших «зверей» в частности текст из массива strTexts предназначенный для «написания» в данный момент мы будем перезапоминать не в строковой переменной S, а в первом (и единственном) элементе массива strBuf каковой же в свою очередь мы запомним в массиве T:

      if(fOut) {

        gFull.setFont(fntTexts);

        (T = strBuf)[0] = strTexts[iText];

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

      } else {

        gFull.setFont(fntOver);

        if(--nBlink <= 0) {

          nBlink = nBlinkFactor;

          fBlink = !fBlink;

          strBuf[0] = "";

        }

а во-вторых, в «переключателе фаз» также перейдем от переменной S к массиву T причем «роль» «фазы невидимости» т.е. пустой строки как раз этот «очищенный» массив strBuf и будет «играть»:

        T = (fBlink ? strBuf : strRegards);

На этом завершим части различные для случаев нахождения маркера вне площади нашего баннера и внутри нее:

      }

и мы перейдем к общей части, которая собственно «написанием» текстов и «занимается» и должна «уметь» делать это для любого из них и в частности для любого числа (ну как минимум для одной или двух) строк в этих текстах. Совершенно очевидно, что это можно обеспечить, только осуществляя это «написание» в цикле, а чтобы не «отягощать» его «работой», которую можно сделать один раз, определим и запомним некоторые значения еще до начала его выполнения.

Сначала запомним значение высоты шрифта, которым будет осуществляться «написание» текста в переменной iS, которую ранее мы использовали для определения координат «звезд» и которая в этом качестве на данный момент уже совершенно не нужна, причем мы объединим это действие с определением метрики используемого шрифта, воспользовавшись опять предоставляемой Java возможностью включать присваивание внутрь других выражений:

      iS = (mtrText = gFull.getFontMetrics()).getHeight();

Запомним также в переменной iY, которая ранее использовалась аналогично переменной iS координату Y самой верхней (а возможно и единственной) из тех строк, которые нам предстоит «написать» определив эту координату как половину разницы между высотой нашего баннера (hFull) и суммарной высотой всех строк, которые нам предстоит «написать» равной произведению высоты одной строки, ранее запомненной нами в переменной iS на их общее количество (T.length) которое мы к тому же запомним в переменной iZ, о которой можно сказать тоже, что и о переменных iS и iY. Учтем при этом также и необходимость «сдвига» координаты Y для текста к «базовой линии»:

      iY = ((hFull - (iS * (iZ = T.length))) / 2) + mtrText.getAscent();

«Запасясь» всеми необходимыми для дальнейшего значениями, которые можно было определить до начала цикла «написания» текста непосредственно к этому циклу мы теперь и перейдем. Цикл этот будет осуществляться по «целой» переменной i, которая будет «пробегать» у нас значения от нуля до общего числа строк в нашем тексте, которое мы запомнили в переменной iZ:

      for(int i = 0; i < iZ; i++) {

Первое что мы сделаем в теле нашего цикла – это перезапомним ту строку текста, которую мы будем в данный момент «писать» (T[i]) в переменной S:

        S = T[i];

Сам же оператор «написания» строки, поскольку вычисление координаты Y для «написания» очередной строки мы из него убрали, будет у нас выглядеть даже проще чем раньше:

        gFull.drawString(S, (wFull - mtrText.stringWidth(S)) / 2, iY);

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

        iY += iS;

после чего можно переходить к очередному выполнению цикла – «написанию» следующей строки:

      }

На этом завершаются изменения связанные с «написанием» текста приветствия в функции DrawFull и единственное что нам осталось выяснить: «А откуда, собственно говоря, само это приветствие возьмется?». Совершенно очевидно, что формирование этого текста и присваивание его массиву strRegards должно находился там же, где находилось присваивание последнему начального значения в те времена, когда он был еще просто переменной т.е. в функции init, однако прежде чем перейти к этой «операции» я хочу остановиться на следующем моменте: Имя, которое нам необходимо включить в приветствие как я уже говорил, скорее всего, хранится в Cookies, а стандартом Java доступ к нему непредусмотрен. Существует способ, который позволяет это сделать реализованный практически во всех современных браузерах и таким образом де-факто ставший стандартом (и когда-нибудь возможно я соберусь об этом написать) но я повторяю, в официальный стандарт Java не входящий, поэтому в нашем апплете мы воспользуемся способом полностью соответствующим стандарту Java и передадим ему необходимую информацию в виде параметра. Делается это из «вызывающей» апплет последовательности HTML-тэгов, а поскольку вопросы работы с HTML и JavaScript выходят за рамки этой статьи, я ограничусь тем, что приведу без каких-либо комментариев код, который можно было бы использовать для этого:

Так или иначе, но дальше мы будем исходить из того, что имя, которое должно быть включено в приветствие, передано нашему апплету через параметр с именем «name» (тэг «<param» в приведенном выше коде) и перейдем к его использованию в нашей функции init, однако сначала добавим в ее начало описание двух переменных – «целой» H и «Строковой» S, которые будут локальными для нее:

    int H;

    String  S;

Ну а теперь заменим то простое присваивание текста приглашения переменной strRegards, которое было у нас раньше на некоторую последовательность операторов, в первом из которых мы при помощи стандартной функции getParameter получим переданный нашему апплету параметр, запомним полученную строку, потенциально содержащую имя, которое должно быть включено в приветствие во вновь введенной нами строковой переменной S, проверим, а был ли такой параметр нам действительно передан т.е. не равно ли полученное значение «нулю» (null) и в том случае если этот параметр действительно был нам передан, содержит ли он хоть какие-нибудь символы т.е. не равен ли он пустой строке (при этом необходимо отметить что в Java обычная операция сравнения «==» для строк выдает результат «истина» только в том случае если оба ее операнда «физически» являются одной и той же строкой, если же необходимо сравнить строки «по содержимому» то для этого нужно использовать функцию equals):

    if(((S = getParameter("name")) == null) || S.equals(""))

Если хотя бы одно из вышеприведенных условий имело место то, значит, приветствовать нам некого, и мы вернемся нашему прежнему приглашению нажать на наш баннер, присвоив массиву strRegards вновь созданный массив из одной строки, значением которой будет уже знакомый нам текст:

      (strRegards = new String[1])[0] = "Жми!";

Ну а в противном случае т.е. когда нам было передано непустое имя для приветствия:

    else {

мы сделаем массив strRegards состоящим уже из двух строк, причем в последней из них мы запомним переданное имя, дополненное восклицательным знаком:

      (strRegards = new String[2])[1] = S + "!";

а в его первую строку мы поместим собственно приветствие, для чего создадим новый объект класса «Дата» соответствующий текущей дате (new Date()) (кстати, для этого нам нужно будет добавить в начало нашего класса в число импортируемых пакетов еще и пакет java.util содержащий в частности описание класса «Дата»), при помощи его стандартной функции getHours() извлечем из него значение текущего часа, запомним это значение во введенной нами «целой» переменной H и проанализируем его, полагая первую строку приветствия, если значение текущего часа находится в пределах от 5 до 11 тексту «Доброе утро», если значение текущего часа находится в пределах от 11 до 17 тексту «Добрый день», если значение текущего часа находится в пределах от 17 до 24 тексту «Добрый вечер» и тексту «Доброй ночи» во всех остальных случаях т.е. для значений от 0 до 5:

      strRegards[0] = ((H = new Date().getHours()) > 17 ? "Добрый вечер" : (H > 11 ? "Добрый день" : (H > 5 ? "Доброе утро" : "Доброй ночи")));

и таким образом завершим формирование нашего приветствия:

    }

а вместе с ним и изменения в функции init, да и вообще всю работу над нашим баннером который теперь стал еще «интеллектуальнее».

Благодарю за внимание. Может быть, когда-нибудь у меня дойдут руки написать еще о чем-нибудь. :)

Ну а если кто-либо захочет посмотреть этот апплет в действии, скачать его текст или текст этой статьи – милости прошу: http://mywebdesign.fatal.ru/Articles.

1 Звезда2 Звезды3 Звезды4 Звезды5 Звезд (Пока оценок нет)
Загрузка...

Дата публикации:
Автор публикации: