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