Статьи

Создание мобильного приложения для заказа пиццы Titanium: выбор топингов

В этой серии статей, состоящей из нескольких частей, я научу вас, как создавать приложение для заказа пиццы с помощью Titanium Mobile от начала до конца. В этом уроке мы создадим экран «Выберите свою начинку».

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

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
var win = Ti.UI.currentWindow;
 
//— Create the sub windows
var crusts = Ti.UI.createWindow();
var toppings = Ti.UI.createWindow();
var details = Ti.UI.createWindow();
 
//— We set the background here since this wont change
win.backgroundImage = ‘../images/bg_main.png’;
 
//— Include our clock
Ti.include(‘../includes/clock.js’);
 
//— This method will close the crusts/details window and open the toppings window
function openToppings(e)
{
    crusts.close();
 
    toppings.url = ‘toppings.js’;
    toppings.crust = e.crust;
    toppings.path = e.path;
    toppings.returnToppings = e.toppings;
     
    toppings.open();
}
 
//— The method will close the toppings window and open the crusts window
function openCrust(e)
{
    crusts.url = ‘crusts.js’;
    crusts.open();
}
 
//— Have our app listen for our custom events
Ti.App.addEventListener(‘toppings’,openToppings);
 
openCrust({});

Итак, когда вы нажмете следующую кнопку в окне openToppings() будет вызван метод openToppings() . Это приведет к:

  • Закрытие окна корок
  • Установка свойства URL в окне
  • Создание 3 пользовательских свойств
    • кора
    • путь
    • returnToppings (это свойство будет использовано в части 3)
  • Открытие окна начинки

Давайте создадим новый файл JS с именем toppings.js и сохраним его в папке main_windows . Вместо того, чтобы читать длинный абзац, объясняющий, что делает весь этот код, я просто прокомментировал код напрямую:

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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
var win = Ti.UI.currentWindow;
 
//— Scrollview for our toppings list, maximum toppings, numToppings for reference
var scrollView = Ti.UI.createScrollView();
var maxToppings = 6;
var numToppings = 0;
 
//— These are our toppings.
//— container will hold our view when it is selected
var toppings = [
    {title:’Bacon Bits’,path:’../images/toppings/bacon_bits.png’,container:null},
    {title:’Beef’,path:’../images/toppings/beef.png’,container:null},
    {title:’Grilled Chicken’,path:’../images/toppings/grilled_chicken.png’,container:null},
    {title:’Ham’,path:’../images/toppings/ham.png’,container:null},
    {title:’Italian Sausage (Crumbled)’,path:’../images/toppings/italian_sausage_crumbled.png’,container:null},
    {title:’Italian Sausage (Sliced)’,path:’../images/toppings/italian_sausage_sliced.png’,container:null},
    {title:’Jalapenos’,path:’../images/toppings/jalapenos.png’,container:null},
    {title:’Mushrooms’,path:’../images/toppings/mushrooms.png’,container:null},
    {title:’Black Olives’,path:’../images/toppings/olives_black.png’,container:null},
    {title:’Green Olives’,path:’../images/toppings/olives_green.png’,container:null},
    {title:’Red Onions’,path:’../images/toppings/onions_red.png’,container:null},
    {title:’White Onions’,path:’../images/toppings/onions_white.png’,container:null},
    {title:’Pepperoni’,path:’../images/toppings/pepperoni.png’,container:null},
    {title:’Banana Peppers’,path:’../images/toppings/peppers_banana.png’,container:null},
    {title:’Green Peppers’,path:’../images/toppings/peppers_green.png’,container:null},
    {title:’Red Peppers’,path:’../images/toppings/peppers_red.png’,container:null},
    {title:’Pineapple’,path:’../images/toppings/pineapple.png’,container:null},
    {title:’Pork’,path:’../images/toppings/pork.png’,container:null},
    {title:’Diced Tomatoes’,path:’../images/toppings/tomatoes_diced.png’,container:null},
    {title:’Marinated Tomatoes’,path:’../images/toppings/tomatoes_marinated.png’,container:null},
    {title:’Roma Tomatoes’,path:’../images/toppings/tomatoes_roma.png’,container:null}
];
 
//— toppings title
var toppingsTitle = Ti.UI.createLabel({
    text:’2.
    font:{
        fontFamily:’Verdana’,
        fontWeight:’bold’,
        fontSize:22
    },
    color:’#A90329′,
    shadowColor:’#333′,
    shadowOffset:{x:1,y:1},
    textAlign:’left’,
    width:Ti.Platform.displayCaps.platformWidth,
    height:58,
    left:10
});
 
//— toppings title background
var toppingsTitleView = Ti.UI.createView({
    width:328,
    height:58,
    backgroundImage:’../images/crustHeaderBg.png’,
    top:100,
    left:-6,
    opacity:0
});
toppingsTitleView.add(toppingsTitle);
 
