RTFM! статьи, советы, скрипты
::Заработок с помощью сайта ::JScript по-русски! ::Все русские мануалы::
МЕНЮ
О САЙТЕ
НОВОСТИ
СТАТЬИ
КНИГИ
АППЛЕТЫ
СВЯЗЬ

СЧЕТЧИКИ
Rambler's Top100
Рейтинг@Mail.ru
Яндекс цитирования

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

Автор: Alex Сайт: http://www.izcity.com/     Рейтинг: N/A из 7       <<НАЗАД
   
   
   Необходимо отметить что, вообще говоря, каких-либо логических ограничений на число потоков, на которые будет разделена та или иная программа, сама Java не содержит, физическим же ограничением в этом вопросе служит только мощность компьютера, на котором эта программа будет выполняться. На деле же в подавляющем большинстве случаев в частности для алгоритмов, осуществляющих разного рода визуальные эффекты оказывается достаточным всего двух потоков: Один основной, который существует вообще в любой, даже и в не потоковой программе и «занимается» разного рода «традиционными вещами» типа ввода данных, прорисовки картинок, «реагирования» на «действия» мыши и т.п. и второй, «в обязанности» которого входит как раз динамическое изменение картинки для получения желаемых эффектов.
   
   Однако в Java недостаточно только описать, что наш класс будет потоковым – нужно еще и необходимое нам число дополнительных потоков создать. Для нас как уже было отмечено, достаточно одного дополнительного потока и создать (породить) его удобнее всего в стандартно вызываемой Java-системой при каждом запуске нашего апплета функции start, заголовок которой будет выглядеть следующим образом:
   
    public void start()
   
    {
   
   Собственно говоря, все компоненты этого описания нам известны еще по функции paint, поскольку функция start также как и она «публичная» (public) и «пустая» (void). Единственное различие между этими функциями состоит в том, что функция start не имеет ни одного параметра и в этой связи у нас еще меньше возможностей варьировать ее описание (точнее говоря – их совсем нет) чем для функции paint, поскольку функция start как уже было отмечено, относится к стандартно вызываемым Java-системой.
   
   Как мы уже говорили единственное, что нам нужно сделать в этой функции это создать (породить) параллельный основному поток, который у нас будет отвечать за динамическое изменение картинки на нашем баннере, но поскольку функция start вызывается Java-системой при каждом запуске нашего апплета сначала необходимо проверить, не было ли уже это создание (порождение) осуществлено, для чего мы проверим значение переменной trdPict имеющей тип «Поток» (Thread) описанной среди других в начале нашего апплета:
   
    if(trdPict == null)
   
   и если создание (порождение) не было осуществлено (значение переменной trdPict равно «нулю» (null)) создадим новый экземпляр класса «Поток» (Thread) указав ему что, интерфейс Runnable для него будет осуществлять наш класс MyBanner (this), запомним этот новый поток в переменной trdPict и запустим его в работу, вызвав его стандартную функцию start():
   
    (trdPict = new Thread(this)).start();
   
   И это все что нам было нужно сделать в нашей функции start.
   
    }
   
   Ну а теперь, когда второй наш поток начал выполняться самое время задаться вопросом: «А что же, собственно говоря, он будет делать?».
   
   Ответить на этот вопрос очень несложно, если вспомнить что при создании нового экземпляра класса Thread мы указали, что интерфейс Runnable для него будет осуществлять наш класс и учесть что все что для этого нужно – это наличие в нем функции run, которая вызывается Java-системой тогда когда поток, для которого в качестве реализующего этот интерфейс был указан соответствующий класс (в данном случае – наш класс), начинает выполняться и которая, находясь текстуально внутри описания нашего класса, на самом деле выполняется уже в другом потоке.
   
   Вот к описанию этой функции мы сейчас и перейдем. Ее заголовок выглядит следующим образом:
   
    public void run()
   
    {
   
   Останавливаться на нем подробно нет никакой необходимости, поскольку он за исключением собственно имени функции полностью совпадает с заголовком функции start, а об этом заголовке уже все было сказано. Поэтому сразу перейдем к ее телу, которое из соображений уже упоминавшегося хорошего стиля программирования на Java начнем с описания двух переменных, которые нужны, будут нам в дальнейшем:
   
    Font newFont;
   
    FontMetrics newMetr;
   
   Что же касается выполняемых операторов тела этой функции то необходимо отметить, что Java-системой она вызывается только один раз при запуске созданного нами потока, и если мы хотим использовать этот поток для постоянной модификации нашего изображения, то мы должны делать это в цикле с некоторой задержкой по времени. При этом, поскольку мы хотим осуществлять такую модификацию все время пока «работает» наш баннер никаких критериев выхода из этого цикла у нас нет т.е. в некотором смысле он является бесконечным, и мы пишем:
   
    for( ; ; ) {
   
   Первое что мы сделаем, прежде чем модифицировать изображение на баннере – это проверим, была ли уже создана наша «большая картинка» поскольку пока она не создана, никакое изображение на баннере прорисовываться не будет а, следовательно, и модифицировать это изображение совершенно бессмысленно:
   
    if(imgFull != null) {
   
   Если выяснилось что наша «большая картинка» уже создана, то мы перво-наперво изменяем на некоторую величину dx некоторую переменную rot, на «физическом смысле» которой мы остановимся несколько позднее, когда будем рассматривать собственно прорисовку изображения для нашего баннера, и которая как раз изменения, происходящие с нашим «звездным небом» т.е. «полет звезд» навстречу зрителю и определяет:
   
    rot += dx;
   
   Затем мы саму эту величину dx мы в свою очередь изменим на другую величину ddx, которую если условно интерпретировать величину dx как некоторого рода «скорость» изменения нашей картинки можно также условно считать ее «ускорением» и проверим, не стала ли наша «скорость» слишком большой т.е. не превысило ли значение dx некоторого предела max:
   
    if((dx += ddx) > max)
   
   и если это произошло, установим наше «ускорение» ddx равным некоторой фиксированной отрицательной величине -defddx с тем чтобы, начиная со следующего цикла изменения картинки наша «скорость» не возрастала, а наоборот убывала:
   
    ddx = -defddx;
   
   Аналогичную проверку мы сделаем и на предмет того, не стала ли наша «скорость» dx слишком большой отрицательно величиной, что в терминах физических аналогий видимо можно рассматривать, как проверку того, не стала ли наша картинка слишком сильно «тормозить»:
   
    if(dx < -max)
   
   и если это произошло, то установим наше «ускорение» ddx равным некоторой фиксированной величине defddx с тем чтобы, начиная со следующего цикла изменения картинки наша «скорость» не убывала, а наоборот возрастала:
   
    ddx = defddx;
   
   На этом собственно наша работа по модификации «звездного неба» полностью завершена не считая уже упомянутой необходимости вернуться к нашей переменной rot, но прежде чем пойти дальше остановимся на следующих общих четах использованных нами на этом этапе величин, а именно: Все они были описаны еще в начале нашего класса, причем dx, ddx и rot были описаны как переменные, а defddx и max – как постоянные. Все они имеют тип «двойной» (double) т.е. их значения являются дробными числами с плавающей точкой с удвоенной относительно обычного точностью.
   
   Однако то, что мы уже сделали еще далеко не вся наша работа по модификации изображения нашего баннера, поскольку как Вы, наверное, еще не забыли, мы начали «городить весь этот огород» с рисунком звездного неба не как самоцель, а ради того чтобы поведать человечеству о Ваших четвероногих, двукрылых, хвостатых и не имеющих ни того, ни другого, ни третьего питомцах, а для этого нам очевидным образом в изображение нашего баннера необходимо включить еще и текст, который также как и наши «звезды» будет «лететь» на зрителя.
   
   Сделаем мы это путем изменения размеров шрифта, которым наш текст будет «написан» в нашем баннере, но для того чтобы «полет» наших букв не был слишком быстрым, введем некоторую переменную nFactor (кстати, также описанную в начале нашего класса и имеющую тип «целый») которая будет показывать, за сколько циклов изменения наших «звезд» (а периодичность циклов изменения картинки баннера выбирается как раз исходя из скорости их «движения») будет происходить изменение размеров шрифта. В каждом цикле мы будем уменьшать ее значение на единицу, и если в очередной раз оно станет неположительным, то это будет свидетельствовать о том, что настала пора изменить размеры шрифта или даже сам выводимый текст:
   
    if(--nFactor <= 0) {
   
   Следующая проверка, которую мы сделаем, покажет нам, не достигли ли мы конца перечня всех обитателей Вашего «живого уголка». Об этом будет свидетельствовать то, что, при очередном повторении нашего цикла начав с начала (с 0) перебор размеров шрифта (переменная nFont описанная в начале нашего класса и имеющая тип «целый») мы увидим, что номер очередного выводимого нами текста (переменная iText также описанная в начале нашего класса и также имеющая тип «целый») превосходит общее количество этих текстов имеющихся у нас в наличии (константа nTexts описанная в начале нашего класса имеющая тип «целый» и значение равное количеству элементов (length) в массиве strTexts в свою очередь также описанном в начале нашего класса и имеющем тип «Строка» (String)). И здесь, на мой взгляд, необходимо отметить вот какой момент: Алгоритм нашего баннера устроен таким образом, что для смены выводимых в нем текстов достаточно просто заменить их в описании массива strTexts при этом он всегда будет работать правильно, не требуя в связи с этим изменением каких-либо дополнительных «настроек». Более того – это относится не только к содержанию этих текстов, но и к их количеству: Наш баннер будет правильно работать при количестве этих текстов от одного до любого разумного ограниченного только возможностями компьютера, на котором его будут просматривать, однако здесь не увлекайтесь – если Вы перепишете в этот массив всю книгу Брема, то навряд ли кто-либо из зрителей дождется конца этого цикла – по-видимому, число 2 – 4 текста можно считать оптимальным.
   
   Итак:
   
    if((nFont == 0) && (++iText >= nTexts))
   
   и в том случае если это произошло т.е. мы достигли конца нашего массива текстов, мы опять начнем его перебор с начала:
   
    iText = 0;
   
   Ну а теперь пришла пора создать наш очередной шрифт и проверить впишется ли в наш баннер выбранный на данном этапе текст, будучи «написан» этим шрифтом для чего мы опять воспользуемся возможностью включать внутрь выражений оператор присваивания:
   
    if(((newMetr = getFontMetrics(newFont = new Font(strFont, Font.BOLD, ++nFont))).getHeight() > hFull) || (newMetr.stringWidth(strTexts[iText]) > wFull)) {
   
   Этот оператор делает следующее:
   
   § Создает новый шрифт (new Font) с именем определенным в начале нашего класса константой strFont (был выбран шрифт «Arial» как один из стандартных, поскольку такой шрифт должен быть обязательно установлен на компьютере, на котором просматривается баннер и если выбрать уж очень экзотический шрифт, то зритель просто ничего не увидит), стилем жирных букв (стандартная константа класса Font – BOLD) и размером увеличенной на единицу переменной nFont и запоминаем созданный шрифт в описанной в начале функции run переменной newFont имеющей тип «Шрифт» (Font);
   
   § При помощи стандартной функции класса «Шрифт» getFontMetrics определяем метрику созданного шрифта и запоминаем ее в описанной в начале функции run переменной newMetr имеющей тип «Метрика Шрифта» (FontMetrics);
   
   § При помощи стандартной функции класса «Метрика Шрифта» getHeight() определяем высоту созданного шрифта и проверяем, не превосходит ли она высоты (hFull) нашего баннера т.е. будет ли вписываться в него по высоте текст «написанный» этим шрифтом;
   
   § Если по высоте созданный нами шрифт вписывается в пределы баннера, то при помощи стандартной функции класса «Метрика Шрифта» stringWidth определяем ширину выбранного на данном этапе текста «написанного» этим шрифтом и проверяем, не превосходит ли она ширины (wFull) нашего баннера т.е. будет ли вписываться в него по ширине наш текст «написанный» этим шрифтом.
   
   Если хотя бы одно из этих условий имело место т.е. если высота созданного шрифта оказалась больше высоты баннера или если ширина выбранного на данном этапе текст «написанного» этим шрифтом оказалась больше ширины баннера это означает что с данным текстом мы «дошли до точки» т.е. дальше его увеличивать некуда пора на какое-то время остановится, а затем перейти к следующему тексту что мы и сделаем, установив размер шрифта, в нуль, показывая тем самым, что на следующем этапе его изменения необходимо предварительно перейти к следующему тексту:
   
    nFont = 0;
   
   а переменную nFactor, которая как мы уже говорили, показывает, через сколько циклов произойдет очередное изменение шрифта - равной константе nStopFactor которая была определена как «целая» в начале нашего класса:
   
    nFactor = nStopFactor;
   
   Если же ни одно из указанных выше условий не имеет места т.е. если выбранный на данном этапе текст «написанный» созданным нами шрифтом полностью вписывается в баннер:
   
    } else {
   
   перезапомним этот шрифт в переменной fntTexts описанной в начале нашего класса и имеющей тип «Шрифт» поскольку он нам потребуется при прорисовке нашей картинки:
   
    fntTexts = newFont;
   
   а переменную nFactor установим равной константе nDepartFactor, которая была определена как «целая» в начале нашего класса:
   
    nFactor = nDepartFactor;
   
   На этом все наши работы по модификации картинки завершены:
   
    }
   
    }
   
   и нам остается только прорисовать ее, воспользовавшись уже упоминавшейся функцией DrawFull:
   
    DrawFull();
   
   а тем самым завершить и всю нашу «полезную деятельность» внутри цикла, которой как Вы, конечно же, помните, мы занимались только после проверки, была ли уже создана «большая картинка» нашего баннера:
   
    }
   
   Ну а поскольку наша «полезная деятельность» завершилась, нам осталось только заняться «деятельностью бесполезной» т.е. подождать, когда придет время вновь (или в первый раз) заняться «деятельностью полезной» что можно сделать при помощи следующей конструкции:
   
    try {
   
    Thread.sleep(nDelay);
   
    } catch(InterruptedException e) {
   
    }
   
   Здесь мы при помощи стандартной функции класса «Поток» sleep «говорим» что наш поток, модифицирующий картинку баннера должен «заснуть» (sleep) (а что может быть бесполезней?) на время равное числу миллисекунд определяемое константой nDelay описанной в начале класса баннера как «целая». «Обрамление» же try-catch указывает на то, что если процесс «сна» будет прерван (InterruptedException) то ненужно делать ничего.
   
   И на этом очередной наш цикл модификации картинки баннера завершен и можно переходить к следующему:
   
    }
   
   А также завершена и вся функция run запуска потока модификации картинки:
   
    }
   
   Ну вот теперь, когда наша картинка модифицируется и прорисовывается у нас «дошли руки» до разбора уже два раза упоминавшейся функции собственно эту прорисовку и осуществляющей - DrawFull, но прежде чем рассмотреть ее «по существу» необходимо остановится на еще одной интересной особенности Java. Вы, скорее всего, обратили внимание, что между двумя упоминаниями функции DrawFull существует некое различие, которое состоит в том, что в первом случае (в функции paint) мы передавали ей в качестве параметра графический контекст, который в свою очередь передавала нашей функции paint Java-система, а во втором случае (в функции run) мы никаких параметров функции DrawFull вообще не передавали и в этом и заключается та интересная особенность Java, о которой я хотел сказать. Дело в том, что в Java можно создавать функции с одним и тем же именем, но с различным набором параметров и Java сама будет выбирать, которую же из них использовать в том или ином случае именно в зависимости от того, с какими параметрами в этом конкретном случае данная функция вызывается. При этом сама Java не накладывает каких-либо ограничений на то, как эти функции будут связаны между собой с содержательной точки зрения, и вообще говоря, это могут быть даже совершенно различные ничего общего между собой не имеющие функции, хотя конечно логичнее назвать одним и тем же именем функции как-либо связанные между собой, что мы и сделали с нашей функцией DrawFull и поэтому начнем ее рассмотрение с того ее варианта, который не имеет параметров.
   
   Ее заголовок в данном случае имеет вид:
   
    private void DrawFull()
   
    {
   
   Поскольку эта функция только «наша личная» нужная только внутри нашего класса MyBanner мы описали ее как «приватную» (private) т.е. доступ к ней возможен только внутри нашего класса, к тому же она не вырабатывает никакого значения (void) и, конечно же, не имеет параметров.
   
   Что же касается ее тела то оно крайне просто и состоит всего из одного оператора:
   
    DrawFull(getGraphics());
   
   т.е. сводится к вызову того варианта DrawFull, который имеет своим параметром графический контекст в качестве, какового ей передается графический контекст нашего апплета полученный путем вызова стандартной функции getGraphics() т.е. в нашем случае функция DrawFull без параметров является фактически просто более удобной формой записи этого оператора позволяющей сократить текст нашего апплета и сделать его более понятным, а на деле все вызовы этого варианта функции DrawFull (а она нам еще понадобится) могли бы быть заменены на приведенный вызов варианта функции DrawFull с параметром.
   
   Таким образом наша функция DrawFull без параметров завершена
   
    }
   
   и настала пора заняться функцией DrawFull с параметром, заголовок которой будем выглядеть следующим образом:
   
    private void DrawFull(Graphics g)
   
    {
   
   и я думаю, что какие-либо комментарии здесь излишни, поэтому сразу перейдем к описанию локальных для функции DrawFull переменных:
   
    int iX, iY, iZ, iS;
   
    String S;
   
    FontMetrics mtrText;
   
   и здесь я тоже обойдусь без комментариев, поскольку со всеми использованными здесь типами мы уже встречались, а смысл этих переменных я объясню в момент их использования, как это было и в предыдущих случаях.
   
   Первое что мы сделаем с нашей «большой картинкой» а как мы уже говорили она фактически и является изображением нашего баннера - «нарисуем» на ней «небо» черного цвета, что делается путем всего двух операторов. Ее графический контекст как Вы помните храниться у нас в переменной gFull, и при помощи стандартной этого для этого класса функции setColor мы сначала установим для нее цвет, определяемый стандартной для класса «Цвет» (Color) константой «черный» (black):
   
    gFull.setColor(Color.black);
   
   а затем при помощи также стандартной для класса «Графический контекст» функции fillRect «зальем» всю ее (от 0 до wFull по ширине и от 0 до hFull по высоте) этим цветом:
   
    gFull.fillRect(0, 0, wFull, hFull);
   
   Теперь, когда мы «обзавелись» собственным «небом» украсим его нашими «звездами» прорисовав их в цикле по целой переменной i, которая будет пробегать у нас значение от 0 до nStars, аналогично тому, как это было сделано при задании начального расположения наших «звезд» в функции paint:
   
    for(int i = 0; i < nStars; i++) {
   
   И здесь начинается самое страшное: Как я уже говорил еще в начале этой статьи, практически все алгоритмы создающие красивые визуальные эффекты крайне громоздки и нелогичны и если использованный для нашего баннера алгоритм все-таки достаточно прост и компактен (за что и был выбран) то с точки зрения логичности только ненамного лучше своих «коллег» и смысл многих его операторов может быть объяснен только единственным образом: «А шоб красивше было...». Вот к этой-то категории и относятся четыре следующих оператора, поэтому я «по ходу дела» прокомментирую их только формально.
   
   Перезапомним Z координату для очередной «звезды» (zStar[i]) уменьшенную на два в описанной в начале функции DrawFull локальной для нее «целой» переменной iZ и проверим, не оказалось ли это значение меньше -63-х:
   
    if((iZ = zStar[i] - 2) < -63)
   
   Если таковое имело место то «удалим» нашу «звезду» на максимальное «расстояние» равное 100-а единицам:
   
    iZ = 100;
   
   Теперь пересчитаем обратно пропорционально «удалению» нашей «звезды» ее координату X (xStar[i]) запомнив полученное значение в описанной в начале функции DrawFull локальной для нее «целой» переменной iS:
   
    iS = (xStar[i] * 64) / (iZ + 64);
   
   и координату Y (yStar[i]) запомнив полученное значение в описанной в начале функции DrawFull локальной для нее «целой» переменной iY:
   
    iY = (yStar[i] * 64) / (iZ + 64);
   
   Ну вот самое страшное позади, поскольку остальные используемые нами для прорисовки «звезды» формулы несколько более логичны а, следовательно, и более понятны. Это относится в частности к двум следующим операторам, которые обязательно нужно рассматривать только вместе, поскольку именно так они описывают поворот системы координат для нашей «звезды» на угол rot, а ведь именно эту переменную мы использовали в функции run для «динамизации» нашей картинки т.е. наши звезды как бы «ходят по кругу» однако в силу того, что они расположены случайным образом и в результате действия еще некоторых операторов, которые мы также сейчас рассмотрим, создается полное впечатление их «полета» навстречу зрителю. Для описания этого преобразования мы воспользуемся стандартными функциями sin и cos класса «Математика», которые являются ни чем иным как обычными синусом и косинусом. Наряду с поворотом мы еще и сдвинем нашу «звезду» по направлению к центру апплета на величину wHalf по ширине – координате X и на величину hHalf по высоте – координате Y с тем, чтобы наши «звезды» «летели» навстречу зрителю именно оттуда, а не из начала координат, каковым в Java является левый верхний угол апплета:
   
    iX = (int)((double)iS * Math.cos(rot) - (double)iY * Math.sin(rot)) + wHalf;
   
    iY = (int)((double)iS * Math.sin(rot) + (double)iY * Math.cos(rot)) + hHalf;
   
   Проверим теперь не «убежала» ли наша звезда за пределы нашего баннера т.е. не оказались ли ее координаты меньше нуля или больше ширины (wFull) – для координаты X или высоты (hFull) – для координаты Y баннера:
   
    if((iX < 0) || (iX > wFull) || (iY < 0) || (iY > hFull))
   
   и если это произошло «удалим» ее на максимальное «расстояние» по координате Z:
   
    iZ = 100;
   
   И сейчас как раз с этой самой координатой Z мы и поработаем, для чего сначала установим цвет нашей «звезды» в зависимости от ее «расстояния» от зрителя по этой оси, что совместно с уже произведенным нами поворотом ее координат как раз и будет создавать впечатление «полета» «звезды» навстречу зрителю. «Звезда» находящаяся на половине и дальше максимального «расстояния» от зрителя по оси Z будет окрашена у нас в серый цвет, для чего мы воспользуемся стандартной константой класса «Цвет» «серый» (gray), «звезда» находящаяся от зрителя по оси Z на «расстоянии» от четверти до половины максимального будет окрашена у нас в светло-серый цвет - стандартная константа lightGray класса «Цвет» ну а «звезда» находящаяся от зрителя по оси Z на «расстоянии» менее четверти максимального будет у нас белой - стандартная константа white того же класса. Установим соответствующий цвет при помощи уже использовавшейся нами функции setColor:
   
    gFull.setColor((iZ > 50 ? Color.gray : (iZ > 25 ? Color.lightGray : Color.white)));
   
   И последнее что нам осталось сделать, до того как прорисовать «звезду» - определить ее размер, который, конечно же, также будут зависеть от ее «удаления» от зрителя и будет очевидным образом тем меньше чем она «дальше» (закон перспективы!). Определим мы этот размер очень простым образом - вычтя координату Z «звезды» из максимального «расстояния» а заодно еще и перезапомнив ее в массиве координат Z всех «звезд» (zStar[i]) и разделив полученное значение на 50 т.е. максимально «близкая» к зрителю «звезда», «расстояние» которой от зрителя равно 0 будет иметь размер 2. Параллельно мы еще и проверим, не оказался ли размер «звезды» нулевым:
   
    if((iS = (100 - (zStar[i] = iZ)) / 50) == 0)
   
   и если это произошло, положим этот размер минимальным видимым – единице:
   
    iS = 1;
   
   Ну а теперь прорисуем нашу «звезду» просто «залив» при помощи уже использовавшейся нами функции fillRect отведенную «звезде» область выбранным для нее цветом. Да-да - наши «звезды» будет прямоугольными но, даже теперь зная этот «секрет» Вы никогда этого не заметите, поскольку такова сила человеческого воображения – ведь в реале Вы никогда не видели квадратных звезд! Так и обитатели Диптауна ухитрялись набираться в глубине:
   
    gFull.fillRect(iX, iY, iS, iS);
   
   На этом прорисовка очередной «звезды» нами завершена и можно перейти к очередному повторению цикла «по звездам» и прорисовке следующей из них:
   
    }
   
   а по завершению всех этих циклов - завершению прорисовки всех «звезд» наступает пора заняться «написанием» выбранного на данный момент текста про собак, кошек, птичек и т.д. и т.п. «Писать» этот текст мы будем, конечно же, тем шрифтом, который мы создали для этого в функции run и поэтому перво-наперво проверим – а был ли он вообще создан:
   
    if(fntTexts != null) {
   
   и если это действительно имело место, то при помощи уже хорошо нам знакомой функции setColor установим цвет, которым будет «написан» наш текст – желтый цвет, задаваемый стандартной константой класса «Цвет» yellow:
   
    gFull.setColor(Color.yellow);
   
   Теперь при помощи также стандартной для класса «Графический контекст» функции setFont установим наш шрифт fntTexts в качестве шрифта, которым будет осуществляться процесс «письма» в нашей «большой картинке»:
   
    gFull.setFont(fntTexts);
   
   выберем из массива всех наших текстов strTexts тот текст, который мы будем «писать» в данный момент и запомним его в описанной в начале функции DrawFull локальной для нее переменной S имеющей тип «Строка»:
   
    S = strTexts[iText];
   
   и, наконец, при помощи еще одной стандартной для класса «Графический контекст» функции getFontMetrics() определим метрику для установленного для него в данный момент шрифта (каковым в нашем случае является наш шрифт fntTexts) и запомним ее в описанной в начале функции DrawFull локальной для нее переменной mtrText имеющей тип «Метрика Шрифта»:
   
    mtrText = gFull.getFontMetrics();
   
   И теперь у нас все готово, для того чтобы «написать» наш текст на нашем баннере, и мы воспользуемся для этого очередной стандартной для класса «Графический контекст» функцией drawString, которая в качестве своего первого параметра должна получить ту строку (текст), которую мы хотим «написать», а в качестве второго и третьего параметров – координаты этой строки X и Y соответственно. Мы будем «писать» наш текст посредине баннера, поэтому его координата по оси X с очевидностью будет равна половине разности между шириной баннера (wFull) и шириной нашего текста определенной при помощи уже известной нам стандартной функции класса «Метрика Шрифта» stringWidth. А вот с координатой Y для нашего текста все обстоит чуть сложнее: Мы, конечно же, также определим ее как половину разности между высотой баннера (hFull) и высотой нашего шрифта определенной при помощи опять-таки уже известной нам стандартной функции класса «Метрика Шрифта» getHeight() однако в Java «началом координат» по оси Y для текста служит не его верхний угол, как это имеет место для картинок, а некоторая так называемая «базовая линия», которая располагается почти в самом низу текста – ниже нее опускаются только разного рода «хвостики» букв таковые имеющих. Получить расстояние от верхней кромки текста до этой «базовой линии» можно при помощи еще одной стандартной функции «Метрика Шрифта» - getAscent() и именно на эту величину нам будет нужно увеличить ранее полученную координату Y нашего текста с тем, чтобы он попал именно по центру баннера.
   
   Таким образом:
   
    gFull.drawString(S, (wFull - mtrText.stringWidth(S)) / 2, ((hFull - mtrText.getHeight()) / 2) + mtrText.getAscent());
   
   и «написание» нашего текста также завершено:
   
    }
   
   после чего единственное что нам осталось – это прорисовать созданную нами «большую картинку» в переданном функции DrawFull в качестве параметра графическом контексте и для этого мы используем еще одну стандартную функцию этого класса drawImage, первым параметром которой является картинка, которую необходимо прорисовать (в нашем случае – наша «большая картинка») а второй и третий параметры указывают ее смещения по ширине и высоте, каковые в нашем случае очевидным образом равны нулю:
   
    g.drawImage(imgFull, 0, 0, this);
   
   и теперь можно и полностью завершить нашу функцию DrawFull
   
    }
   
   
   
   


<<НАЗАД      ОЦЕНИТЬ СТАТЬЮ    ВЕРСИЯ ДЛЯ ПЕЧАТИ>>
Статья прочитана :  раз.




пейкюлю



 
 
 
 
 
pauk ©® 2000-2015. All rights reserved.
При перепечатке ссылка на сайт и указание обязательны.
Мнение администрации сайта не всегда совпадает с мнением автора.
Орфография и пунктуация - авторские.
Администрация не несет никакой ответственности за использование материалов.
.
Protected by Copyscape DMCA Takedown Notice Infringement Search Tool