25 августа 2019 года    
Воскресенье | 20:22    
Главная
 Новости
Базы данных
Безопасность PC
Всё о компьютерах
Графика и дизайн
Интернет-технологии
Мобильные устройства
Операционные системы
Программирование
Программы
Связь
Сети
 Документация
Статьи
Самоучители
 Общение
Форум







Разделы / Программирование / Java

Frequently Asked Questions (with answers) for Java programmers.

Секция 5 из 7 - Предыдущая - Следующая
Все секции - 1 - 2 - 3 - 4 - 5 - 6 - 7

новая замена. Также годится метод Calendar.set( year, month, day ).
Заметьте, что год в GregorianCalendar начинается с 1 A.D., а не с 1901, как
в старом конструкторе класса Date.


                             java.util.TimeZone

*(Часть 9) Как я могу проверить, правильную ли временную зону использует
моя JVM?

[*] Следующий код выводит на экран ID текущей временной зоны по умолчанию.

  System.out.println( TimeZone.getDefault().getID() );

*(Часть 9) Значение TimeZone.getDefault не такое, как я ожидал(а). В чем
проблема?

[*] Значение временной зоны по умолчанию базируется на значении системного
свойства "user.timezone". JVM предполагает установить это значение. В таких
релизах, как JDK 1.1 значение user.timezone часто бывает не установлено, и
таким образом TimeZone.getDefault() использует свое собственное встроенное
"неитрализирующее" ("fallback") значение (по умолсанию когда нет значения
по умолчанию). В дальнейших JDK 1.1 релизах и в JDK 1.2 установка значения
user.timezone гораздо лучше и значение "fallback" теперь GMT (Greenwich
Mean Time). Вплоть до JDK 1.1.3, значение "fallback" было "PST" (North
American Pacific Timezone).

*(Часть 9) Используют ли все стандартные объекты одно и то же Do all the
standard objects use the значение временной зоны по умолчанию?

[*] Нет, до JDK 1.2. В JDK 1.1, Date.toString() и Calendar используют
значение TimeZone.getDefault() которое часто может быть неопределенным
(смотрите предыдущий вопрос). В JDK 1.1, Calendar в SimpleDateFormat был
установлен в 1-ю временную зону (для US это PST).

System.out.println( "Date format TZ = " + TimeZone.getDefault().getID() );
sdf = DateFormat.getDateTimeInstance( DateFormat.LONG, DateFormat.LONG );
System.out.println( "Date format TZ = " + sdf.getTimeZone().getID() );
Calendar cal = Calendar.getInstance();
System.out.println( "Calendar TZ = " + cal.getTimeZone().getID() );

Когда запускается в системе JDK 1.1.6, НЕ в Североамериканском
Тихоокеанском времени и не во временной зое GMT, то результат будет :

Timezone default = GMT
Date format TZ = PST
Calendar TZ = GMT

Этот пример показывает 2 ошибки (bugs) значение user.timezone не
определено, и оно установлено в GMT (смотрите обсуждение
TimeZone.getDefault()) а так же показано, что DateFormat зависит от 1-го
значения, которым является в данном случае PST.

Если Вы не хотите использовать DateFormat в качестве Местной временной зоны,
смотрите код представленный ниже.

*(Секция 15) Почему < Windows RMI/мой java debugger/IDE/другое> зависает
на несколько минут если мой Windows PC не подключен к Интернету?

[*] Java имеет встроеную сетевую поддержку. Когда Java программа стартует,
автоматически загружается Winsock DLL. Первое, что она пытается выполнить,
это определить (resolve) полностью квалифицированное имя домена для машины
"localhost". Если в Вашей системе не описано это имя, она будет пытаться
запросить его у сервера имен Интернета, который обычно находится у Вашего
провайдера Интернет (ISP). Поэтому система либо запросит Вас подключиться к
провайдеру Интернет, либо будет ждать пока не истечет время попытки.