//— holds the pizza image
var pizza = Ti.UI.createView({
    top:270,
    width:216,
    height:156,
    backgroundImage:win.path
});
 
//— this will hold all the selected toppings
var toppingsHolder = Ti.UI.createView({
    width:216,
    height:156
});
pizza.add(toppingsHolder);
win.add(pizza);
win.add(toppingsTitleView);
 
//— Details Button
var details = Ti.UI.createButton({
    width:137,
    height:75,
    backgroundImage:’../images/details.png’,
    top:385,
    left:165,
    opacity:0
});
 
//— Cancel Button
var cancel = Ti.UI.createButton({
    width:137,
    height:75,
    backgroundImage:’../images/cancel.png’,
    top:385,
    left:10,
    opacity:0
});
 
//— If android OS, use the image property instead of backgroundImage (Ti SDK bug)
if (Ti.Platform.osname == ‘android’)
{
    details.image = ‘../images/details.png’;
    cancel.image = ‘../images/cancel.png’;
    pizza.image = win.path;
}
else
{
    pizza.opacity = 0;
}
win.add(details);
win.add(cancel);
 
//— Cancel click event goes back to the crust window and passes the current crust so it selects the correct one when returning
cancel.addEventListener(‘click’,function(e){
    Ti.App.fireEvent(‘cancelToppings’,{crust:win.crust});
});
 
details.addEventListener(‘click’,function(e){
 
});
 
//— Fade the views and buttons in
toppingsTitleView.animate({
    opacity:1,
    duration:500
});
 
pizza.animate({
    opacity:1,
    duration:500
});
 
details.animate({
    opacity:1,
    duration:500
});
 
cancel.animate({
    opacity:1,
    duration:500
});

Поскольку мы добавили три пользовательских свойства в окно начинки, когда оно открывалось, мы можем ссылаться на них, используя win.propertyName . В нашем случае мы устанавливаем изображение пиццы на win.path . Это говорит представлению использовать любое изображение, которое мы выбрали в качестве фонового изображения. Идите вперед и скомпилируйте. Пройдя окно корок, вы уже не сможете идти назад или вперед, но вы можете видеть, что окно топпинга будет содержать ту корку, которую вы выбрали. Ваше окно начинки должно выглядеть примерно так:


Теперь мы хотим добавить наш прокручиваемый список в окно начинки. Я объяснил в коде, что делает каждый шаг, но, повторюсь, в iOS на самом деле нет компонента-флажка, поэтому я пошел дальше и сделал свой собственный, используя два файла PNG, и я просто меняю изображение на основе выбранного Свойство на флажок просмотра.

Ниже приведен код для генерации списка, а также обработки события click для каждого топинга в списке:

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
//— This method toggles a topping item by checking the selected property
//— It will fade a new topping in and also remove a topping when it gets unchecked
function toppingListClick(e)
{
    if (e.source.selected)
    {
        e.source.selected = false;
        e.source.backgroundImage = ‘../images/checkbox_no.png’;
 
        numToppings -= 1;
        if (toppings[e.source.toppingID].container != null)
        {
            toppingsHolder.remove(toppings[e.source.toppingID].container);
            toppings[e.source.toppingID].container = null;
        }
    }
    else
    {
                //— If numToppings is less than maxToppings, add the new topping else alert them
        if (numToppings < maxToppings)
        {
            e.source.selected = true;
            e.source.backgroundImage = ‘../images/checkbox_yes.png’;
 
            var aTopping = Ti.UI.createView({
                backgroundImage:toppings[e.source.toppingID].path
            });
             
            if (Ti.Platform.osname == ‘android’)
            {
                aTopping.image = toppings[e.source.toppingID].path;
            }
            else
            {
                aTopping.opacity = 0;
                aTopping.animate({
                    opacity:1,
                    duration:500
                });
            }
            toppingsHolder.add(aTopping);
            toppings[e.source.toppingID].container = aTopping;
            numToppings += 1;
        }
        else
        {
            alert(«Hang on there cowboy! Let’s not get carried away with toppings. » + numToppings + » is the max.»);
        }
    }
}
 
