Windows Foundation Classes for Java (WFC) образует каркас, который состоит из Java-пакетов и поддерживает
компоненты, предназначенные для операционной системы Windows и модели объектов
Dynamic HTML. WFC тесно интегрирован со средой программирования Visual J++ и
предоставляет полный набор элементов управления Windows, написанных на Java.
Благодаря этому — а также поддержке средств типа Intelli-Sense, Forms Designer,
Application Wizard и Object Browser — значительно упрощается создание Windows-приложений
на Java. Но как бы средства Visual J++ ни облегчали разработку приложений, Вы,
наверное, все же захотите разобраться в структуре и логике пакетов и классов,
из которых состоит WFC.
Цель этой
главы — представить концептуальную структуру пакетов и классов WFC, а также
разъяснить некоторые из его фундаментальных моделей. Многие пакеты образуют
инфраструктуру модели компонентов, и разработчики, сосредоточенные на использовании
WFC-элемен-тов, могут их игнорировать. К остальным пакетам проще всего обращаться
из Forms Designer среды Visual J+ + . Изучая библиотеку WFC, Вы поймете, какие
пакеты и классы нужны для Вашего приложения. В разделах этой главы рассматриваются
следующие вопросы.
Фундаментальные
блоки WFC — Windows DLL и Dynamic HTML. WFC базируется на Win32-мoдeли программирования,
что позволяет создавать на Java Windows-приложения, способные задействовать
все элементы пользовательского интерфейса, события и системные сервисы Windows.
WFC построен также и на модели объектов Dynamic HTML, что дает возможность создавать
на Java как клиентские, так и серверные HTML-страницы, и работать с Dynamic
HTML непосредственно из Java. В основе этих технологий лежат DLL-библиотеки,
которые предоставляют для инфраструктуры WFC ключевые API-функции. Эти DLL стали
доступными в Java благодаря двум технологиям: JActiveX и J/Direct. Если DLL
представляет COM/ActiveX-компонент, JActiveX создает классы-оболочки, преобразующие
СОМ-объекты в Java-объекты. А если DLL основана не на СОМ, то средствами J/Direct
можно напрямую обращаться к ее функциям и обеспечить маршалинг типов данных
между Java и языком, на котором была создана эта DLL (например, С или C++).
Обе технологии используют все возможности компилятора JVC и Microsoft Virtual
Machine for Java (Microsoft-реализации виртуальной машины Java).
Об этом важно
знать в основном потому, что некоторые пакеты WFC целиком состоят либо из классов-оболочек
СОМ (формируемых JActiveX), либо из классов J/Direct. У этих классов есть методы,
непосредственно связанные с API-функциями, — они не документированы в WFC Reference,
так как обычно не вызываются напрямую, а предназначены для поддержки классов
из других пакетов.
Кроме пакетов
поддержки API конкретной операционной платформы, WFC включает семь основных
пакетов.
Пакет |
Описание |
||
com.ms.wfc.app |
Базовые классы,
инкапсулирующие поддержку Windows-приложений. Сервисы этих классов
используются шаблоном форм в Visual J++. Кроме базовой структуры обработки
сообщений Windows, они поддерживают такие элементы Windows, как буфер
обмена, реестр, потоки, описатели окон, системная информация и т.
д. |
||
com.ms.wfc.core |
Базовые классы
модели компонентов. Этот пакет поддерживает контейнеры, события, исключения,
свойства и инфраструктуру, обеспечивающую взаимодействие с такими
средствами Visual J++, как Forms Designer. |
||
com.ms.wfc.data |
Java-классы
ADO (ActiveX Data Objects), поддерживающие доступ к данным и связывание
с данными. Кроме того, он включает пакет com. ms.wfc. data. ui, который
предоставляет базовые классы для WFC-элементов, связанных с доступом
к данным. |
||
com.ms.wfc.html |
Классы, используемые
для реализации Dynamic HTML в Java. |
||
com.ms.wfc.io |
Классы, применяемые
при доступе к потокам данных. Реализуют полный набор средств чтения
и записи последовательных потоков, доступа к файлам и согласования
потоков данных разных типов. |
||
com.ms.wfc.ui |
Базовые классы
элементов управления, поставляемых с WFC. Эти классы обеспечивают
также доступ к графическим API-функциям. |
||
com.ms.wfc.util |
Вспомогательные
классы для сортировки, реализации хэш-таблиц и т. д. |
||
Для поддержки
API конкретной операционной платформы в WFC имеются следующие основные классы.
Пакет |
Описание |
||
com.ms.wfc.ax | Классы-оболочки Java для ActiveX-интерфейсов. | ||
com.ms.wfc.html.om | Классы-оболочки Java для модели объектов Dynamic HTML. | ||
com.ms.wfc.ole32 | Классы-оболочки Java для сервисов OLE. | ||
com.ms.wfc.win32 | Классы-оболочки Java для Win32 API. | ||
Работа
с визуальными WFC-компонентами
Хотя визуальные
WFC-компоненты для приложений Win32 и Dynamic HTML (DHTML) имеют некоторые различия,
общих характеристик у них все же больше, что позволяет адаптировать WFC-модель
компонентов к потребностям приложений обоих типов. Например, и Win32-, и DHTML-модели
включают основные типы элементов управления (поля, флажки, кнопки, переключатели
и поля со списками). У многих распространенных элементов управления Win32 есть
свои аналоги в DHTML — они и называются так же, только перед именем ставится
префикс Dh (так, классу com.ms.wfc.ui.Edit соответствует класс com.ms.wfc.html.DhEdit).
Кроме того, шрифты, цветовые константы и большинство типов событий одинаковы
в обеих моделях. Конечно, существуют и компоненты, характерные лишь для какой-то
одной из этих моделей, например таблицы в DHTML или элементы управления со списочным
представлением (list view controls) в Win32. Вероятно, самое заметное различие
между WFC-компонентами для Win32 и DHTML в том, что компоненты для DHTML недоступны
в Forms Designer. Это означает, что Вы должны создавать, добавлять и изменять
элементы DHTML в редакторе исходного кода, но Java-код, реализующий такие компоненты,
в обеих моделях выглядит практически одинаково.
WFC интегрирован
с Visual J++инструментом визуальной разработки и является надстройкой над Win32
API операционной системы Windows. Основной пакет WFC, поддерживающий визуальные
компоненты, — com.ms.wfc.ui, а базовый класс большинства визуальных WFC-компонентов
— класс com.ms.wfc.ui.Control. Многие элементы управления WFC (WFC-элементы)
расширяют и этот класс, и,класс Form (который представляет визуальный контейнер
для элементов управления).
Класс
Control
Класс Control
содержит все основные свойства и методы, необходимые для управления Win32-OKHOM.
Методы можно сгруппировать по назначению.
Control расширяет
класс com.ms.wfc.core.Component — базовый для всех WFC-компонентов.
Использование
форм
Форма — главный
визуальный элемент приложения или нестандартное диалоговое окно, связанное с
приложением. Основой всех форм в WFC служит класс com.ms.wfc.ui.Form.
Forms Designer
в Visual J++ начинает свою работу с вывода шаблона формы, который предоставляет
класс, расширяющий com.ms.wfc.ui.-Form, помогает настроить свойства формы и
добавить на нее элементы управления. Класс, производный от Form, добавляет метод
main, отсутствующий в Form. Форма запускается вызовом из main метода com.ms.wfc.
арр.Application, run с передачей ему нового экземпляра класса, основанного на
Form (этот код уже присутствует в шаблоне). Подробнее см. раздел «Запуск
и завершение приложений» далее в этой главе. Основанный на Form класс,
используемый в качестве модального диалогового окна, можно запустить вызовом
Form. showDialog (showDialog запускает
и модальные диалоговые окна, основанные на классе com.ms.wfc.ui.CommonDialog).
Немодальные диалоговые окна, основанные на Form, можно открывать методом show
формы, который делает ее видимой.
Form расширяет
класс com.ms.wfc.ui.Control, поэтому у него есть все методы класса Control плюс
множество собственных, поддерживающих его функциональность как окна и контейнера
элементов управления. К ним относятся методы для:
Этот список
неполон, но дает общее представление о формах. Еще один класс из пакета com.ms.wfc.ui,
расширяющий класс Form, — UserCont-rol. Он предназначен для создания нестандартных
элементов управления, основанных на Form; Вы можете помещать их в Toolbox.
Обзор
WFC-элементов
Все видимые
WFC-элементы содержатся в пакете cpm.ms.wfc.ui. Так как в нем более 240 классов,
сразу найти нужные довольно трудно. К счастью, они разбиты на несколько основных
категорий.
Классы, показанные
в следующей таблице, содержатся в пакете com.ms.wfc.ui и расширяют непосредственно
Control. Элементы управления, которые расширяют эти классы, перечислены во второй
графе таблицы.
Класс |
Описание |
||
Animation |
Инкапсулирует
Windows-элемент управления Animation, который воспроизводит анимационные
файлы формата AVI (Audio- Video Interleaved). |
||
AxHost |
Оболочка ActiveX-элемента,
позволяющая обращаться с нему как к WFC-элементу. |
||
Button |
Инкапсулирует
Windows-элемент управления Button — кнопку. |
||
CheckBox |
Инкапсулирует
Windows-элемент управления CheckBox — флажок с меткой. |
||
ComboBox |
Инкапсулирует
Windows-элемент управления ComboBox — поле со списком. |
||
DateTimePicker |
Инкапсулирует
Windows-элемент управления DateTimePicker, позволяющий вводить дату
и время. |
||
Edit |
Инкапсулирует
Windows-элемент управления Edit, позволяющий вводить текст. |
||
Form |
Представляет
окно верхнего уровня. |
||
GroupBox |
Инкапсулирует
элемент управления GroupBox — прямоугольник, включающий другие элементы
управления. |
||
Label |
Инкапсулирует
Windows-элемент управления Label, который отображает строку текста,
не редактируемую пользователем. |
||
ListBox |
Инкапсулирует
Windows-элемент управления ListBox — список, из которого пользователь
выбирает один или несколько элементов. Используется совместно с Listltem.
Элемент управления CheckedListBox расширяет класс ListBox. |
||
ListView |
Инкапсулирует
Windows-элемент управления ListView, отображающий набор элементов,
каждый из которых состоит из значка (он берется из списка изображений)
и метки. |
||
MDIClient |
Представляет
окно, содержащее дочерние MDI-окна. |
||
MonthCalendar |
Инкапсулирует
Windows-элемент управления Calendar, предоставляющий элементарный
интерфейс в виде календаря, из которого можно выбрать дату. |
||
Panel |
Представляет
контейнер для других элементов управления. Его расширяет класс TabPage. |
||
PictureBox |
Инкапсулирует
Windows-элемент управления PictureBox, используемый для вывода растровых
изображений. |
||
ProgressBar |
Инкапсулирует
Windows-элемент управления ProgressBar, который динамически отображает
ход выполнения операции, заполняя полоску крошечными «кирпичиками». |
||
RadioButton |
Инкапсулирует
Windows-элемент управления RadioButton — переключатель. |
||
Rebar |
Инкапсулирует
элемент управления Rebar — контейнер для других элементов управления;
его можно перемещать и масштабировать. Используется совместно с RebarBand. |
||
RichEdit |
Инкапсулирует
Windows-элемент управления RichEdit. |
||
ScrollBar |
Представляет
базовый класс для полос прокрутки. HScrollBar и VScrollBar расширяют
этот класс. |
||
Splitter |
Инкапсулирует
элемент управления Splitter, позволяющий изменять размер1
стыкуемых элементов управления в период выполнения. |
||
StatusBar |
Инкапсулирует
Windows-элемент управления StatusBar. Используется совместно с классом
StatusBarPanel. |
||
TabBase |
Определяет базовый
класс, содержащий основную функциональность для элементов управления
типа «вкладка». TabControl (использующий TabPage) и TabStrip
(использующий Tabltem) расширяют этот класс. |
||
ToolBar |
Инкапсулирует
нестандартный элемент управления ToolBar. Используется совместно с
классом ToolbarButton. |
||
TrackBar |
Инкапсулирует
Windows-элемент управления TrackBar (также известный как Slider) —
линейку с ползунком для выбора значения из заданного диапазона. |
||
TreeView |
Инкапсулирует
Windows-элемент управления TreeView. Используется совместно с классом
TreeNode. |
||
UpDown |
Инкапсулирует
элемент управления UpDown (иногда называемый Spinner) — наборный счетчик. |
||
Доступ к графическим
средствам
WFC-приложения
выполняют графические операции, используя объект Graphics, который инкапсулирует
встроенные графические средства операционной системы Windows. Этот объект обеспечивает
гибкую поддержку большинства распространенных графических операций, включая
вывод изображений и значков, а также прорисовку линий, многоугольников и текста.
Объект Graphics
выполняет свою работу, создавая оболочку контекста устройства Windows — структуры
данных, определяющей системные графические объекты, связанные с ними атрибуты
и графические режимы. Вы можете получить контекст устройства, скрываемый объектом
Graphics, и использовать объект Graphics параллельно с графическими функциями
Win32 API.
Все WFC-объекты,
расширяющие объект Control, поддерживают создание объекта Graphics методом createGraphics,
а объекты, расширяющие объект Image, — например, Bitmap, Icon и Metafile, -
методом getGraphics. Подробнее об использовании этого объекта см. главу 15 «Графика».
Визуальные
компоненты Dynamic HTML
Элементы
Dynamic HTML образуют второй набор визуальных компонентов в WFC. Элементы управления
из пакета com.ms.wfc.html базируются на модели объектов Dynamic HTML. Классы
этого пакета применяются
при создании новых элементов и связывании с существующими на HTML-странице.
Такие компоненты создаются и используются в клиентских браузерах или на сервере,
передающем их клиентским браузерам. Модель объектов Dynamic HTML существует
на нескольких платформах. Она не привязана исключительно к Win32, хотя элементы
пользовательского интерфейса обеих моделей весьма схожи, потому что стандартный
набор кнопок, списков, переключателей и др. имеется как в Win32, так и в DHTML.
Оба набора
WFC-элементов (для Win32 и Dynamic HTML) построены на аналогичной основе, так
как оба в конечном счете производны от класса com.ms.wfc.core.Component. Компоненты
могут быть включены в контейнер и поддерживают интерфейс IComponent, имеющий
методы для «посадки» (siting) компонентов. Вопрос о том, как связываются
компоненты и контейнеры, большинству программистов, использующих WFC, мало интересен;
но поскольку элементы и из com.ms.wfc.html, и из com.ms.wfc.ui базируются на
компонентах, они обладают близкими характеристиками. Так, все компоненты добавляются
в свои родительские контейнеры методом add.
Подробнее
об использовании пакета com.ms.wfc.html см. главу 14 «Программирование
Dynamic HTML в Java».
Базовый класс
Control и классы, его расширяющие (например, кнопки и поля), предоставляют стандартные
Windows-события: click, keyPress, mouseMove, dragDrop и др. Вы работаете с событиями
в своем приложении, используя делегаты (delegates). Для написания обработчиков
событий в приложении не обязательно досконально разбираться в делегатах. Но
понимание того, как создаются и применяются делегаты, очень важно при разработке
элементов управления, взаимодействии с другими приложениями, генерирующими события,
или использовании WFC-компонентов в потоках. Без него сложно вникнуть и в детали
Java-кода, генерируемого Forms Designer. Поэтому в данном разделе сначала даются
общие сведения о делегатах, а потом рассматриваются практические аспекты обработки
событий.
Что такее
делегат? Объявление delegate определяет класс, расширяющий com.ms.lang.Delegate.
Компилятор JVC, тоже распознает ключевое слово delegate, упрощая создание класса,
основанного на Delegate. Экземпляр делегата может вызвать какой-нибудь метод
объекта и передать ему данные. Делегат — что очень важно — изолирован от объекта,
на который он ссылается, и ему не нужно ничего о нем знать. Поэтому он идеально
подходит для «анонимных вызовов». В других языках подобная функциональность
реализуется процедурными указателями. Но в отличие от них делегаты объектно-ориентированны,
обеспечивают полный контроль типов и безопасны.
В WFC делегаты
чаще всего применяются для связывания событий с методами-обработчиками, например,
событие click кнопки Вы связываете с методом его обработки в своем классе. При
возникновении события элемент управления вызывает делегат, передавая ему всю
доступную информацию, связанную с событием. Делегат в свою очередь вызывает
зарегистрированный метод-обработчик и передает данные ему. Вы можете использовать
делегаты и для связывания одного события с несколькими методами — такая организация
называется многоканальным вещанием (multicasting); когда возникает событие,
вызывается каждый делегат из списка в том порядке, в котором они были добавлены.
И наоборот, делегатам нескольких разных событий можно присвоить один и тот же
метод-обработчик (например, кнопка панели инструментов и элемент меню могут
вызывать один обработчик). Работая с событиями, Вы регистрируете делегат, чтобы
Ваша программа получала уведомление об определенном событии в конкретном элементе
управления. Для этого Вы должны вызвать метод addOn<co6uтие> элемента
управления, где вместо <событие> подставляется имя нужного события.
Так, чтобы зарегистрировать делегат для события click кнопки, вызовите метод
addOnClick объекта кнопки. Метод addOn<co6ыmue> принимает в качестве
параметра экземпляр делегата; обычно это существующий делегат WFC, сопоставленный
с данными конкретного события. При вызове addOn<co6bimue> создается
экземпляр делегата со ссылкой на метод, с которым Вы хотите связать событие.
Вот как связать обработчик события btnOK_Click (в текущем классе) с событием
click кнопки (btnOK):
Button btnOK
= new Button();
btnOk.addOnMouseClick(new
EventHandler(this.btnOK_Click)); Для большинства событий Вы можете создавать
и передавать экземпляр универсального делегата EventHandler, который возвращает
универсальный объект Event. Однако некоторые события, несущие дополнительную
информацию, требуют специальных классов обработчиков. Например, события, сообщающие
о перемещении мыши, обычно содержат такую информацию, как позиция курсора мыши.
Чтобы получить ее, Вы создаете и передаете экземпляр класса MouseEventHandler,
а тот возвращает обработчику объект MouseEvent. Для получения информации о состоянии
клавиш Shift и др. от событий, связанных с клавиатурой, нужен класс KeyEventHandler
— он возвращает объект KeyEvent. В WFC все классы делегатов обработчиков событий
расширяют класс com.ms.lang.Delegate. Большинство из них содержится в пакете
com.ms.wfc.ui, их имена заканчиваются на EventHandler. События WFC (пакет
com.ms.wfc.ui) расширяют класс com.ms.wfc.core.Event, их имена заканчиваются
на Event.
Совет Forms
Designer-позволяет связывать событие с определенным методом через окно Properties
в режиме Events. При этом Forms Designer создает соответствующий метод addOn<co6ытие>
и заготовку обработчика этого события.
Вызывая Ваш обработчик, делегат передает ему два параметра. Первый — ссылка на объект, где возникло данное событие, второй — объект Event, который, возможно, содержит информацию о событии. Обработчик для делегата из предыдущего примера может выглядеть так:
private void btnOK_Click(Object source, Event e)
{
if (source instanceof Button)
{
String buttonName = ((Button)source).getText();
MessageBox.show("You clicked button " + buttonName);
} }
Если Вы связали событие со своим методом через универсальный класс EventHandler, объект Event в Вашем обработчике не даст никакой полезной информации. Но если событие несет такую информацию, Вы получаете ее от объекта конкретного события. Ниже показано, как могут выглядеть делегат и обработчик для события, связанного с перемещением мыши. Объект MouseEvent предоставляет свойства, позволяющие узнать позицию курсора мыши.
// запрос на уведомление
Button btnTest
= new Button();
// метод addOn<coбытие> использует класс
MouseEventHandler btnTest.addOnMouseMove(new
MouseEventHandler(this.btnTestMouseMove));
// обработчик
события "перемещение мыши"
private void btnTestMouseMove(Object source, MouseEvent e)
{
edit1.setText(e.x + ", " + e.y);
}
Если Вы хотите
обрабатывать одно событие нескольких элементов управления или несколько событий
одного элемента управления, запрашивайте отдельное уведомление для каждой комбинации
«элемент-событие». Несколько уведомлений могут указывать один и
тот же обработчик — например, все кнопки панели инструментов вызывают один обработчик
события click. Тогда через исходный объект, переданный обработчику события,
Вы сможете узнавать, какая кнопка была «нажата». (Обычно Вы приводите
тип переданного обработчику объекта к соответствующему классу, чтобы иметь возможность
вызывать методы этого класса.)
В следующем
примере определяются кнопки панели инструментов, запрашивается уведомление об
их событиях click и указывается метод-обработчик.
private void initEventHandlers()
{
Button buttonNew = new Button();
Button buttonSave = new Button();
Button buttonExit = new Button();
// все события направляются одному обработчику
buttonNew.addOnClick(new MouseEventHandler(this.toolbarClick));
buttonSave.addOnClick(new MouseEventHandler(this.toolbarClick));
buttonExit.addOnClick(new
MouseEventHandler(this.toolbarClick));
// общий обработчик
события
srivate void toolbarClick(Object source, Event e)
{
String buttonName;
if (source instanceof Button)
{
buttonName = new String((Button)source). getText());
MessageBox.show("You clicked button " + buttonName);
} }
WFC и Forms
Designer среды Visual J + + упрощают разработку приложений с многоязыковой поддержкой,
WFC-приложение можно создать в нескольких языковых версиях, различающихся лишь
двоичным файлом ресурсов. Правила именования файлов ресурсов требуют указывать
поддерживаемый язык в имени файла, а это позволяет при запуске приложения загружать
нужные ресурсы в соответствии с региональными стандартами и языком, выбранными
на компьютере пользователя. Локализация требует соответствующей реализации на
этапе разработки и поддержки в период выполнения. Ряд свойств визуальных компонентов
(форм и элементов управления) Visual J + + рассматривает как локализуемые. При
разработке приложения Visual J++ помещает значения таких свойств в двоичный
файл ресурсов. Например, текст, шрифт и размер элемента управления могут изменяться
в зависимости от языка.
Чтобы создать
локализованную версию приложения, сконструируйте его визуальное представление
в Visual J + +, установите свойство localizable формы как true и сохраните ее.
Тогда Visual J++ автоматически создаст двоичный файл ресурсов и поместит в него
все локализуемые свойства.
Когда свойство
localizable формы установлено как true, Visual J++ всегда сохраняет ресурсы
в единственном файле ресурсов с именем Форма.resources, где Форма
— имя главной формы (например, Forml.resources). Каждая создаваемая Вами
версия будет сохраняться под этим именем. А Вы, заканчивая подготовку каждой
новой языковой версии, дол-
жны делать
копию этого файла ресурсов и присваивать ей имя, соответствующее данному языку;
при этом Вы обязаны использовать стандартные суффиксы, принятые в Windows (например,
Forml_jpn_jpn.resources для японской версии). Первый суффикс обозначает основной
язык, второй — дополнительный.
Внимание
Прежде чем что-то изменять и сохранять любые локализованные версии,
обязательно сохраните под новым именем исходную версию. Она будет Вашим мастер-файлом
ресурсов.
Допустим,
Вам надо создать американо-английскую, французскую и японскую версии своего
приложения, главная форма которого называется Zippo.java. Предположим также,
что Вы начинаете работу с английской версии (но это необязательно). Создайте
сначала форму на английском языке и установите ее свойство localizable как true.
Сохранив форму, Вы создадите файл Zippo.resources. Теперь в Windows Explorer
или командой MS-DOS сделайте копию файла Zippo.resource и переименуйте ее в
Zippo_enu_enu.resources (enu — стандартный суффикс для американо-английских
версий, и в данном случае английский будет как основным, так и дополнительным
языком).
Затем в Forms
Designer поменяйте язык на французский и соответственно модифицируйте элементы
управления и их свойства. Закончив, снова сохраните форму — эта версия перезапишет
существующий файл Zippo.resources. Вновь сделайте копию этого файла и переименуйте
ее в Zippo_fra_fra.resources.
Чтобы протестировать
эту версию, откройте в Control Panel (Панель управления) диалоговое окно
Regional Settings Properties (Свойства: Язык и стандарты) и выберите
нужный региональный стандарт — в данном случае French (Standard) [Французский
(стандартный)]. Перезагружать систему после этой операции не требуется — региональный
стандарт будет изменен только для локального потока.
Использование
прикладных сервисов WFC
Пакет com.ms.wfc.app
содержит много классов, реализующих прикладные сервисы WFC. Большая их часть
принадлежит самому объекту Application. Эти сервисы главным образом создают
потоки, запускают приложения, обрабатывают события приложений и т. д. О Java-потоках
см. раздел «Использование Java-потоков с WFC» далее в этой главе.
Другие прикладные сервисы связаны с Win32-функция. Они обеспечивают доступ к
реестру Windows, буферу обмена и системной информации.
Запуск
и завершение приложений
WFC-приложение
запускается методом Application, run. Он обычно помещается в метод main класса,
основанного на Form и образующего главную форму приложения. У Application, run
имеются переопределенные методы, которые либо не принимают параметры, либо принимают
один параметр — класс формы, представляющий главное окно приложения. Его вызов, как правило, выглядит так:
public
static void main(String args[])
{
Application.
run(new MyMainWindow());
}
Если форма
передается методу run, ее свойство visible автоматически устанавливается как
true, а к форме добавляется обработчик события onClosed. Последний при закрытии
формы вызывает метод Application. exitThread. А если методу run форма не передается,
приложение работает, пока не будет вызван метод Application.exit, завершающий
все потоки и закрывающий окна приложения, или exitThread, завершающий только
текущий поток.
Вы используете
объект Application, чтобы закрепить обработчики за пятью разными событиями,
возникающими в контексте приложения: applicationExit, idle, settingChange, systemShutdown
и threadException. Сделать это позволяют следующие addOn-методы.
Метод Application |
Описание |
||
addOnApplicationExit |
Задает обработчик,
вызываемый при завершении приложения. В нем можно освободить ресурсы,
задействованные приложением и не очищаемые сборщиком мусора. (Чтобы
не дать приложению завершиться, назначьте обработчик для события закрытия
формы.) |
||
addOnldle |
Задает обработчик,
вызываемый, когда очередь сообщений приложения пуста. Его можно использовать
для выполнения фоновых операций или очистки. |
||
addOnSettingChange |
Задает обработчик,
вызываемый при изменении пользователем параметров окна. |
||
addOnSystemShutdown |
Задает обработчик,
вызываемый непосредственно перед завершением работы системы инициированным
пользователем. Это дает возможность сохранить пользовательские данные. |
||
addOnTh read
Except ion |
Задает обработчик,
вызываемый при возникновении неперехваченного исключения Java, что
позволяет приложению корректно его обработать. Обработчик принимает
в качестве параметра объект com.ms.wfc.app.Thread-ExceptionEvent,
у которого есть поле, идентифицирующее возникшее исключение. |
||
У addOn-методов
имеются парные removeOn-методы, удаляющие обработчики событий.
Win32-система
содержит большой объем информации, доступной WFC-приложениям и компонентам.
Доступ в основном осуществляется через классы, входящие в пакет com.ms.wfc.app.
Большая часть этой информации хранится в реестре Windows, и Вы обращаетесь к
ней через классы RegistryKey и Registry. Другую системную информацию, например
размеры экранных элементов Windows, параметры операционной системы, сведения
о сетевых и аппаратных возможностях, можно получить, вызывая статические методы
класса com.ms.wfc.app.Systemlnformation. Системное время доступно через класс
com.ms.wfc.app.Time. В этом разделе дан обзор способов, которыми WFC-приложение
может получить эту системную информацию.
Информация,
хранящаяся в реестре Windows
Класс RegistryKey
из пакета com.ms.wfc.app содержит методы для доступа к системному реестру Windows.
Используйте его методы, чтобы создавать и удалять подразделы реестра, подсчитывать
число подразделов в текущем разделе и получать их имена, а также чтобы получать,
устанавливать и удалять параметры в подразделах. Класс com.ms.wfc.app.Registry
имеет поля, содержащие объекты RegistryKey, которые представляют корневые разделы
реестра (они начинаются с HKEY_). (Экземпляры корневых объектов RegistryKey
можно создавать и методом getBaseKey.) Методы объектов RegistryKey позволяют
перечислять и манипулировать разделами и параметрами в подразделах данного корневого
объекта. Так, следующий код получает массив имен подразделов раздела HKEY_CURRENT_USER
и количество имен в этом массиве:
int subKeyCount;
String[] subKeyNames;
subKeyNames = Registry.CuRRENT_USER.getSubKeyNames();
subKeyCount = Registry. CURRENTJJSER.getSubKeyCount();
Аналогичным
образом можно получить или установить любой подраздел и его параметры. Следующий
пример демонстрирует, как получить список имен недавно использовавшихся в Visual
Studio файлов и вывести его в поле:
String path;
// для строки пути
String[] valueNames;
// для массива имен недавно использовавшихся файлов
int valueCount;
// для количества имен файлов в valueNames
path = new String("Software\\Microsoft\
\VisualStudio\\6.0\\FileMRUList");
RegistryKey
subKey = registry. CURRENTJJSER.getSubKey(path);
// получаем имена файлов и их количество
valueNames = subKey.getValueNames();
valueCount = subKey.getValueCount();
if (valueCount
> 0)
for (Int i=0; i < valueCount; ++i)
{
// получаем
параметр, т. е. имя файла
String value
= new String((String)subKey.getValue(valueNames[i]));
// конкатенация
имени ("1", "2", и т. д.) и значения параметра
String valString
= new String(valueNames[i] + " " + value);
// добавляем
результат в поле
edit1.setTSxt(edit1.getText()
+ valString + "\r\n"); }
Кроме того,
Вы можете создавать новые разделы методом createSubKey и устанавливать значения
параметров в этих разделах, используя метод setValue.
Информация
о национальных настройках
Эта информация
относится к настройкам, связанным с региональным стандартом и языком, которые
выбраны на компьютере пользователя. Сюда входит множество параметров, в том
числе набор символов, международный телефонный код, форматы отображения денежных
сумм, календарь, система мер и т. д.
Все они обычно
задаются в диалоговом окне Regional Settings Properties, открываемом
из Control Panel, но обращаться к ним можно и программно. WFC обеспечивает
доступ к этой информации через методы класса com.ms.wfc.app.Locale и подклассы
Locale с константами, которые используются методами Locale. О том, как считывать
и устанавливать эту информацию, см. методы класса Locale.
Информация
о времени
Существует
еще одна категория системной информации — время. Класс com.ms.wfc.app.Time предоставляет
объект Time, который позволяет получать в том числе и системное время: конструктор
по умолчанию создает объект Time с системными датой и временем. Кроме того,
объект Time может пригодиться в решении многих других задач — например, в сравнении
объектов Date и Time, преобразовании времени в различные форматы и сохранении
данных объекта Time для последующего использования.
Созданные
объекты Time изменять нельзя. Однако класс Time предоставляет методы для создания
новых объектов с временным смещением (например, addSeconds, addMinutes, addHours,
addDays и addYears). Существуют и методы для получения лишь какого-то одного
из свойств объекта Time: секунд, минут, часов, дней и т. д.
Объект Time
в WFC хранит время как число стонаносекундных единиц, отсчитываемое от 1 января
100 г. нашей эры. Максимальное значение, которое может хранить WFC-объект Time,
— 31 декабря 10 000 г. нашей эры. Преобразование WFC-объектов Time в другие
форматы (String, Variant, SYSTEMTIME и т. п.) может вызвать потерю точности,
да и не все форматы способны хранить столь большой диапазон значений. Не путайте
класс Time с другим классом пакета com.ms.wfc.app — Timer. Последний на самом
деле является элементом управления; единственная причина, по которой он не включен
в пакет com.ms.wfc.ui, — отсутствие у него пользовательского интерфейса.
Операции
drag-and-drop и работа с буфером обмена
Поддержка
drag-and-drop в WFC основана на модели Win32 (OLE), которая ускоряет копирование
и вставку данных. А используя буфер обмена, приходится выполнить несколько операций:
выделить данные в источнике, выбрать из контекстного меню команду Cut или Сору,
переключиться на приемник (файл, окно или приложение) и выбрать из контекстного
меню команду Paste.
Поддержка
drag-and-drop избавляет от необходимости пользоваться контекстным меню. Вместо
этого предлагается такой сценарий: Вы нажимаете левую кнопку мыши для захвата
данных, выделенных в источнике, и отпускаете ее после перемещения данных в приемник
(на мишень). Операции drag-and-drop позволяют переносить любые данные, которые
можно поместить в буфер обмена; следовательно, drag-and-drop работает с теми
же форматами данных, что и буфер обмена. Форматы определяют вид данных: текст,
растровое изображение, HTML, звук (WAV) и т. д. Класс com.ms.wfc.app.DataFormats
содержит поля, относящиеся к каждому формату данных, используемому буфером обмена.
Имена этих полей (например, CF_TEXT) связаны непосредственно с именами Win32-KOHCTАHT.
Данные для
операций drag-and-drop и с буфером обмена хранятся в классе DataObject (пакет
com.ms.wfc.app), который реализует интерфейс IDataObject. Последний определяет
методы для записи и чтения данных, получения списка форматов в объекте данных
и запроса о наличии определенного формата.
Чтобы программно
обмениваться данными с .буфером, используйте статические методы класса com.ms.wfc.app.Clipboard.
Метод Clipboard.setDataObject принимает IDataObject и помещает'его в буфер обмена
Windows, a Clipboard.getDataObject возвращает IDataObject из этого буфера. Приемник
должен поддерживать формат данных, помещенных в буфер обмена. Для проверки запросите
объект данных методом IDataObject. getDataPresent, передав формат, который может
принять мишень, — getDataPresent возвращает true, если такой тип данных имеется.
Реализация
источника в операции drag-and-drop
Операция drag-and-drop для любого WFC-элемента (основанного на com.ms.wfc.ui.Control) начинается с вызова метода Control.doOragDrop. Обычно это делается в ответ на перемещение пользователем мыши при нажатой левой кнопке.:Поэтому в обработчик события mouseMove помещается код, проверяющий, не нажата ли левая кнопка (нажатие этой кнопки — сигнал к началу операции перетаскивания). Пример такого обработчика для списка с именами файлов:
private void
listFiles_mouseMove(Object source, MouseEvent e)
{
// если нажата левая кнопка, начинаем операцию drag-and-drop
if(this.getMouseButtons()==MouseButton.LEFT)
{
String dafa = (String)listFiles.getSelectedltem();
HstFiles.doDragDrop(data,
DragDropEf feet. ALL);
} }
Метод doDragDrop принимает подлежащие передаче данные и объект класса com.ms.wfc.ui.DragDropEffect. Класс DragDropEffect содержит константы, которые можно комбинировать побитовой операцией «OR» для выполнения операции drag-and-drop в требуемом режиме.
Метод DragDropEffect | Описание |
COPY | После передачи данные не удаляются из источника. |
MOVE | После передачи данные удаляются из источника. |
SCROLL | После передачи данные прокручиваются в окне мишени. |
ALL | После передачи данные удаляются из источника и прокручиваются в окне мишени (то же, что COPY | MOVE | SCROLL). |
NONE | Никакие операции не выполняются. |
Мишень, принимающая
данные в результате операции drag-and-drop, получает событие dragDrop — оно
содержит объект DragDropEffect, что позволяет легко определить режим операции.
Реализация
мишени в операции drag-and-drop
Часть операции
drag-and-drop, когда данные «опускаются» на мишень, обрабатывается
как событие. Класс Control создает инфраструктуру обработчиков для следующих
событий drag-and-drop: dragDrop, dragEnter, dragLeave и dragOvcr. Чтобы назначить
этим событиям обработчики, используйте следующие методы.
Метод Control |
Описание |
||
addOnDragDrop
addOnDragEnter
addOnDragLeave addOnDragOver |
Задает обработчик для данных, отпускаемых в . Вашем элементе управления или окне (при отпускании левой кнопки мыши). Задает обработчик, вызываемый, когда в процессе перетаскивания данных в окно попадает курсор. Задает обработчик, вызываемый, когда в процессе перетаскивания данных курсор покидает окно. Задает обработчик,
вызываемый, когда в процессе перетаскивания данных в окне перемещается
курсор. |
||
У каждого
addOn-метода есть парный removeOn-метод, удаляющий обработчик события. Как и
все addOn- и removeOn-методы в WFC, добавляющие и удаляющие обработчики, эти
методы принимают делегат (в данном случае — DragEventHandler), имя которого
формируется по имени метода-обработчика. Например, следующая строка добавляет
метод txtFile_dragDrop как обработчик события dragDrop:
txtFile.addOnDragDrop(new
DragEventHandler(this.txtFile_dragDrop));
В операции
drag-and-drop чаще всего обрабатывается событие drag-Drop. Но независимо от
конкретного события, код обработчика должен делать минимум три вещи. Сначала
определить, может ли он принять данные предлагаемого формата, и, если да, то
скопировать и отобразить их (или как-то иначе уведомить пользователя о приеме
данных). Все данные и информация передаются в событии DragEvent. Среди других
полей в нем есть поле данных и поле режима. Поле DragEvent.data содержит объект,
поддерживающий интерфейс IDataObject, который предусматривает методы для получения
данных и их форматов, а также запроса о наличии определенного формата.
Поэтому обработчик
должен сначала вызвать метод-Drag Event, data, getDataPresent, передав ему формат,
который он распознает, а потом определить, присутствует ли такой тип данных.
Если да, обработчик может вызвать метод DragEvent.data.getData.(передав ему
этот тип данных) и получить сами данные. Способ отображения данных определяется
элементом управления. Следующий пример демонстрирует обработчик события dragDrop
для поля; он отображает текст, перемещенный мышью в это поле.
private void txtFile_dragDrop(Object source, DragEvent e)
{
// если в объекте
содержится текст, выводим его в поле
if (e.data.getDataPresent(DataFormats.CF_TEXT))
{
String filename
= (String)e.data.getData(DataFormats.CF_TEXT);
txtFile.setText(filename);
} }
Использование
Java-потоков с WFC
Java не накладывает
никаких ограничений на потоки. Это означает, что любой объект в любой момент
может вызвать любой другой объект из любого потока. При написании объектов обращайте
особое внимание на то, чтобы их методы были атомарными (atomic) и безопасными
для потока (реентерабельными).
Некоторые
классы активно используют преимущества свободных потоков, и в WFC предусмотрен
блокирующий код, позволяющий сделать их объекты безопасными для потока. Вот
эти классы:
С другой
стороны, любой объект, производный от com.ms.wfc.ui.Control, использует модель
разделенных потоков (apartment threading model), так как с каждым элементом
управления связано Win32-OKHO. Кроме того, большинство других объектов пакета
com.ms.wfc.ui не синхронизируется, и их тоже следует считать использующими модель
разделенных потоков. По сходным причинам пакеты com.ms.wfc.io, com.ms.wfc.html
и com.ms.wfc.util нельзя считать реентерабельными.
Смешение
моделей Java- и Win 32-потоков
WFC обращается
к Win32-конструкциям (например, окнам) из Java-объектов. Диспетчер Win32-OKOH
придерживается модели разделенных потоков, a Windows при необходимости автоматически
выполняет мар-шалинг вызовов из одного потока в другой. Когда объект, построенный
на модели свободных потоков, вызывает объект, использующий модель разделенных
потоков, необходим маршалинг этого вызова в поток (apartment thread) объекта.
И пока он не обработает запрос, свободный поток блокируется. Любые другие вызовы
его объекта из объектов свободных потоков блокируются, пока не освободится поток,
первого. В конечном счете это может привести в тупик — к взаимной блокировке.
Каким же образом Java-объекты, построенные на модели свободных потоков, работают
с WFC-элементами? Когда дело доходит до межпотоковых операций, WFC перекладывает
ответственность за соответствующий запрос на программиста — он должен разработать
алгоритм так, чтобы предотвратить возникновение тупиковых ситуаций. Для этого
из потока элемента управления можно вызвать делегат, который в свою очередь
вызовет указанный в нем метод.
Для вызова
нужного делегата из потока, создавшего описатель окна элемента управления и
содержащего цикл обработки сообщений, применяйте методы Control, invoke или
Control. invokeAsync. Очень важно использовать именно поток элемента управления
— особенно на случай, если элементу управления по какой-то причине понадобится
заново создать описатель своего окна. Метод invoke заставляет поток обратиться
к заданному методу обратного вызова и ждать возврата управления от него, a invokeAsync
приводит к тому же, но возврата управления не ждет. Все исключения в вызываемом
потоке, в обоих случаях передаются элементу управления, которому принадлежит
этот поток. Кроме того, объект Control.createGraphics позволяет выполнять фоновые
операции закраски и анимации в объекте ui.Graphics. Хотя объект Graphics использует
модель разделенных потоков, createGraphics полностью отвечает модели свободных
потоков. А это дает возможность одному потоку создать графический объект для
элемента управления из другого потока.
Свободные
потоки можно создавать по стандартной в Java методике: реализуя интерфейс java.lang.Runnable.
Приведенный
ниже пример демонстрирует класс, реализующий Run-nable и принимающий два элемента
управления (линейку с ползунком и метку) как параметры своего конструктора.
В методе run потока он переходит в поток первого элемента управления, вызывая
его метод invokeAsync. Этот метод передает делегат с именем tDelegate (экземпляр
com.ms.app.Methodlnvoker), который определяет метод обратного вызова tCallBack.
Внутри последнего метода поток элемента управления может безопасно манипулировать
свойствами линейки с ползунком — в данном случае он изменяет стиль разметки
шкалы. Это приводит к необходимости повторного создания описателя окна элемента.
Если пропустить показанный здесь переход в поток элемента, последний будет создан
в новом потоке, и уже больше не примет никаких сообщений, а значит, перестанет
отвечать.
import com.ms.wfc.app.*;
import com.ms.wfc.core. *;
import com.ms.wfc.ui.*;
/.*
* Runnable -
интерфейс, реализуемый при создании нового Java-потока */
public class
RunnableClass implements Runnable
{
final int SLEEP
= 500; Label 1; TrackBar tb;
/**
* это поток
для нашего класса
*/ Thread thread;
/*,
* создаем специальный
делегат, чтобы WFC мог вызывать его из потока
* элемента управления
*/ Methodlnvoker
tDelegate = new Methodlnvoker(tCallback);
/**
* создаем новый
Java-поток и указываем ему стартовать
* через метод
start() */
public RunnableClass
(TrackBar tb, Label 1)
{
this.l = 1;
this.tb = tb;
thread = new Thread(this, "RunnableClass thread");
thread.start();
}
public void
run()
{
while (true)
{ /**
* вызываем заданный
метод из потока метки
*/
tb.invokeAsync
(tCallback);
try
{
Thread.sleep
(SLEEP);
}
catch (InterruptedException
e)
{ } }
}
int nCount =
0;
int nTickStyles[]
= {TickStyle.BOTH,
TickStyle.BOTTOMRIGHT,
TickStyle.NONE,
TickStyle.TOPLEFT};
/**
* этот код выполняется
в потоке линейки с ползунком */
private void
tCallback() {
int nlndex =
nCount % (nTiokStyles.length);
l.setText ("hello
from tCallBack: " + nCount);
tb.setTickStyle
(nTickStyles [nlndex]);
nCount++;
int nValue = tb.getValue();
if (nValue >=
tb.getMaximum())
tb.setValue(0);
else
tb.setValue (nValue + 1);
}
public void stopThread()
{
thread.stop();
} }
Завершение
потока в данном случае сводится лишь к вызову его метода stop. Здесь класс Form,
создающий объект RunnableClass, вызывает при уничтожении этого объекта его метод
stopThread. Сказанное иллюстрирует следующий код.
import RunnableClass;
public class
SimpleRunnable extends Form {
/**
* это класс,
реализующий интерфейс Runnable */
RunnableClass
runnableClass;
public SimpleRunnable()
{
// требуется
для поддержки Forms Designer initForm();
runnableClass
= new RunnableClass (tb, 1); }
public void
dispose()
{
runnableClass.stopThread();
super.dlspose();
components.d±spose();
}
Container components = new Container();
Edit edescription = new Edit();
TrackBar tb = new TrackBar();
Label 1 = new
Label();
private void
initForm() {
// код, инициализирующий
элементы управления, опущен... }
public static
void main(String args[])
{
Application.
run(new SimpleRunnable());
} }
В качестве
альтернативы новый поток приложения можно создать методом Application. createThread
и обойтись без реализации Java-интерфейса Runnable или расширения Java.lang.Thread.
Этот метод принимает делегат как параметр (в качестве делегата часто используется
Met-hodlnvoker, но вместо него допустим любой другой). Тогда всю логику можно
свести в один класс — обычно класс приложения, основанный на Form. Следующий
пример демонстрирует этот вариант.
import com.ms.wfc.app.*;
import com.ms.wfc.core.*;
import com.ms.wfc.ui.*;
public class
SimpleAppThread extends Form
{
final int SLEEP
= 700;
Thread thread;
// указываем
контекст потока, в котором запускается метод
Methodlnvoker
cbDelegate = new Methodlnvoker(cbThrdCallback);
public SimpleAppThread()
{
initForm();
/.„
* Здесь создается
новый поток и в нем запускается methodlnvoker.
* Возвращаемый
объект потока необходим, чтобы мы могли завершить
* поток при
закрытии (уничтожении) формы. Заметьте, что метод
* thread.start()
вызывается автоматически.
*/
thread = Application.createThread
(new Methodlnvoker(this.methodlnvoker));
}
private void
methodlnvoker() {
while (true)
{
// cbThrdCallback
вызывается в потоке элемента управления "флажок"
cb.invoke (cbDelegate);
try
{
Thread.sleep
(SLEEP); }
catch (InterruptedException e) { } }
}
int nCount =
0; /..
* метод обратного
вызова потока, который устанавливает свойство
* alignment
флажка; его код должен выполняться в потоке флажка */
private void
cbThrdCallback() {
cb.setText ("threadCallback loop: " + nCount++);
if (nCount %
2 == 0)
cb.setTextAlign (LeftRightAlignment.LEFT);
else
cb.setTextAlign (LeftRightAlignment.RIGHT);
}
public void
dispose() {
thread.stop();
super. disposeO;
components.dispose();
}
private void cbSuspend_click(Object sender, Event e)
{
if (cbSuspend.getChecked())
{
cbSuspend.setText ("press to resume thread");
thread.suspendO;
}
else {
cbSuspend.setText
("press to suspend thread");
thread.resume();
}
}
Container components = new Container();
CheckBox cbSuspend = new CheckBox();
CheckBox cb =
new CheckBox();
private void
initForm() {
// код, инициализирующий
элементы управления, опущен... }
public static
void main(String args[])
{
Application.
run(new SimpleAppThread());
} }
Здесь поток
останавливается вызовом его метода stop из метода dispose формы. Поток не находится
в отдельном классе, поэтому его методы можно вызывать напрямую из класса, основанного
на Form. У класса Application есть также метод Application.exitThread, который
прекращает цикл обработки сообщений потока и закрывает его окна, но не завершает
и не останавливает сам поток. А метод Application, exit прекращает циклы обработки
сообщений всех потоков и закрывает все окна.
Использование
локальной памяти потока
Поддержку
локальной памяти потока (Thread Local Storage, TLS) обеспечивает WFC-класс Application.
Каждый поток может выделить TLS-область для хранения только своих данных. Вызов
метода Application. allocThreadStorage возвращает индекс этой области. Чтобы
записать значение в TLS, вызовите метод Application.setThreadStorage, передав
индекс области и записываемое значение. А чтобы получить значение, вызовите
метод Application. getThreadStorage. По окончании не забудьте освободить все
занятые участки TLS-памяти потока, вызвав Application.freeThreadStorage.
Обработка
исключений в потоках
В пакете
com.ms.wfc.app содержится класс ThreadExceptionDialog, автоматически отображаемый
при возникновении в потоке необрабатываемого исключения. Вы можете взять исключения
под свой контроль, вызвав метод Application. addOnThreadException и назначив
собственный обработчик исключений. Метод addOnThreadException принимает в качестве
параметра делегат ThreadExceptionEventHandler, конструируемый из Вашего метода
обработки события и класса ThreadException-Event.
Обычно обработчик исключений потока запрашивает поле исключений переданного ему объекта ThreadExceptionEvent и таким образом определяет, что делать. Из этого обработчика можно запустить ThreadExceptionDialog и получить от него результаты так же, как, и в случае любых других диалоговых окон WFC. Иначе говоря, Вы запускаете диалоговое окно методом Form. showDialog и извлекаете результаты из полей класса com.ms.wfc.ui.DialogResult.
1. Электромагнитная волна (в религиозной терминологии релятивизма - "свет") имеет строго постоянную скорость 300 тыс.км/с, абсурдно не отсчитываемую ни от чего. Реально ЭМ-волны имеют разную скорость в веществе (например, ~200 тыс км/с в стекле и ~3 млн. км/с в поверхностных слоях металлов, разную скорость в эфире (см. статью "Температура эфира и красные смещения"), разную скорость для разных частот (см. статью "О скорости ЭМ-волн")
2. В релятивизме "свет" есть мифическое явление само по себе, а не физическая волна, являющаяся волнением определенной физической среды. Релятивистский "свет" - это волнение ничего в ничем. У него нет среды-носителя колебаний.
3. В релятивизме возможны манипуляции со временем (замедление), поэтому там нарушаются основополагающие для любой науки принцип причинности и принцип строгой логичности. В релятивизме при скорости света время останавливается (поэтому в нем абсурдно говорить о частоте фотона). В релятивизме возможны такие насилия над разумом, как утверждение о взаимном превышении возраста близнецов, движущихся с субсветовой скоростью, и прочие издевательства над логикой, присущие любой религии.
4. В гравитационном релятивизме (ОТО) вопреки наблюдаемым фактам утверждается об угловом отклонении ЭМ-волн в пустом пространстве под действием гравитации. Однако астрономам известно, что свет от затменных двойных звезд не подвержен такому отклонению, а те "подтверждающие теорию Эйнштейна факты", которые якобы наблюдались А. Эддингтоном в 1919 году в отношении Солнца, являются фальсификацией. Подробнее читайте в FAQ по эфирной физике.
|
![]() |