Есть мнение, что можно избежать этой проблемы в Win95 дав определив в
системе другой способ определять DNS имена. У меня это не работает.
Отредактируйте файл hosts Вашей системы так, чтобы localhost и полное имя
домена были оба определены. В Windows 95 файл hosts находитися в
%windir%\HOSTS (например, C:\WINDOWS\HOSTS). В Windows NT файл hosts
находится в: %windir%\System32\DRIVERS\ETC\HOSTS например,
C:\WINNT\System32\DRIVERS\ETC\HOSTS).
В Win95 есть ловушка, которая заключается в том, что если последняя запись
не заканчивается символами carriage-return/line-feed, то файл hosts
читается не до конца. Так, если система называется goober.best.com
приведите файл hosts от вида


        127.0.0.1 localhost


к виду


        127.0.0.1 goober.best.com localhost


Файл может выглядеть следующим образом:

     # Hosts file
     127.0.0.1       localhost
     129.146.77.177  goober


Другой способ состоит в том, чтобы установить PPP соединение с провайдером
Интернет всякий раз, когда Вы будете запускать сетевые программы.

Опыт некоторых людей говорит о том, что работа с сетью не совсем
удовлетворительна под Windows95 с использованием Winsock 1.1, и время от
времени необъяснимо глючит. Вы можете попытаться загрузить Winsock 2.0.
Чтобы получить Winsock 2.0, Вы должны вытащить его из набора Microsoft
Windows Sockets 2.0 Software Development Kit. Этот софт может быть получен
со следующих адресов:
http://www.microsoft.com/win32dev/netwrk/winsock2/ws295sdk.html или:
ftp://ftp.microsoft.com/bussys/WinSock/winsock2/

Патчи, которые нужны для улучщения работы в сети уже есть в Win98.

                           Другие Сетевые Вопросы

*(Секция 15) Если я вызываю InetAddress.getByName() метод с аргументом IP
- адресом в виде строки, типа "192.168.0.1", получаю на некоторых (но не на
всех) платформах UnknownHostException. Код :

Socket sock = new Socket("155.152.5.1", 23);

бросает исключение. Почему?

[*] Это платформенное различие, которое возникает из различной семантики
лежащих в основе сетевых библиотек, и пофиксено в JDK 1.1. В Solaris и
Windows NT, строка IP адреса работает только для тех адресов, которые имеют
ассоциированное имя хоста. На Linux и Windows 95 строки IP адреса
срабатывают всегда.
Когда создается экземпляр InetAddress с IP адресом в качестве параметра,
выполняется обратное DNS преобразование. Если IP адрес не ассоциирован с
правильным именем хоста, то создание экзепляра класса InetAddress потерпит
неудачу. Это является частью борьбы с DNS-spoofing (подмена DNS), и в JDK
1.1 работает потому что обратное преобразование не происходит пока имя
хоста не запрошено. Так в JDK 1.1,

        InetAddress in = InetAddress.getByName("155.152.5.1");


[Note: Эту информацию нужно подтвердить. Сетевые гуру?]
Другие сайты:

     У Microsoft есть несколько network-related патчей на их сайте
     http://www.microsoft.com/

*(Секция 15) Я хочу передать class файл всем тем желающим, кто использует
мой апплет. Есть идеи насчет того как?

[*] Ты должен использовать маленькую хитрость: разместить твой .class
файл(ы) в .zip архиве и использовать метод showDocument() от URL. Некто,
обращаясь к этому URL получит окно диалога, в котором он будет запрошен о
сохранении файла на его локальном диске.
Другие сайты:

     Вы можете увидеть это живьем и попробовать на себе на:
     http://www.best.com/~rmlynch/saveit.html

*(Секция 15) Как заставить URLConnection работать через proxy firewall?
Т.е. Как заставить Java приложение выполнять доступ к Web через прокси?

[*] Это обычно нужно для любого сетевого доступа к другому домену. Вы
должны задать runtime системе что Вы пытаетесь сделать используя аргументы
командной строки когда запускаете программу.

java -DproxySet=true -DproxyHost=SOMEHOST -DproxyPort=SOMENUM code.java

Отметтье, что proxyPort является опциональным и по умолчанию равен 80. Без
этого Вы увидите исключение типа java.net.UnknownHostException или
java.net.NoRouteToHostException

Установки прокси работают и для java.net.URLConnection, и для
java.net.Sockets.

