Статьи

Полет с Гриффоном

Новые рамки приходят и уходят. Они склонны стоять и падать в зависимости от того, начинают ли люди экспериментировать с ними. Новая структура Griffon вряд ли скоро упадет, так как большое и энергичное сообщество Groovy / Grails заинтересовано в нем.

Однако, чтобы внести свой вклад, я собираюсь начать играть с этим сам. Создатели Griffon были достаточно умны, чтобы включить некоторые образцы в свое первоначальное распределение, поэтому первое приложение, которое я сделал, — это небольшая часть одного из этих примеров. Несмотря на небольшой размер, затрагиваются многие основные концепции Griffon, и конечный результат не отличается от любого другого приложения Swing. Это JXTree внутри JScrollPane внутри JPanel, с JToolBar, содержащим две кнопки, которые раскрывают / сворачивают узлы дерева, содержащие текст узлов в Javadoc NetBeans :

Однако у меня также есть апплет (и приложение JNLP), созданный с помощью «griffon run-app», т. Е. Та же команда Griffon, которая создала указанное выше приложение Swing. Апплет ведет себя идентично приложению Swing, поэтому кнопки «Свернуть все» и «Развернуть все» выполняют то, что от них ожидают, при этом общий вид апплета идентичен приложению Swing:

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

Давайте сначала посмотрим на весь код, прежде чем собирать все вместе в приложение. Поскольку Griffon поощряет структуру MVC, код будет разделен на эти три части.

Вид

Мы начнем с простого представления, которое ничего не делает:

application( title: "Griffon Demo", size: [250,300], locationByPlatform: true ) {
panel( ) {
borderLayout()
scrollPane( constraints: CENTER ) {
jxtree( id: "topics" )
}
toolBar( constraints: SOUTH ) {
button( )
button( )
}
}
}

Выше приведено полное содержимое (т. Е. Ничего более этого) в файле Groovy с именем «GriffonDemoView». Почему мы назначаем идентификатор для JXTree? Так что мы можем обратиться к нему из контроллера! Вот где мы получим содержимое JXTree и затем передадим его в представление. Groovy-код таков, что вы сможете прочитать вышесказанное невооруженным глазом и сразу же визуализировать, из чего состоит пользовательский интерфейс. Однако мы хотим, чтобы наши две кнопки могли что- то делать . Поэтому у нас будет отдельный Groovy-файл, в данном случае называемый «GriffonDemoActions», в котором будут найдены все наши действия. Тогда мы подключим их в поле зрения. Вот полное содержимое отдельного файла Groovy, где будут определены все наши действия:

actions {

action( id: 'collapseAllAction',
name: "Collapse all",
closure: controller.collapseAll,
accelerator: shortcut('C'),
mnemonic: 'C',
shortDescription: "Collapse all categories",
smallIcon: imageIcon("org/tango-project/tango-icon-theme/16x16/actions/go-first.png")
)

action( id: 'expandAllAction',
name: "Expand all",
closure: controller.expandAll,
accelerator: shortcut('E'),
mnemonic: 'E',
shortDescription: "Expand all categories",
smallIcon: imageIcon("org/tango-project/tango-icon-theme/16x16/actions/go-last.png")
)

}

Все вышеперечисленное — это то, как вы ожидаете, за исключением свойства «закрытие» каждого действия. Там вы видите ссылку на замыкание, определенное в контроллере. Вот где настоящая работа выполняется каждым действием. Мы рассмотрим эту часть, когда будем иметь дело с контроллером. Сейчас мы подключим действия к кнопкам:

build(GriffonDemoActions)

application( title: "Griffon Demo", size: [250,300], locationByPlatform: true ) {
panel( ) {
borderLayout()
scrollPane( constraints: CENTER ) {
jxtree( id: "topics" )
}
toolBar( constraints: SOUTH ) {
button( action: collapseAllAction )
button( action: expandAllAction )
}
}
}

Обратите внимание на строку 1 выше, которая включает в себя Groovy-файл «GriffonDemoActions», чтобы наши две кнопки могли ссылаться на действия, которые мы определили в этом файле. В настоящее время файл Actions должен быть явно подключен, но, возможно, в будущем «GroovyDemoActions» будет автоматически включен, говорит мне Андрес.

Контроллер

Далее контроллер.

import javax.swing.tree.*

class GriffonDemoController {

def model
def view

def expandAll = { evt ->
ViewUtils.expandTree( view.topics )
}

def collapseAll = { evt ->
ViewUtils.collapseTree( view.topics )
}


def loadPages() {
doOutside {
def contents = new DefaultMutableTreeNode("NetBeans Javadoc")
def leafNodes = new URL(model.menuUrl).text
def lastCategory = null
(leafNodes =~ /href="(([a-zA-Z-]+)\/(.+?)\.html)"/).each { match ->
def category = new DefaultMutableTreeNode(match[2])
def pageNode = new PageNode( title: match[3] )
if( lastCategory?.toString() == category.toString() ){
lastCategory.add( new DefaultMutableTreeNode(pageNode) )
}else{
lastCategory = category
category.add( new DefaultMutableTreeNode(pageNode) )
contents.add( category )
}
}
doLater {
view.topics.model = new DefaultTreeModel(contents)
}
}
}

}

Давайте посмотрим, что здесь происходит. Строки 9 и 13 ссылаются на служебный класс, который я скопировал вместе с файлом PageNode (который является простым POGO) из образца GrailsSnoop (который является частью дистрибутива Griffon). Я надеюсь, что весь класс ViewUtils будет частью API, и я написал об этом Андресу. Обратите внимание, как оно подключено к представлению через идентификатор «themes» JXTree. Точно так же строка 34 заполняет модель JXTree содержимым, построенным из строки 18–32. (Посмотрите на строку 20, чтобы увидеть ссылку на модель, где задан URL-адрес местоположения, которое будет проанализировано).

А когда вызывается метод loadPages ()? Из одного из файлов жизненного цикла, который автоматически создается при запуске «griffon create-app»; этот конкретный называется «Startup.groovy»:

def rootController = app.controllers.root
rootController.loadPages()

Следовательно, точка входа в ваше приложение находится в контроллере через файлы жизненного цикла, которые создает для вас «griffon create-app». Но как Гриффон узнает, что app.controllers.root — это GriffonDemoController? Для этого несколько файлов конфигурации находятся в «griffon-app / conf», заполняемом во время работы «griffon create-app». Один из них, называемый «Application.groovy», выделяет наши сгенерированные файлы следующим образом:

mvcGroups {
root {
model = 'GriffonDemoModel'
view = 'GriffonDemoView'
controller = 'GriffonDemoController'
}
}

Другими словами, Griffon не только явно обрабатывает ваш жизненный цикл, но также генерирует скелеты для необходимых вам классов.

Модель

Наконец, модель. В этом случае это действительно просто:

class GriffonDemoModel {
String baseUrl = "http://bits.netbeans.org/dev/javadoc/"
String menuUrl = baseUrl + "allclasses-frame.html"
}

Вот и все! Приложение заполнено. Вот структура, когда все вышеперечисленное собрано вместе:

Из перечисленных выше файлов ЕДИНСТВЕННЫМ, который вы должны были создать вручную, был «GriffonDemoActions», хотя даже он может быть включен в скрипт «create-app» в будущем. Теперь запустите «griffon run-app», и вы получите приложение Swing, апплет и приложение JNLP. Как показано в начале этой статьи, ваше приложение будет перечислять содержимое Javadoc NetBeans. Довольно круто, я считаю.