• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 开发首页<a name="ZH-CN_TOPIC_0000001054927705"></a>
2
3应用首页主要展示城市的空气质量概况。首页总共有两屏(可以根据需求设置多屏),每屏显示一个城市的空气质量信息:主要包括AQI指数、城市名称、污染物指数、更新时间和信息来源等数据。
4
5从第一章节中的[显示效果图](device-camera-visual-overview.md)分析可知,首页由三部分组成:
6
7-   标题栏:位于页面正上方,位置固定,包括应用退出按钮和页面标题。
8-   信息栏:主要展示城市的空气信息指标等内容;该页面根据用户需求可设置多屏,且能循环滑动。
9-   页面位置指示器:主要功能是标识当前页面,位置固定在页面底部的中间。
10
11综上,我们可搭建一个纵向三行排列的弹性页面布局来实现首页的功能。
12
131.  在hml文件中添加一个根节点div,注意每个hml文件中有且只能有一个根节点,代码如下:
14
15    ```
16    <div class="container">
17    </div>
18    ```
19
20    class="container"表示组件使用的样式,container是index.css文件中的一个样式类,代码如下:
21
22    ```
23    .container {
24        flex-direction: column;
25        height: 480px;
26        width: 960px;
27    }
28    ```
29
30    在这个样式类中,我们分别设置了根组件div的高度和宽度(注意在应用的开发过程中,除部分组件(text)外必须显式指定组件的高度和宽度,否则可能无法显示)、并将flex-direction属性设置为column,该属性表示div的子组件是垂直方向从上到下排列;这样就可以实现本节开头所说的纵向三行排列的弹性页面布局。
31
322.  实现标题栏:标题栏包括一个退出按钮和一个标题,两个控件是横向排列;首先添加一个div,并设置flex-direction的属性为row,表示子组件是水平方向从左往右排列;然后依次添加一个image和text组件,代码如下:
33
34    ```
35    <div class="container">
36        <div class="header" onclick="exitApp">
37            <image class="back" src="common/ic_back.png"></image>
38            <text class="title">
39                空气质量
40             </text>
41        </div>
42    </div>
43    ```
44
45    设置组件的高度、边距、颜色等属性。
46
47    ```
48    .header {
49        width: 960px;
50        height: 72px;
51    }
52    .back {
53        width: 36px;
54        height: 36px;
55        margin-left: 39px;
56        margin-top: 23px;
57    }
58    .title {
59        width: 296px;
60        height: 40px;
61        margin-top: 20px;
62        margin-left: 21px;
63        color: #e6e6e6;
64    }
65    ```
66
67    onclick="exitApp" 设置了div组件的click事件,当在标题栏上触发点击事件时,就会执行函数exitApp,该函数位于index.js文件中,代码如下:
68
69    ```
70    exitApp() {
71        console.log('start exit');
72        app.terminate();
73        console.log('end exit');
74    }
75    ```
76
77    app.terminate\(\)函数实现了程序退出功能;在使用该函数前,需要引入app模块,在js文件的最上方写如下代码:
78
79    ```
80    import app from '@system.app'
81    ```
82
83    代码编写完成后,在模拟器中运行项目,显示效果如下图所示:
84
85    **图 1**  标题栏效果<a name="fig14273162465317"></a>
86    ![](figures/标题栏效果.png "标题栏效果")
87
883.  实现城市空气质量信息的多屏左右滑动,需要使用“swiper”组件。
89
90    在根节点中添加一个子节点swiper,代码片段如下:
91
92    ```
93    <div class="container">
94        <div class="header" onclick="exitApp">
95            <image class="back" src="common/ic_back.png"></image>
96            <text class="title">
97                空气质量
98            </text>
99        </div>
100        <swiper class="swiper" index="{{swiperPage}}" duration="500" onchange="swiperChange">
101        </swiper>
102    </div>
103    ```
104
105    -   class="swiper"设置了组件的高度和宽度,代码如下:
106
107        ```
108        .swiper {
109            height: 385px;
110            width: 960px;
111        }
112        ```
113
114    -   index="\{\{swiperPage\}\}" duration="500" onchange="swiperChange" 这些代码用来设置组件的属性和事件。其中,duration="500" 表示设置swiper的页面滑动的动画时长为500ms。
115    -   index="\{\{swiperPage\}\}"设置了swiper子组件索引值,\{\{swiperPage\}\}这种写法表示index的值是和js代码中的swiperPage变量动态绑定的,index的值会随着swiperPage变动而改变。
116    -   onchange="swiperChange" 设置了swiper组件的change事件和函数swiperChange绑定,对应的js代码如下:
117
118        ```
119        //引入router模块,用户页面跳转
120        import router from'@system.router'
121        import app from '@system.app'
122
123        export default {
124            //定义参数
125            data: {
126              //默认是第一页
127              swiperPage: 0
128            },
129            onInit () {
130            },
131            exitApp(){
132                console.log('start exit');
133                app.terminate();
134                console.log('end exit');
135            },
136            //swiper滑动回调事件,保存当前swiper的index值,每次滑动都会将index值保存在swiperPage变量中
137            swiperChange (e) {
138                this.swiperPage = e.index;
139            }
140        }
141        ```
142
1434.  设置一个城市的空气质量信息为一屏,在一屏内,要展示多种信息,分别使用不同的控件进行展示。
144
145    在swiper中添加两个子组件stack(绝对布局),每个stack组件内分别添加text、image、progress等组件来显示对应的信息 ,页面结构如下:
146
147    ```
148     <swiper class="swiper" index="{{swiperPage}}" duration="500" onchange="swiperChange">
149        <!--第一屏-->
150        <stack class="swiper">
151            <text></text>------空气质量
152            <text></text>------城市名称
153            <progress></progress>-----进度条
154            <image></image>-------云朵图片
155            <text></text>--------AQI数值
156            <text>AQI</text>------AQI
157            <div>--------空气指标详细信息
158            </div>
159            <div>--------更新时间和网站等信息
160            </div>
161        </stack>
162        <!--第二屏-->
163        <stack class="container">
164            <text></text>
165            <text></text>
166            <progress></progress>
167            <image></image>
168            <text></text>
169            <text></text>
170            <div></div>
171        </stack>
172    </swiper>
173    ```
174
175    代码编写完成后,模拟器运行效果如下:
176
177    **图 2**  标题栏和信息栏效果<a name="fig177003454238"></a>
178    ![](figures/标题栏和信息栏效果.png "标题栏和信息栏效果")
179
1805.  添加页面位置指示器:由于当前swiper不支持设置indicator,需要开发者自己来实现该效果。在根节点中添加一个子组件div,并设置相应样式;然后在该div中添加两个子组件div,设置两个div的border-radius,并在swiper滑动事件中动态改变对应div的背景色来实现该效果。
181
182    ```
183    <div class="images">
184        <div class="circle-div" style="background-color: {{iconcheckedColor}};"></div>
185        <div class="circle-div" style="background-color: {{iconUncheckedColor}};margin-left: 36px;"></div>
186    </div>
187    ```
188
189    **图 3**  页面位置指示器效果图<a name="fig767374119496"></a>
190    ![](figures/页面位置指示器效果图.png "页面位置指示器效果图")
191
1926.  所有组件设置样式、动画效果和数据动态绑定,完整代码如下所示:
193
194    -   **index.hml文件**
195
196    ```
197    <div class="container">
198        <div class="header" onclick="exitApp">
199            <image class="back" src="common/ic_back.png"></image>
200            <text class="title">
201                空气质量
202            </text>
203        </div>
204        <swiper class="swiper" index="{{swiperPage}}" duration="500" onchange="swiperChange">
205            <stack class="swiper">
206                <text class="airquality" style="color:{{textColor1}};">{{airData[0].airQuality}}
207                </text>
208                <text class="location-text">{{airData[0].location}}
209                </text>
210                <progress class="circle-progress" style="color: {{textColor1}};background-Color: {{bgColor1}};" type="arc"
211                        percent="{{percent1}}"></progress>
212                <image class="image" src="{{src1}}"></image>
213                <text class="aqi-value">{{airData[0].detailData}}
214                </text>
215                <text class="aqi">
216                    AQI
217                </text>
218                <div class="detail">
219                    <div class="text-wrapper">
220                        <text class="gas-name">
221                            CO
222                        </text>
223                        <text class="gas-value">
224                            100
225                        </text>
226                    </div>
227                    <div class="text-wrapper">
228                        <text class="gas-name">
229                            NO2
230                        </text>
231                        <text class="gas-value">
232                            90
233                        </text>
234                    </div>
235                    <div class="text-wrapper">
236                        <text class="gas-name">
237                            PM10
238                        </text>
239                        <text class="gas-value">
240                            120
241                        </text>
242                    </div>
243                    <div class="text-wrapper">
244                        <text class="gas-name">
245                            PM2.5
246                        </text>
247                        <text class="gas-value">
248                            40
249                        </text>
250                    </div>
251                    <div class="text-wrapper">
252                        <text class="gas-name">
253                            SO2
254                        </text>
255                        <text class="gas-value">
256                            150
257                        </text>
258                    </div>
259                    <input class="btn" type="button" onclick="openDetail" value="历史记录"></input>
260                </div>
261                <div class="footer">
262                    <text class="update-time">
263                        更新时间: 10:38
264                    </text>
265                    <text class="info-source">
266                        信息来源: tianqi.com
267                    </text>
268                </div>
269            </stack>
270            <stack class="swiper">
271                <text class="airquality" style="color: {{textColor2}};">{{airData[1].airQuality}}
272                </text>
273                <text class="location-text">{{airData[1].location}}
274                </text>
275                <progress class="circle-progress" style="color: {{textColor2}};background-Color: {{bgColor2}};" type="arc"
276                        percent="{{percent2}}"></progress>
277                <image class="image" src="{{src2}}"></image>
278                <text class="aqi-value">{{airData[1].detailData}}
279                </text>
280                <text class="aqi">
281                    AQI
282                </text>
283                <div class="detail">
284                    <div class="text-wrapper">
285                        <text class="gas-name">
286                            CO
287                        </text>
288                        <text class="gas-value">
289                            10
290                        </text>
291                    </div>
292                    <div class="text-wrapper">
293                        <text class="gas-name">
294                            NO2
295                        </text>
296                        <text class="gas-value">
297                            50
298                        </text>
299                    </div>
300                    <div class="text-wrapper">
301                        <text class="gas-name">
302                            PM10
303                        </text>
304                        <text class="gas-value">
305                            60
306                        </text>
307                    </div>
308                    <div class="text-wrapper">
309                        <text class="gas-name">
310                            PM2.5
311                        </text>
312                        <text class="gas-value">
313                            40
314                        </text>
315                    </div>
316                    <div class="text-wrapper">
317                        <text class="gas-name">
318                            SO2
319                        </text>
320                        <text class="gas-value">
321                            150
322                        </text>
323                    </div>
324                    <input class="btn" type="button" onclick="openDetail" value="历史记录"></input>
325                </div>
326                <div class="footer">
327                    <text class="update-time">
328                        更新时间: 10:38
329                    </text>
330                    <text class="info-source">
331                        信息来源: tianqi.com
332                    </text>
333                </div>
334            </stack>
335        </swiper>
336        <div class="images">
337            <div class="circle-div" style="background-color: {{iconcheckedColor}};"></div>
338            <div class="circle-div" style="background-color: {{iconUncheckedColor}};margin-left: 36px;"></div>
339        </div>
340    </div>
341    ```
342
343    -   **index.css文件**
344
345    css文件中定义了许多class,每个class用于定义组件的位置、大小、字体、颜色、背景色等信息。同时,每一个子组件都叠加在父组件中,父组件的样式会影响子组件的呈现。
346
347    ```
348    .aqi-value {
349        text-align: center;
350        font-size: 65px;
351        color: #f0ffff;
352        width: 156px;
353        height: 92px;
354        top: 134px;
355        left: 210px;
356    }
357    .aqi {
358        text-align: center;
359        color: #a2c4a2;
360        width: 156px;
361        height: 45px;
362        top: 90px;
363        left: 210px;
364    }
365    .airquality {
366        top: 222px;
367        text-align: center;
368        width: 156px;
369        height: 45px;
370        left: 210px;
371    }
372    .image {
373        top: 285px;
374        left: 274px;
375        width: 32px;
376        height: 32px;
377    }
378    .location-text {
379        text-align: center;
380        color: #ffffff;
381        width: 200px;
382        height: 52px;
383        font-size: 40px;
384        left: 380px;
385        top: 16px;
386    }
387    .container {
388        flex-direction: column;
389        height: 480px;
390        width: 960px;
391    }
392    .circle-progress {
393        center-x: 128px;
394        center-y: 128px;
395        radius: 128px;
396        startAngle: 198;
397        totalAngle: 320;
398        strokeWidth: 24px;
399        width: 256px;
400        height: 256px;
401        left: 160px;
402        top: 58px;
403    }
404    .detail {
405        width: 256px;
406        height: 265px;
407        left: 544px;
408        top: 58px;
409        flex-direction: column;
410    }
411    .text-wrapper {
412        width: 256px;
413        height: 35px;
414        margin-top: 6px;
415    }
416    .gas-name {
417        width: 128px;
418        height: 35px;
419        text-align: left;
420    }
421    .gas-value {
422        width: 128px;
423        height: 35px;
424        text-align: right;
425    }
426    .btn {
427        width: 180px;
428        height: 50px;
429        margin-top: 6px;
430        margin-left: 38px;
431        background-color: #1a1a1a;
432        color: #1085CE;
433    }
434    .footer {
435        top: 326px;
436        width: 960px;
437        height: 28px;
438    }
439    .header {
440        width: 960px;
441        height: 72px;
442    }
443    .back {
444        width: 36px;
445        height: 36px;
446        margin-left: 39px;
447        margin-top: 23px;
448    }
449    .title {
450        width: 296px;
451        height: 40px;
452        margin-top: 20px;
453        margin-left: 21px;
454        color: #e6e6e6;
455    }
456    .swiper {
457        height: 385px;
458        width: 960px;
459    }
460    .images {
461        width: 60px;
462        height: 15px;
463        margin-left: 450px;
464    }
465    .update-time {
466        width: 480px;
467        height: 28px;
468        font-size: 20px;
469        color: #A9A9A9;
470        text-align: right;
471    }
472    .info-source {
473        width: 450px;
474        height: 28px;
475        font-size: 20px;
476        color: #A9A9A9;
477        text-align: left;
478        margin-left: 24px;
479    }
480    .circle-div {
481        width: 12px;
482        height: 12px;
483        border-radius: 6px;
484    }
485    ```
486
487    -   **index.js:**
488
489    js文件主要用于实现App应用的逻辑交互。在本页面js文件中,需要实现如下功能:根据数值动态改变文字、进度条颜色、页面跳转。
490
491    ```
492    //导入router和app模块
493    import router from '@system.router'
494    import app from '@system.app'
495
496    export default {
497        data: {
498          //页面绑定数据
499          textColor1: '#00ff00',
500          textColor2: '#00ff00',
501          bgColor1: '#669966',
502          bgColor2: '#669966',
503          swiperPage: 0,
504          percent1: 40,
505          percent2: 90,
506          iconUncheckedColor: '#262626',
507          iconcheckedColor: '#ffffff',
508          iconcheckedBR: '6px',
509          src1: 'common/cloud_green.png',
510          src2: 'common/cloud_green.png',
511          airData: [{
512            location: '东莞',
513            airQuality: '良',
514            detailData: 40
515          }, {
516            location: '深圳',
517            airQuality: '差',
518            detailData: 90
519          }]
520        },
521        onInit () {
522          //根据数值的不同,设置不同的字体、背景颜色和图片
523          if(this.airData[0].detailData > 100){
524            this.src1 = 'common/cloud_red.png';
525            this.textColor1 = '#ff0000';
526            this.bgColor1 = '#9d7462';
527          } else if(50 < this.airData[0].detailData && this.airData[0].detailData <= 100){
528            this.src1 = 'common/cloud_yellow.png';
529            this.textColor1 = '#ecf19a';
530            this.bgColor1 = '#9d9d62';
531          }
532          if(this.airData[1].detailData > 100){
533            this.src2 = 'common/cloud_red.png';
534            this.textColor2 = '#ff0000';
535            this.bgColor2 = '#9d7462';
536          } else if(50 < this.airData[1].detailData && this.airData[1].detailData <= 100){
537            this.src2 = 'common/cloud_yellow.png';
538            this.textColor2 = '#ecf19a';
539            this.bgColor2 =  '#9d9d62';
540          }
541          if(this.selectedCityIndex){
542            this.swiperPage = this.selectedCityIndex;
543            if(this.swiperPage == 0){
544              this.iconcheckedColor = '#ffffff';
545              this.iconUncheckedColor = '#262626';
546            }else{
547              this.iconcheckedColor = '#262626';
548              this.iconUncheckedColor = '#ffffff';
549            }
550          }
551        },
552        //跳转到详情页面
553        openDetail () {
554          router.replace({
555            uri: 'pages/detail/detail',
556            params: {selectedCityIndex:this.swiperPage}
557          });
558        },
559        //退出应用
560        exitApp(){
561          console.log('start exit');
562          app.terminate();
563          console.log('end exit');
564        },
565        //页面滑动事件,滑动时改变最新的标识
566        swiperChange (e) {
567          this.swiperPage = e.index;
568          if(e.index == 0){
569            this.iconcheckedColor = '#ffffff';
570            this.iconUncheckedColor = '#262626';
571          }else{
572            this.iconcheckedColor = '#262626';
573            this.iconUncheckedColor = '#ffffff';
574          }
575        }
576    }
577    ```
578
579
580