Netscape'вская и IE'ая JVMs (виртуальные Java машины) (по крайней мере в
версиях 4.х+) принимают установки прокси для апплетов из конфигурации
прокси браузера. Вы можете также работать с прокси в проиложениях (не
апплетах) добавив следующие строки


    // установите чтобы использовать прокси
    System.getProperties().put("proxySet", "true");
    System.getProperties().put("proxyHost", "myproxy.server.name");
    System.getProperties().put("proxyPort", "80");


Но как мне узнать имя прокси сервера?
Этот код просто говорит Вам как можно получить URL соединение через прокси.
Но так как это Ваш прокси сервер, то предполагается, что Вы знаете его имя.
И не существует кода, который можно написать, который позволит произвольным
URL соединениям быть инициироваными извне firewall'а. Подумайте об этом!
Если бы это было так, то firewall не выполнял бы свою работу.

Также отметтье, что существуют соответствующие socksProxyPort и
socksProxyHost когда socks используются вместо прокси. Socks порт по
умолчанию - 1080.

*(Секция 15) Что означает "swizzle"(неточный синоним - взбить[коктейль]),
например "Swizzle an object?"

[*] Это означает сериализовать. "To swizzle an object" означает рекурсивно
сериализовать созданные объекты.

*(Секция 15) Я использую возможности ссериализации в 1.1 чтобы сохранить
некоторые объекты на диске. Я добавил новое поле в один из моих объектов,
который сериализуется и теперь десериализация моих старых объектов больше
не работает. Я получаю такое исключение :


java.io.InvalidClassException: MacroData; Local class not compatible


[*] Вам нужно добавить объявление типа


    static final long serialVersionUID = 4021215565287364875L;


в модифицированный класс. Актуальное значение этого длинного целого
предоставляется утилитой "serialver", которая есть в JDK. Любая отличная от
первой версия класса требует, чтобы эта статическая переменная была
определена в классе.

*(Секция 15) Мой код работающий с сокетами выглядит правильным, но не
работает!

[*] При использовании сокетов Вы обычно открываете оба входящий и исходящий
потоки. TCP соединение является полнодуплексным, но либо отсылающая, либо
принимающая стороны могут закрываться независимо. По умолчанию, когда
установлено что удаленная сторона закрыла соединение, локальная сторона
просто закрывает его. Проверьте, может быть это случается с Вами
добавлением соответствующей пары. Используйте tcpdump чтобы проверить это.

*(Секция 15) Как мне получить соответствие IP адреса и имени хоста?

[*] В Java 1.1 (ранние версии были глюкавыми) используйте:


    String host = InetAddress.getByName("211.10.2.119").getHostName();


*(Секция 15) Как мне встроить якорь (anchor) в URL? Просто поместить его
как часть строки в конструктор не работает.

[*] Что-то типа:


    URL url = new URL("http://www.my_domain.com/my_page.html");
    URL anchor = new URL(url, "#section2");
    this.getAppletContext().showDocument(anchor);

*(Часть 10) Я использую add(Component), чтобы добавить компоненты в
контейнер. Есть ли способ явного указания z-порядка этих компонент?

[*] В JDK 1.0 нет явного способа установить z-порядок компонент. Вы можете
попробовать экспериментально подобрать его с помощью используемого вами
броузера, или Вы можете использовать CardLayoutManager, чтобы
удостоверится, что необходимая вам панель спереди.

В JDK 1.1 z-порядок компонент ("z-порядок" означает порядок
"спереди-сзади", т.е. какое окно перед каким находится) управляется
посредством метода add(Component comp, int index). По умолчанию компоненты
добавляются от 0 до N. Метод класса Container paint отрисовывает его
видимые компоненты от N до 0.

*(Часть 10) Как я могу получить размеры и разрешение экрана?

[*] Используйте

java.awt.Toolkit.getDefaultToolkit().getScreenSize()

или

java.awt.Toolkit.getDefaultToolkit().getScreenResolution()

Разрешение экрана возвращается в точках-на-дюйм (dpi, dots-per-inch).

Загляните в класс Toolkit - там много полезных методов

Toolkit.getDefaultToolkit().getColorModel().getPixelSize()

возвращает цветовую модель в битах-на-пиксел (bpp, bits-per-pixel)