/*
This method creates the topping list.
I made my own using a view, a button and swapping out the background image
*/
function createToppingsList()
{
    scrollView.opacity = 0;
    scrollView.top = 155;
    scrollView.height = 120;
    scrollView.contentWidth = Ti.Platform.displayCaps.platformWidth;
    scrollView.contentHeight = ‘auto’;
    scrollView.showVerticalScrollIndicator = true;
    win.add(scrollView);
 
    for (i = 0; i < toppings.length; i++)
    {
        //— The label
        var toppingLabel = Ti.UI.createLabel({
            text:toppings[i].title,
            font:{
                fontFamily:’Verdana’,
                fontWeight:’bold’,
                fontSize:14
            },
            color:’#fff’,
            shadowColor:’#333′,
            shadowOffset:{x:1,y:1},
            textAlign:’left’,
            width:Ti.Platform.displayCaps.platformWidth — 10,
            left:10
        });
         
        //— We add a custom property ‘selected’ to our checkbox view
        var checkbox = Ti.UI.createView({
            width:340,
            height:16,
            backgroundImage:’../images/checkbox_no.png’,
            selected:false,
            toppingID:i
        });
         
        var toggler = Ti.UI.createView({
            width:Ti.Platform.displayCaps.platformWidth,
            height:20,
            top: i * 20
        });
         
        //— We use the singletap event rather than the click since its in a scroll view
        checkbox.addEventListener(‘singletap’,toppingListClick);
        toggler.add(toppingLabel);
        toggler.add(checkbox);
         
        scrollView.add(toggler);
    }
    scrollView.animate({
        opacity:1,
        duration:500
    });
}
createToppingsList();

Наконец, мы вызываем метод createToppingsList который вызывается при каждом открытии окна. В третьей части этой серии руководств мы изменим этот метод, чтобы в случае, если пользователь нажал «Отмена» в окне отправки заказа, приложение запомнило, какие начинки были ранее выбраны пользователем. А пока скомпилируйте, и ваше приложение должно выглядеть так:

Вы можете идти вперед и проверять флажки, чтобы увидеть их функциональность.


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

Чтобы обработать это событие, мы должны вернуться к нашему файлу main.js и добавить его и прослушиватель событий для него.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
//— The method will close the toppings window and open the crusts window
function openCrust(e)
{
    toppings.close();
        //— If the event has a crust property, that means the user hit cancel once
       //— in the toppings window
    if (e.crust)
    {
        crusts.crust = e.crust;
    }
    crusts.url = ‘crusts.js’;
    crusts.open();
}
 
//— Have our app listen for our custom events
Ti.App.addEventListener(‘toppings’,openToppings);
Ti.App.addEventListener(‘cancelToppings’,openCrust);

Итак, вы можете видеть, что мы добавили еще одного слушателя событий. Когда он получает событие после того, как пользователь openCrust «Отмена» в начинки, он openCrust метод openCrust . Помните в первой части, как я сказал, что мы будем передавать данные в конце концов? Ну, это время пришло. В событии click для кнопки отмены мы пропустили текущую кору. Мы изменили метод openCrust , закрыв окно «toppings», и если свойство openCrust присутствует в событии, это означает, что они нажали «отмена», поэтому я хочу добавить тип crust в качестве свойства в окно Crusts. Это позволит нам автоматически выбрать ранее выбранную корку. Мы рассмотрим это на следующем шаге.


Откройте crusts.js . Нам нужно добавить условие, чтобы проверить, существует ли свойство crts в окне. Вы захотите поместить этот код прямо в нашу переменную scrollView :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
//— If the window has the crust property, that means we are coming from the
//— toppings window, so choose the last known selected crust
if (win.crust)
{
    for (i = 0; i < crusts.length; i++)
    {
        if (win.crust == crusts[i].title)
        {
            returnCrust = i;
            break;
        }
    }
    scrollView.scrollToView(returnCrust);
}

Если свойство crust не равно null, этот фрагмент будет проходить через наш существующий массив crts и прерываться, как только свойство crust совпадет с заголовком в массиве. Как только он находит совпадение, мы используем метод scrollToView в нашем scrollView . Это будет предварительно выбрать нашу корку из нашего последнего раздела.

У нас есть еще одна часть этого шага. Если вы скомпилировали, вы заметите, что название коры неверно, поэтому нам нужно это исправить. Вставьте этот небольшой фрагмент под нашей переменной crustType .

1
2
3
4
5
//— if returnCrust isn’t null, set the crust type label
if (returnCrust != null)
{
    crustType.text = crusts[returnCrust].title;
}

Проблема решена! Когда вы нажимаете «Отмена» в окне начинки, мы возвращаемся к окну корок и выбираем ранее выбранную корку, а также совпадаем с названием корки. Идите и проверьте это. Выберите корку, перейдите на начинки и нажмите «Отмена». Вы должны быть в состоянии идти вперед и назад столько, сколько хотите!


Во второй части мы обрабатывали некоторые пользовательские события, которые позволяли нам перемещаться между некоторыми окнами с помощью наших openToppings и openCrust в main.js Мы узнали о передаче данных между окнами. По сути, мы создали новый компонент, который не существует в iOS, это флажок. Конечно, iOS SDK имеет тумблер, но это ужасно и не очень хорошо выглядит в нашем приложении. В третьей части этого руководства мы рассмотрим переход к окну отправки заказа. Оказавшись в окне, мы заполним некоторые текстовые поля и при отправке отправим всю нашу информацию о пицце в скрипт PHP. Затем PHP-скрипт отправит на ваш адрес электронной почты выбранный вами адрес, имитируя, как поступил бы заказ, если бы это было реально работающее приложение.