Статьи

Создание облака тегов с помощью Google Web Toolkit

Некоторое время назад я прочитал учебник Дэна Веллмана , в котором описаны шаги, необходимые для создания аккуратного Облака тегов . Пример Дэна в основном опирался на инфраструктуру jQuery для запроса данных и конструирования элементов пользовательского интерфейса. Я решил написать его учебник снова и снова с двумя исключениями использования GWT вместо jQuery и другого метода выбора вариаций размера шрифта.


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

Облако тегов

Мы собираемся использовать последнюю версию GWT (в настоящее время 1.5) и работать с MySQL и
PHP как наш бэкэнд для запроса данных JSON. Как и в учебнике Дэна, я тоже предполагаю, что вы уже
знакомы с вставкой в ​​базу данных. Код PHP в этой статье будет просто
рассмотрим, как запрашивать данные из базы данных и отправлять результат обратно в формате JSON. Вы
следует ожидать, чтобы узнать:

  • как GWT может запрашивать данные у PHP-сервера и обрабатывать ответ с помощью обратных вызовов
  • как использовать PHP для отправки данных JSON обратно клиенту GWT
  • как анализировать данные JSON в GWT
  • как создать и разместить пару виджетов пользовательского интерфейса GWT
  • как стилизовать виджеты GWT с помощью CSS
  • как правильно выбрать размер шрифта для облака тегов

Я использовал плагин Cypal Studio GWT для Eclipse, чтобы создать этот проект. Если вы уже используете
эта комбинация, вы должны иметь возможность скачать и открыть этот проект в Eclipse.
В противном случае здесь есть ссылка для получения дополнительной информации.

Хотя отладчик GWT не совсем отлаживает JavaScript, использование Eclipse с плагином Cypal Studio позволяет отлаживать код GWT внутри Eclipse IDE, что лучше, чем многие другие отладчики JavaScript.

По умолчанию, как часть скрипта, который генерирует пустой проект GWT, вы получите файл HTML
что более или менее похоже на код ниже. Возможно, вам придется исправить путь к файлу JavaScript
в соответствии с настройками вашего сервера.

01
02
03
04
05
06
07
08
09
10
11
<html>
    <head>
        <title>Main</title>
    </head>
    <body>
        <script language=»javascript» src=»in.cypal.studio.gwt.samples.TagCloud.nocache.js»></script>
     
        <!— This div is added to allow center align the page even in IE —>
        <div id=»wrapper» style=»text-align:center»></div>
    </body>
</html>

Наше облако тегов появится в центре браузера. Так как выравнивание по центру страниц с использованием CSS не работает должным образом в IE, мы добавляем новый элемент DIV и устанавливаем его id в «обертку». Это все, что нам нужно для начала. По мере продвижения в этом руководстве мы еще раз посетим этот документ, чтобы добавить больше, но сейчас давайте продолжим.

Мы начнем с изменения onModuleLoad() класса MainEntryPoint,
так как это метод, который GWT использует, чтобы начать выполнение нашего кода. Мы хотим начать с
запрашивая данные (имена тегов и их частоты) из серверной части PHP и MySQL.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public void getTagData(){
     
    // you may need to change the URL according to your server setup
    String url = «/wmGetTags.php»;
     
    RequestBuilder requestBuilder = new RequestBuilder(RequestBuilder.GET,url);
    try{
        requestBuilder.sendRequest(null, new RequestCallback() {
            public void onResponseReceived(Request request, Response response){
                if (response.getStatusCode() == 200){
                    // handler code
                }
            }
             
            public void onError(Request request, Throwable exception){
                throw new UnsupportedOperationException(«Not supported yet.»);
            }
        });
    } catch (Exception e){
        e.printStackTrace();
    }
}
 
public void onModuleLoad() {
 
}

Мы определили новый метод getTagData() в котором RequestBuilder
type создается для вызова PHP-скрипта wmGetTags в серверной части. Обратите внимание, как
sendRequest() принимает параметр обратного вызова, который обрабатывает ответ, как только он
возвращается

При создании нового RequestCallback мы должны реализовать
onResponseReceived() и onError() для обработки каждого случая. Обратите внимание, как в
onResponseReceived() , мы проверяем код состояния ответа. Это потому что
в течение жизненного цикла запроса этот метод может вызываться несколько раз
браузер, хотя он не может быть полностью выполнен. Запрос завершается только тогда, когда
код состояния равен 200. Мы проверяем код состояния с помощью getStatusCode()
метод.

Далее мы создадим виджет FlowPanel и вставим его в DIV-оболочку.
Библиотека виджетов GWT предоставляет множество различных видов панелей для различного использования; однако,
FlowPanel — это виджет, который позволяет содержать в себе более одного дочернего виджета. Эта
свойство делает его подходящим виджетом для облака тегов. То, что мы делаем здесь, это создание
держа контейнер для всех тегов, которые мы должны показать.