Math.pow(2, Toolkit.getDefaultToolkit().
    getColorModel().getPixelSize())

возвращает цветовую модель в виде количества цветов. Или используйте это:

1 << Toolkit.getDefaultToolkit().
    getColorModel().getPixelSize()

Степень 2 вычисляется как двоичный сдвиг 1.

*(Часть 10) Как я могу учесть размеры заголовка и рамки при отрисовке
Frame'а?

[*] Используйте метод MyFrame.getInsets(). Он возвращает объект
java.awt.Insets который имеет четыре целых поля: top, left, bottom, right,
которые дают количество пикселов для каждого из полей, откладываемых
сверху. Вы можете использовать эти значения, чтобы отрегулировать объект
Dimension, возвращаемый Сomponent.getSize().

Если Вы это делаете это в конструкторе, Вам необходимо удостоверится, что
Peer объект Frame'а уже создан. Иначе объект Insets, возвращаемый
getInsets(), будет состоять из нулей. Вызовите Frame.addNotify(), чтобы
спровоцировать создание Peer'а.

*(Часть 10) Как нужно изменять pазмеpы списка? Я объявил список как

List tlist = new List(10);

но стpоки в списке были длиной 80 символов и только 15 первых были
показаны. Мне не удалось изменить размер списка, чтобы показать его
содержимое, не прибегая к использованию полос прокрутки.

[*] Нельзя изменять размер списка в конструкторе, так что добавте следующие
строки в Applet (или где бы то ни было):

public void paint (Graphics g) {
    tlist.setSize(200,200);
}

Потом перед тем как показать Panel/Frame со списком:

tlist.resize(400,400);

*(Часть 10) Как мне узнать, когда произошло изменение размеров окна?

[*] Перегрузите метод Component.setBounds(int, int, int, int), что бы
делать то что Вам необходимо. Конечно, Вам так же нужно вызывать
super.setBounds(). Необходимо заметить, что setBounds() заменил устаревший
(deprecated) метод reshape().

Заметьте, что новое API вызывает не что иное как устаревшее API. Например
Component.setBounds вызывает Component.reshape, но не reshape вызывает
setBounds. Это происходит потому, что AWT часто требуется вызывать его для
своих собственных нужд. Если AWT вызовет старый метод, затем новый метод, и
Вы перегрузили новый метод, AWT (что есть неправильно) не вызовет Вашу
процедуру. В случае если AWT вызывает новый метод (а он в свою очередь
вызывает старый), любые перегрузки нового метода будут корректно вызваны
AWT по необходимости. Если вышесказаное вам не понятно, просто забудте это.

*(Часть 10) Как мне отцентрировать диалоговое окно?

[*] На текущий момент, Вы не можете получить абсолютные координаты апплета
на экране. Его положение (0,0) относительно броузера, а не экрана. Но вы
можете отцентрировать нечто, что всплывает или показывается, на экране с
помощью кода, как этот:

Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
my_window.move(
 ( screen.width - my_window.size().width ) / 2,
 ( screen.height - my_window.size().height ) / 2 );

my_window.show().

В подобной манере, Вы можете отцентрировать нечто относительно его предка.
Обратите внимание на умное использование такого API, как translate(),
которое делает работу за Вас:

void center(Component parent) {
    pack();

    Point p = parent.getLocation();
    Dimension d = parent.getSize();
    Dimension s = getSize();

    p.translate((d.width - s.width) / 2,
                (d.height - s.height) / 2);
    setLocation(p);
}

*(Часть 10) Как я могу получить абсолютные координаты мыши?

[*] Вы имеете в виду, что если размеры броузера 640x480, Вы хотите получить
Y-координату в диапазоне от 0 до 480. А если окно броузера около 800х600,
то в диапазоне от 0 до 600. Это может понадобится для всплывающий меню,
когда вы хотите его показать по абсолютным координатам мыши.

Метод заключается в анализе событий мыши и положений указателя и его
родителей пока не будет родителя. Хотя для некоторых броузеров это
неприменимо. [Ждем лучших предложений.]

*(Часть 10) Как я могу определить изменение размера фpейма или другого
компонента?

[*] Если Вы используете JDK 1.0.2, Вы можете перегрузить метод
Component.reshape(int, int, int, int) что бы делать все что Вам необходимо,
конечно необходимо вызывать super.reshape().
В JDK 1.1.х, setBounds() пришел на смену reshape(), тем не менее существует
путь лучше, чем перегрузка метода setBounds(), для определения изменения
размеров - использовать новую модель событий. Заметьте, что новое API
вызывает устаревшее.

Приемлимый путь определения изменения размеров в JDK 1.1.х это
зарегистрировать ComponentListener на Frame'е, приблизительно так:

import java.awt.*;
import java.awt.event.*;

class MyFrame extends Frame {
    public MyFrame() {
        addComponentListener(new CmpAdapter());
    }

    class CmpAdapter extends ComponentAdapter {
        public void componentResized(ComponentEvent evt) {
            //doSomething();
        }
    }
}

Иначе такого же эффекта можно добится таким образом:

class MyFrame extends Frame implements ComponentListener {
    public MyFrame() {
        addComponentListener(this);
    }

    public componentHidden(ComponentEvent evt) { }
    public componentMoved(ComponentEvent evt) { }
    public componentShown(ComponentEvent evt) { }
    public componentResized(ComponentEvent evt) {
        //doSomething
    }
}

Или так:

  public MyFrame() {
          addComponentListener(new ComponentAdapter() {
            public void componentResized(ComponentEvent evt) {
              // doSomething;
        }
      } );
    }

*(Часть 10) Для чего предназначены методы Component preferredSize() и
minimumSize()?

[*] Эти методы позволяют LayoutManagerу вычислять продпочтительный и
минимальный размеры компонент, которые он упорядочивает. Вы можете
управлять значениями, котрые использует LayoutManager, создавая подклассы
используемых вами компонентов и перегружая эти методы. Вы их не вызываете,
Вы перегружаете их и они вызываются из вне.

*(Часть 10) Но разве AWT не может просто запомнить, что было выведено в
данный контекст Graphics и продублировать это вместо того, чтобы вызывать
paint()?