1
2
3
4
5
public void onModuleLoad() {
    getTagData();
    flowPanel = new FlowPanel();
    RootPanel.get(«wrapper»).add(flowPanel);
}

Эта часть довольно проста. Давайте создадим новый скрипт PHP и назовем его wmGetTags.php.
Во-первых, мы должны создать соединение с базой данных с помощью функции mysql_connect() ,
затем выполните запрос SELECT к таблице, содержащей имена тегов и их вхождения.
Наконец, когда запрос завершен, мы используем «For Loop» для генерации ответа в формате JSON.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<?php
     
  //connection information
  $host = «localhost»;
  $user = «root»;
  $password = «your_password_here»;
  $database = «tagcloud»;
  
  //make connection
  $server = mysql_connect($host, $user, $password);
  $connection = mysql_select_db($database, $server);
  
  //query the database
  $query = mysql_query(«SELECT * FROM tags»);
  
  //start json object
  $json = «([«;
  
  //loop through and return results
  for ($x = 0; $x < mysql_num_rows($query); $x++) {
    $row = mysql_fetch_assoc($query);
   
    //continue json object
    $json .= «{tag:'» .
   
    //add comma if not last row, closing brackets if is
    if ($x < mysql_num_rows($query) -1)
      $json .= «,»;
    else
      $json .= «])»;
  }
  
  //return JSON with GET for JSONP callback
  $response = $_GET[«callback»] .
  echo $response;
  //close connection
  mysql_close($server);
 
?>

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

1
([{tag:’Gmail’,frequency:21},{tag:’Web’,frequency:19},{tag:’Salesforce’,frequency:66},{tag:’Amazon’,frequency:17}])

Выше приведен пример ответа JSON. Чтобы быть точным, это будет проанализировано в массив с
каждый из его четырех индексов содержит объект с двумя полями. Первое поле «тег» содержит
имя тега, в то время как второе поле «частота» содержит количество вхождений. Выполнение того, что мы до сих пор кодировали, приведет к созданию пустой страницы, однако проверка связи браузера с помощью вкладки «Net» в Firebug должна показать нам вывод сценария PHP выше, как показано на рисунке ниже.

На этом этапе мы должны определить процедуру, которая будет анализировать ответ, полученный от серверной части, и создать пользовательский интерфейс для отображения тегов в облаке. Так как типы HTTP и JSON содержатся в отдельных модулях GWT, мы должны добавить следующие теги <наследники <inherits> в наш <имя модуля> .gwt.xml, чтобы гарантировать, что код, необходимый для анализа JSON, включен для среды выполнения:

Вы можете узнать больше о модулях GWT здесь .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
public void getTagData(){
 
    // …
 
    try{
        requestBuilder.sendRequest(null, new RequestCallback() {
            public void onResponseReceived(Request request, Response response){
                if (response.getStatusCode() == 200){
                    handleGetTags(response.getText());
                }
            }
         
            public void onError(Request request, Throwable exception){
                throw new UnsupportedOperationException(«Not supported yet.»);
            }
        });
    } catch (Exception e){
            e.printStackTrace();
    }
}

Теперь мы должны вызвать handleGetTags() когда код состояния экземпляра Response равен 200, как показано в приведенном выше коде. Метод handleGetTags() фактически обрабатывает данные JSON.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
public void handleGetTags(String jsonText){
  
    JSONObject jsonObject;
    JSONString tagName;
    JSONNumber tagFreq;
    int frequency;
    String realTagName;
     
    JSONValue jsonValue = JSONParser.parse(jsonText);
    JSONArray jsonArray = jsonValue.isArray();
     
    if (jsonArray != null){
        for (int i = 0; i < jsonArray.size(); i++){
             jsonObject = (JSONObject)jsonArray.get(i);
             tagName = jsonObject.get(«tag» ).isString();
             tagFreq = jsonObject.get(«frequency»).isNumber();
             frequency = (int)tagFreq.doubleValue();
             Hyperlink tagLink = new Hyperlink(tagName.stringValue(),tagName.stringValue());
             flowPanel.add(tagLink);
        }
    }
}

Вся связь XMLHTTPRequest между клиентом и серверной частью происходит через обычный текст. Так даже
хотя внутренний ответ отформатирован в формате JSON, его еще предстоит преобразовать / разобрать в реальный
Объекты JavaScript, с которыми мы можем взаимодействовать, как показано ниже.

1
2
JSONValue jsonValue = JSONParser.parse(jsonText);
JSONArray jsonArray = jsonValue.isArray();

Класс JSONParser предоставляет статический метод parse() который принимает строку
Параметр и возвращает объект JSONValue которым мы затем можем взаимодействовать. Как мы
ранее установленный, наш скрипт PHP вернет структуру массива, содержащую несколько
объекты, инкапсулирующие данные, связанные с тегами. Чтобы получить дескриптор этого массива, мы должны использовать
метод isArray() .

01
02
03
04
05
06
07
08
09
10
11
for (int i = 0; i < jsonArray.size(); i++){
     
    jsonObject = (JSONObject)jsonArray.get(i);
    tagName = jsonObject.get(«tag» ).isString();
    tagFreq = jsonObject.get(«frequency»).isNumber();
    frequency = (int)tagFreq.doubleValue();
    realTagName = tagName.stringValue();
  
    //…
   
}

Приведенный выше код будет обращаться к встроенному объекту в каждом индексе массива, чтобы получить доступ к
фактические данные тега. Таким образом, в каждой итерации цикла содержимое текущего индекса возвращается как JSONObject . Каждый извлеченный JSONObject должен иметь два поля: тег и частоту.
Мы используем метод get() класса JSONObject для извлечения этих полей.

1
2
Hyperlink tagLink = new Hyperlink(tagName.stringValue(),null);
flowPanel.add(tagLink);

Затем мы должны внедрить имена тегов в облачный интерфейс. Помните FlowPanel, что мы
создано ранее? Теперь мы хотим создать виджеты гиперссылок и вставить их в наш поток.
Панель — это то, что делают эти две строки выше. Если мы запустим проект, наше облако тегов должно
выглядеть так:

На данный момент у нас есть то, что выглядит как список ссылок, но пока ничего похожего на облако тегов.
GWT позволяет разработчику точно контролировать способ визуализации каждого виджета, позволяя
разработчик, чтобы предоставить свой собственный CSS. Вот что мы должны сделать, чтобы наше облако тегов
подтяжка лица. Давайте вернемся к нашему HTML снова.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<html>
    <head>
        <title>Main</title>
        <style>
            * {
                padding : 0;
                margin : 0;
                font-family : «Lucida Grande»,»Lucida Sans Unicode»,Arial,Verdana,sans-serif;
                overflow : hidden;
            }
             
            .cloudWrap {
                text-align : center;
                margin : 50px auto;
                background-color : #333;
                padding : 10px;
                width : 400px;
                line-height : 1.5em;
            }
             
            .cloudTags {
                float : left;
                padding : 5px;
            }
             
            .cloudTags a {
                color : #FFFFFF;
                text-decoration : none;
            }
        </style>
    </head>
    <body>
        <script language=»javascript» src=»in.cypal.studio.gwt.samples.TagCloud.nocache.js»></script>
 
        <!— This div is added to allow center align the page even in IE —>
        <div id=»wrapper» style=»text-align:center»></div>
    </body>
</html>

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

Теперь вы можете задать вопрос: «Но как нам сказать GWT, какой класс CSS использовать для
какой виджет? «Ну, это легко. Каждый виджет из библиотеки GWT UI предоставляет метод
называется setStylePrimaryName() который принимает имя класса CSS, который вы хотите
назначить виджету. Теперь мы должны вернуться и назначить правильные классы CSS для
наши виджеты. Есть два места, где нам нужно это сделать. Первая — это FlowPanel, которая
содержит теги.

1
2
3
4
5
6
public void onModuleLoad() {
    getTagData();
    flowPanel = new FlowPanel();
    flowPanel.setStylePrimaryName(«cloudWrap»);
    RootPanel.get().add(flowPanel);
}

Второй — после добавления гиперссылки на FlowPanel.

1
2
3
Hyperlink tagLink = new Hyperlink(tagName.stringValue(),null);
flowPanel.add(tagLink);
tagLink.setStylePrimaryName(«cloudTags»);

Теперь у нас должно быть что-то похожее на это:

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

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

Итак, сначала мы должны найти теги с наименьшим и наибольшим числом частот и
запомните их в переменных класса minFrequency и maxFrequency . У нас также есть
определил самый маленький и самый большой размер шрифта, установив MIN_FONT_SIZE и MAX_FONT_SIZE
конечные переменные.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
int maxFrequency = 0;
int minFrequency = 600000000;
final int MIN_FONT_SIZE = 5;
final int MAX_FONT_SIZE = 25;
 
public void handleGetTags(String jsonText){
 
    // …
 
    for (int i = 0; i < jsonArray.size(); i++){
        jsonObject = (JSONObject)jsonArray.get(i);
        tagFreq = jsonObject.get(«frequency»).isNumber();
        frequency = (int)tagFreq.doubleValue();
        if (minFrequency > frequency)
            minFrequency = frequency;
        if (maxFrequency < frequency)
            maxFrequency = frequency;
    }
     
    // …
}

Далее мы определяем метод getLabelSize() который принимает частоту для
текущий тег и возвращает font-size CSS для этого тега.

1
2
3
4
5
public String getLabelSize(int frequency){
    double weight = (Math.log(frequency) — Math.log(minFrequency)) / (Math.log(maxFrequency) — Math.log(minFrequency));
    int fontSize = MIN_FONT_SIZE + (int)Math.round((MAX_FONT_SIZE — MIN_FONT_SIZE) * weight);
    return Integer.toString(fontSize) + «pt»;
}

Теперь мы должны индивидуально назначить размер шрифта CSS для каждого виджета гиперссылки, к которому мы добавляем
FlowPanel. Для этого мы должны получить указатель на объект Style гиперссылки
элемент и установите свойство fontSize как показано ниже:

1
2
Style linkStyle = tagLink.getElement().getStyle();
linkStyle.setProperty(«fontSize»,getLabelSize(frequency));

И наш файл MainEntryPoint.java должен выглядеть так:

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
package org.yournamehere.client;
 
import com.google.gwt.http.client.Response;
import com.google.gwt.http.client.Request;
import com.google.gwt.http.client.RequestBuilder;
import com.google.gwt.http.client.RequestCallback;
 
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Style;
import com.google.gwt.json.client.*;
 
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.Hyperlink;
import com.google.gwt.user.client.ui.RootPanel;
 
public class MainEntryPoint implements EntryPoint {
 
    FlowPanel flowPanel = null;
    int maxFrequency = 0;
    int minFrequency = 600000000;
 
    final int MIN_FONT_SIZE = 5;
    final int MAX_FONT_SIZE = 25;
 
    public void onModuleLoad() {
 
        getTagData();
 
        flowPanel = new FlowPanel();
        flowPanel.setStylePrimaryName(«cloudWrap»);
        RootPanel.get(«wrapper»).add(flowPanel);
    }
 
    public void getTagData(){
 
        String url = «/wmGetTags.php»;
        RequestBuilder requestBuilder = new RequestBuilder(RequestBuilder.GET, url);
 
        try{
            requestBuilder.sendRequest(null, new RequestCallback() {
 
                public void onResponseReceived(Request request, Response response) {
 
                    if (response.getStatusCode() == 200)
                        handleGetTags(response.getText());
                }
 
                public void onError(Request request, Throwable exception) {
                    throw new UnsupportedOperationException(«Not supported yet.»);
                }
            });
        } catch (Exception e){
            e.printStackTrace();
        }
    }
 
 
    public void handleGetTags(String jsonText){
 
        JSONValue jsonValue = JSONParser.parse(jsonText);
        JSONArray jsonArray = jsonValue.isArray();
 
        JSONObject jsonObject;
        JSONString tagName;
        JSONNumber tagFreq;
 
        int frequency;
 
        if (jsonArray != null){
 
            for (int i = 0; i < jsonArray.size(); i++){
 
                jsonObject = (JSONObject)jsonArray.get(i);
                tagFreq = jsonObject.get(«frequency»).isNumber();
 
                frequency = (int)tagFreq.doubleValue();
 
                if (minFrequency > frequency)
                    minFrequency = frequency;
 
                if (maxFrequency < frequency)
                    maxFrequency = frequency;
            }
 
            for (int i = 0; i < jsonArray.size(); i++){
 
                jsonObject = (JSONObject)jsonArray.get(i);
 
                tagName = jsonObject.get(«tag» ).isString();
                tagFreq = jsonObject.get(«frequency»).isNumber();
 
                frequency = (int)tagFreq.doubleValue();
 
                Hyperlink tagLink = new Hyperlink(tagName.stringValue(),null);
                tagLink.setStylePrimaryName(«cloudTags»);
 
                Style linkStyle = tagLink.getElement().getStyle();
                linkStyle.setProperty(«fontSize»,getLabelSize(frequency));
 
                flowPanel.add(tagLink);
            }
        }
    }
 
    public String getLabelSize(int frequency){
        double weight = (Math.log(frequency) — Math.log(minFrequency)) / (Math.log(maxFrequency) — Math.log(minFrequency));
        int fontSize = MIN_FONT_SIZE + (int)Math.round((MAX_FONT_SIZE — MIN_FONT_SIZE) * weight);
        return Integer.toString(fontSize) + «pt»;
    }
}

Этот урок продемонстрировал простые шаги, необходимые для создания облака тегов,
показывая, как GWT может подключаться к PHP и MySQL для получения данных.
Он также показал, как создавать GWT-виджеты и стилизовать их с помощью знакомых методов CSS. Надеюсь, вам понравилось!