[*] Наверное, может, но как вы сможете отменить что-то, что уже было
нарисовано? Как вы сможете начать рисование заново, с другим содержимым?
Эти проблемы можно решить, если ввести дополнительные методы, но это не
лучший выход. На практике гораздо проще иметь возможность взглянуть на
метод paint и увидеть все, что будет сделано при перерисовке этой
компоненты. Итог: Используйте paint(), а не g=getGraphics(); g.drawString(
...

*(Часть 10) Когда я часто вызываю repaint(), половина моих запросов
теряется и не отображается на экране. Почему?

[*] repaint() только сообщает AWT, что вы хотите, чтобы произошла
перерисовка. AWT соберет несколько последовательных запросов на перерисовку
в один, так что будет выполнена только самая последняя перерисовка. Один из
возможных обходных путей - использовать отсекающий прямоугольник и
перерисовывать только те участки, которые были изменены.
*(Часть 10) Почему я получаю это сообщение, если использую JDK 1.1 под X
Windows?

java.lang.NullPointerException
at sun.awt.motif.MFramePeer.<init>(MFramePeer.java:59)
at sun.awt.motif.MToolkit.createFrame(MToolkit.java:153)
at java.awt.Frame.addNotify(Frame.java)
at java.awt.Window.pack(Window.java)

[*] В вашей системе отсутствует шрифт. Переименуйте font.properties из
подкаталога "lib" в font.properties.bak. Тогда JDK не будет искать
несуществующий шрифт.

Эта проблема возникает, поскольку библиотеки Motif AWT используют шрифт
"plain Dialog 12 point" в качестве шрифта по умолчанию. К несчастью, если
используется удаленный X сервер, это шрифт иногда недоступен.

Для X терминала диагностические сообщения могут слегка отличаться:

% appletviewer HelloWorldApplet.html
SIGSEGV 11* segmentation violation
si_signo [11]: SIGSEGV 11* segmentation violation
si_errno [0]: Error 0
si_code [1]: SEGV_ACCERR [addr: 0x14]

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

xlsfonts > ~/fonts.txt

Затем пройдитесь по длинному списку шрифтов и выберите те, которые хотите
использовать. Программа xfd демонстрирует выбранный шрифт:

xfd -fn "имя вашего шрифта" &

*(Sect. 10) Почему GridBagLayout так сложно использовать?

[*] Для этого есть две причины. Во-первых, хотя небольшие упаковки довольно
просты, детализированная упаковка для GUI очень сложна. Во-вторых, при
разработке GridBagLayout человеческий фактор и простота использования не
были основной целью. Если это вас раздражает (меня это раздражает), не
используйте GridBagLayout. Разместите свой GUI на нескольких панелях и
используйте для каждой из них свой менеджер упаковки, чтобы достичь нужного
вам эффекта. Официальное объяснение, данное руководителем проекта AWT
участникам Mountain View Java Users Group 4 декабря 1996, звучит так:

     "Так случилось, и это уже принято к сведению, что GridBagLayout
     слишком сложен для того, чтобы выполнять возложенные на него функции.
     GBL будет по-прежнему поддерживаться, но также будет вскоре выпущен
     улучшенный и упрощенный вариант. Этот 'улучшенный GBL' может быть
     использован вместо GBL."

Итог: не нужно тратить усилий на GBL, на данный момент существуют более
простые альтернативы. К тому же, GBL является причиной утечки памяти. GBL
вставляет "добавленные" компоненты в хэш-таблицу, но
removeLayoutComponent() их никогда не удаляет. См. ошибку номер 4195295.

Трудно пройти мимо документации по GBL. Основываясь на очевидном сходстве,
ее можно взять из grid layout manager из Tk (Tcl/Tk). Если вам не нравятся
вложенные панели и ни один из других менеджеров упаковки не делает того,
что вам нужно (или вы работаете с унаследованным кодом, который уже его
использует), документация из Tk может вам пригодиться.

*(Часть 10) MyClass работает отлично, за исключением случая, когда я хочу
установить другой шрифт. Я не могу заставить это работать под Win95, но то
же самое работает под MacOS и Unix.

[*] Вы, скорее всего, указали шрифт, который отсутствует в вашей поставке
Win95; это одно из различий между платформами, на которые вы можете
натолкнуться, если используете специфику одной из платформ, например,
задаете "Arial" в качестве шрифта и рассчитываете, что это будет работать
на платформах, отличных от Windows.

Для Windows 95 и Solaris 2.6 эти шрифты

   * Dialog
   * SansSerif
   * Serif
   * Monospaced
   * Helvetica
   * TimesRoman
   * Courier
   * DialogInput
   * ZapfDingbats

обнаружены следующей программой:

import java.awt.*;

class foonly {
    static public void main(String s[])
    {
        String n[]= new Frame().getToolkit().getFontList();
        for (int i=0;i<n.length; i++)
            System.out.println(n[i]);

        System.exit(0);
    }
}

Другими словами, вы можете получить массив типа String имен шрифтов,
используя

String[] fonts = Toolkit.getDefaultToolkit().getFontList()

Вместо настоящих имен шрифтов, таких как Helvetica, TimesRoman, и Courier в
JDK 1.1 было отдано предпочтение стилям, таким, как SansSerif, Serif, и
Monospaced (соответственно). Стиль шрифта будет отображаться в наиболее
подходящий шрифт для данной платформы.

В настоящий момент для отображения стилей в имена системных шрифтов
используются записи в одном из файлов font.properties в $JAVAHOME/lib.
Имеется несколько файлов font.properties, соответствующих разным
локализациям. Если вам нужно быстро протестировать новый шрифт, вы можете
изменить файл или дополнить его так, что отображение будет производиться в
тот шрифт, который вы хотите проверить.

*(Часть 10) Я создал Lightweight-компоненту (компоненту, непосредственно
расширяющую класс Component), но она мерцает/не перерисовывается как
следует. Почему?

[*] Lightweight-компоненты, поскольку они считаются "прозрачными", не
перерисовываются непосредственно в ответ на repaint(). Фактически
Component.repaint() просматривает стек компонент снизу вверх, находит
"непрозрачную" Heavyweight-компоненту (она должна быть контейнером), и
затем вызывает *ее* метод repaint().

Из этой точки управление в итоге передается методу Container.update().
Первое, что он делает - вызывает super.update, приводя нас к
Component.update(), который очищает компоненту цветом фона, поскольку он
был вызван для heavyweight-компоненты, и завершается. Затем
Container.update() вызывает update рекурсивно для всех
Lightweight-компонент, которые этот контейнер содержит.

Итог: "прозрачность" lightweight-компонент будет работать правильно (без
мерцания) если первая доступная выше по иерархии включения
heavyweight-компонента является

   * heavyweight-компонентой с двойной буферизацией (она должна быть
     контейнером), или
   * heavyweight-компонентой, которая никогда не обновляется, а только
     перерисовывается (т.е. такой, где метод update() перекрыт и фон по
     умолчанию не очищается).

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

Другой важный момент состоит в том, что если ваш контейнер определяет
собственный метод paint(), то он обязан вызывать super.update/paint(),
иначе содержащиеся в нем lightweight-компоненты никогда не будут
перерисованы. Собрав это воедино, мы видим, что в данном случае для
нормальной работы нужно внесение минимальных изменений в код - поместить
метод

public void update(Graphics g) {
    super.paint(g);
}

в ближайший в иерархии компонент heavyweight-контейнер, содержащий
lightweight-компоненты, которые ничего не выводят в тех областях, которые
уже были прорисованы их родителями, т.е. "непрозрачные" компоненты. Грязно,
но быстро.

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

  public void update(Graphics g) {
    // стандартное создание внеэкранного контекста.
    offg.fillRect(нужный цвет фона, полный размер);
    super.paint(offg);
    g.drawImage(myimage, 0, 0, null);
    }

  public void paint(Graphics g) {
    // на случай изменения размера может вначале вызывать update().
    super.paint(offg);
    g.drawImage(myimage, 0, 0, null);
    }

Их можно объединить, если заставить this.update() вызывать this.paint(), с
подстановкой различных значений параметров, но проще всего перекрыть их по
отдельности, как в примере.

*(Часть 10) В чем разница между методом setForeground(Color c) класса
Component и setColor(Color c) класса Graphics?

[*] Во первых, эти два метода делают одно и тоже: устанавливают цвет в
значение параметра. Разница в том, где это испоьзуется. Есть еще
Component.setBackground, который устанавливает цвет фона.

Если Вы пишите конструктор или обработчик события (вроде "Нажмите здесь
чтобы сделать рисунок синим"), то у вас есть Component и нужно использовать
метод setForeground(). А вот в меттоде paint() у вас есть аргумент типа
Graphics, и поэтому обычно используется g.setColor(c).

В отличие от класса Component, класс Graphics не имеет независимых цвета
фона и цвета рисунка. Графический обьект поступает с цветом, полученым от
рисуемой поверхности. Но после вызова setColor() вся графика будет
рисоваться указаным цветом. Так как эти методы делают разные вещи, названы
они по разному.

*(Часть 10) Когда я двигаю мышью с нажатой кнопкой в Component и затем
сдвигаю мышь с Component, то все события посылаются в тот Component, хотя
курсор и вне его. Это ошибка?

[*] Нет, это особое свойство. Документация о Java API гласит:

     "... При нажатой кнопке мыши события о движении мыши посылаются в
     компонент даже когда курсор выходит за пределы компонента. Это
     продолжается до тех пор, пока кнопка не будет отпущена..."

Это сделано для удобства и облегчения программирования. Вы можете
обрабатывать "претаскивания" с место на место. Если это вам не нужно, то
просто проверте координаты события, и игнорируйте их, если они происходят
вне компонента.

*(Часть 10) Почему мои окна не закрываются, когда я нажимаю на кнопку
закрытия в заголовке?

[*] Вот как нужно делать:

   * JDK 1.0.2: перехватывайте Event.WINDOW_DESTROY и вызывайте методы
     hide() и dispose() для Frame.
   * JDK 1.1:
        o Слушайте события WindowEvent и в методе windowClosing() вызывайте
          hide() и dispose(). Это должно было быть действие по умолчанию, и
          наконец это сделано в Swing'овском JFrame.
        o Разрешите события типа AWTEvent.WINDOW_CLOSING и вызывайте hide()
          и dispose() в етоде processWindowEvent().
   * JDK 1.2: Компонент JFrame по умолчанию сам закрывется при нажатии на
     копку закрытия (см. раздел 10).

*(Часть 10) Как принудительно синхронизировать графические операции,
например, смену курсора или анимацию?

[*] Это делается методом sync() в Toolkit. Вот так:

AnyComponent.getToolkit().sync();

*(Часть 10) Как с помошью клавиши Tab передвигать фокус по компонентам?

[*] В JDK 1.0 Вы должны сами реагировать на нажатие клавиш и самому
программировать передвижению. JDK 1.1 поддерживает Tab и Shift+Tab (назад)
автоматически. Фокус передвигается в том порядке, в каком компоненты были
помещены в контейнер.

*(Часть 10) В чем разница между "низкоуровневыми" и "семантическими"
событиями?

[*] Низкоуровневые события связаны с конкретным компонентом (изменение
размера окна, движение мышью, нажатие клавиши, добавление компонента в
контейнер и т.п.). Семантические события возникают, когда Вы действуете на
управляющий компонент (двигаете линейку прокрутки, нажимаете на кнопку,
выбираете пункт меню и т.п.), и такой тип событий может быть генерирован
несколькими разными компонентами. Button и List генерируют событие Action
когда Вы на них нажимиете.

Самым главным для программиста является то, что Вы можете изменять
низкоуровневые события, как, например, значение клавиши в событии о нажатии
клавиши, при этом новое значение будет отображено. Кроме того, Вы сами
можете "потребить" такое событие, и оно не будет доставлено компоненту. Но
с семантическими событиями такое не пройдет - они уже попало в компонент.

Семантические события: используйте addXListener() чтобы добавить слушателя,
который реализует интерфейс XListener и может получать события типа XEvent.
Низкоуровневые сообытия: используйте метод enableEvents() и перекройте
performX(), чтобы перехватывать события в самом объекте.

*(Часть 10) Возможно ли сделать так, чтобы окно в Java плавало поверх
других? Например, колонка инстраментов плавает над обычными окнами, в
которых эти инструменты используются...

[*] В MS Windows обьект типа Window плавает поверх других окон, в отличие
от Frame, который находится внизу как обычное окно. такое поведение дает
"плавающий" эффект. Но обязательно ли должно Window плавть поверх других -
это совершенно другой вопрос.

На Макинтошах Window либо лежит так же как и обычные окна, либо полностью
модальное - в зависимости от VM, которую Вы используете. В Java нет легкого
способа добиться такого поведения. Если кто-нибудь знает такой способ,
пожалуйста, поделитесь.

*(Часть 10) Могу ли я свернуть/развернуть окно в Java?

[*] В JDK 1.1 не было способа сделать это. В JDK 1.2 эта возможность
появилась:

     MyFrame.setState( Frame.ICONIFIED );
     MyFrame.setState( Frame.NORMAL );

делает это. Установите нужное состояние.

*(Часть 10) Как узнать, какая кнопка мыши была нажата и сколько раз?

[*] Для обработки событий мыши Вы должны осуществить интерфейс
MouseListener либо расширить класс MouseAdapter и чтобы переопределить один
из методов обработки событий мыши. Аргумент типа MouseEvent, передаваемый в
методы, содержит поля, говорящие о том, какая кнопка нажата, и счетчик
нажатий. Используйте что-то вроде:

public void mouseClicked(MouseEvent m) {
 boolean leftButtonPush   =
    (m.getModifiers() & java.awt.event.InputEvent.BUTTON1_MASK) != 0;
 boolean centerButtonPush =
    (m.getModifiers() & java.awt.event.InputEvent.BUTTON2_MASK) != 0;
 boolean rightButtonPush  =
    (m.getModifiers() & java.awt.event.InputEvent.BUTTON3_MASK) != 0;

 int click = m.getClickCount();    // может быть 1, 2, 3 щелчка или больше

Вы можете вызвать m.isPopupTrigger(). Если этот метод возвращает true, то
пользователь запросил всплывающее меню. Во многих системах правая кнопка
служит для вызова таких меню.

Секция 5 из 7 - Предыдущая - Следующая

 Frequently Asked Questions (with answers) for Java programmers.
Лента новостей


2006 (c) Copyright Hardline.ru