“微音乐”微信小程序实战开发过程

RobNavarret 7年前
   <p>本文带大家开发一个音乐播放器微信小程序——微音乐。该播放器通过QQ音乐接口获取音乐相关数据,首先在页面中显示一个音乐分类列表,用户选择分类之后从QQ音乐中查询获取符合要求的音乐列表,在这个音乐列表中单击一首音乐即进入播放页面进行播放。另外,还需要做一个查询功能,可按歌手或音乐名称进行查询。</p>    <h2>QQ音乐API</h2>    <p>与“微天气”案例类似,本案例也是通过互联网中已有的API来获取音乐信息。在互联网上这类API很多,本案例使用“易源接口”网站提供的QQ音乐接口,易源接口网址如下:</p>    <p><a href="/misc/goto?guid=4959742659815845720" rel="nofollow,noindex">https://www.showapi.com/</a></p>    <h2>认识易源接口网站</h2>    <p>在浏览器中打开易源接口网站,可看到如图1所示的界面。从网页左边的“API分类导航”列表可看到,该网站提供了不同种类的API,在大类中又有很多小类,天气预报的接口也有。</p>    <p>在易源接口网站中提供的接口很多是免费的,要使用这些免费接口,也需要在网站中注册账号,然后申请使用。申请成功之后,在“我的应用”中就可看到申请应用的appid,如图2所示。在应用同一行的secret列单击“查看密钥”,将弹出对话框显示该应用的密钥。将appid和secret复制下来,以备程序中使用。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/19fb6b3bf3f2f7adb54b41d558cd788b.png"></p>    <p>图1 易源接口</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/adb869932027eb6f5ed440b5bf1f16d0.png"></p>    <p>图2 我的接口</p>    <h2>QQ音乐接口</h2>    <p>本案例使用易源接口提供的“QQ音乐”接口,其说明如图3所示。可以看到,这个接口是免费使用的。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/81f2a3b7ee456aebe4b3d3e3e46c85e4.png"></p>    <p>图3 QQ音乐接口</p>    <p>在图3所示页面的左侧“接入点列表”中可看到该API提供了3个接入点。</p>    <p>1. 热门榜单</p>    <p>在图3所示页面中,单击左侧的“热门榜单”,将显示该接入点的详细信息。</p>    <p>热门榜单接入点的URL地址如下:</p>    <p><a href="/misc/goto?guid=4959742659921300309" rel="nofollow,noindex">http://route.showapi.com/213-4</a></p>    <p>请求该URL地址时,还需要传入一些参数,主要有以下这些。</p>    <ul>     <li>showapi_appid:这是用户申请的appid。</li>     <li>showapi_sign:这是用户应用的密钥。</li>     <li>topid:这是音乐分类编码(如5表示内地音乐,6表示港台音乐)。</li>    </ul>    <p>该接入点返回的JSON数据格式如下(与易源接口官方提示的内容有些不同):</p>    <pre>  <code class="language-javascript">{      "showapi_res_code": 0,      "showapi_res_error": "",      "showapi_res_body": {          "ret_code": 0,          "pagebean": {              "songlist": [{                  "songname": "一定要幸福 (《咱们相爱吧》电视剧主题曲)",                  "seconds": 294,                  "albummid": "003V7SAg16Ed0F",                  "songid": 109127914,                  "singerid": 4607,                  "albumpic_big": "http://i.gtimg.cn/music/photo/mid_album_300/                            0/F/003V7SAg16Ed0F.jpg",                  "albumpic_small": "http://i.gtimg.cn/music/photo/                         mid_album_90/0/F/003V7SAg16Ed0F.jpg",                  "downUrl": "http://dl.stream.qqmusic.qq.com/109127914.mp3?                          vkey=3B0957F1A4CDCAD8875251834B7C0DA2D4287FA3BC1A5F73AA                           002D3833AE5685FE6168E75BBDB277CB0635E3B483CB6E3A073                           E7A1B9723A4&guid=2718671044",                  "url": "http://ws.stream.qqmusic.qq.com/                           109127914.m4a?fromtag=46",                  "singername": "张靓颖",                  "albumid": 1679081              },                   ……              ],              "total_song_num": 100,              "ret_code": 0,              "update_time": "2016-11-17",              "color": 0,              "cur_song_num": 100,              "comment_num": 1010,              "currentPage": 1,              "song_begin": 0,              "totalpage": 1          }      }  }</code></pre>    <p>从上面的JSON数据可看出,该接入点返回的数据中,音乐列表数据保存在songlist数组中,该数组中的每一个元素是一首音乐的信息,各字段的含义如下:</p>    <pre>  <code class="language-javascript">"songname":音乐名称,  "seconds": 时长,  "songid": 音乐ID,  "singerid": 歌手id,  "albumpic_big": 专辑大图片,高宽300,  "albumpic_small": 专辑小图片,高宽90,  "downUrl": mp3下载链接,  "url": 流媒体地址,  "singername": 歌手名,  "albumid": 专辑id</code></pre>    <p>2. 根据歌名、人名查询歌曲</p>    <p>热门榜单接入点的URL地址如下:</p>    <p><a href="/misc/goto?guid=4959742660010046972" rel="nofollow,noindex">http://route.showapi.com/213-1</a></p>    <p>请求该URL地址时,还需要传入一些参数,主要有以下这些。</p>    <ul>     <li>showapi_appid:这是用户申请的appid。</li>     <li>showapi_sign:这是用户应用的密钥。</li>     <li>keyword:查询关键字(人名或歌名)。</li>    </ul>    <p>该接入点返回的JSON数据格式如下所示:</p>    <pre>  <code class="language-javascript">{      "showapi_res_code": 0,      "showapi_res_error": "",      "showapi_res_body": {          "ret_code": 0,          "pagebean": {              "w": "刘德华",              "allPages": 14,              "ret_code": 0,              "contentlist": [{                  "m4a": "http://ws.stream.qqmusic.qq.com/                         179990.m4a?fromtag=46",                  "media_mid": "002Ly1Xh1pwBGt",                  "songid": 179990,                  "singerid": 163,                  "albumname": "幻影中国巡回演唱会Live",                  "downUrl": "http://dl.stream.qqmusic.qq.com/179990.mp3                         ?vkey=1BD3868E2A0278D184D1FEC2A9391F1A673AAF1FCAB59DEA                         F0DCCF80ED58E564978D1EAAF5E53B85B0E5D30ACFF2AFBF32296                         4C86ED8B14D&guid=2718671044",                  "singername": "刘德华",                  "songname": "练习 (Live)",                  "strMediaMid": "002Ly1Xh1pwBGt",                  "albummid": "004UpCFj3kyano",                  "songmid": "002Ly1Xh1pwBGt",                  "albumpic_big": "http://i.gtimg.cn/music/photo/mid_album_300/                          n/o/004UpCFj3kyano.jpg",                  "albumpic_small": "http://i.gtimg.cn/music/photo/                          mid_album_90/n/o/004UpCFj3kyano.jpg",                  "albumid": 15531              },              ,],              "currentPage": 1,              "notice": "",              "allNum": 393,              "maxResult": 30          }      }  }</code></pre>    <p>可以看出,这与使用热门榜单接入点获取的数据格式类似,只是这里多了一些查询相关的数据,另外,返回的音乐列表不是保存在songlist数组中了,而是保存contentlist数组中,流媒体地址不是保存在url中,而是保存在m4a中。其他数据的含义基本相同,这里就不列出来了。</p>    <p>本案例主要使用这两个接入点,读者可在本案例的基础上做歌词显示功能,则需要使用到“根据歌曲id查询歌词”这个接入点。</p>    <p>另外,在访问某一个接入点后如果返回“没有订购套餐”的错误结果,由于本API是免费使用的,出现这个提示说明用户对接入点还未订购。可在图3所示页面中单击“价格一览表”,显示如图4所示页面,单击左侧的“为所有免费接入点一键订购”即可正常使用所有免费接入点了。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/9ec5b3294462ed9d6921a26390591e2a.png"></p>    <p>图4 为所有免费接入点一键订购</p>    <h2>界面设计</h2>    <p>“微音乐”需要设计4个界面,分别是:</p>    <p>(1)音乐分类列表界面,如图5所示,显示音乐的分类列表。</p>    <p>(2)音乐列表界面,如图6所示,这是在图5所示界面中选择某一分类中,列出该分类下的音乐曲目,为了使界面更好看一点,在曲目上方显示一张图片,这张图片直接获取第一首曲目的专辑封面图片。</p>    <p>(3)音乐播放界面,如图7所示,在图6所示曲目列表中单击一首曲目,就进入本界面,上方显示专辑图片,下方显示歌名、歌手名称和播放按钮,单击播放按钮就可播放。</p>    <p>(4)搜索界面,如图8所示,在输入框中输入关键字,单击“立即搜索”按钮进行搜索,结果显示在下方的列表中,单击结果中的某一首歌曲,进入图7所示播放界面开始播放。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/be66d91a48251c3de6feb089b0f08589.jpg"></p>    <h2>创建项目</h2>    <p>界面初稿设计出来之后,就可以考虑进入实际程序开发过程了。</p>    <h2>准备资源</h2>    <p>从图5至图8所示的4个页面可看出,本案例中需要显示一些图标和图片,其中专辑封面图片通过API动态获取,而每首歌典前面出现的图标就需要在编写代码之前准备好,还有图7中的播放按钮图标,以及与其对应的暂停播放的图标。</p>    <p>另外,在界面下方有一个工具条,最好也设计出对应的图标。对于工具条中的图标还需要设计出正常状态和选择状态两种不同的图标,方便用户区分当前选择是哪一个tab。</p>    <p>通常,这些图标可以从网络中去搜索,然后再用Photoshop等图像处理软件进行简单的加工即可。本案例使用到的图标如图9所示。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/8fba8889dd28f221872796ede77d59f5.png"></p>    <p>图9 案例用到的图标</p>    <p>在项目中新建一个名为images的子目录,将如图9所示的设计好的图标复制到该子目录备用。</p>    <h2>创建项目</h2>    <p>首先按以下步骤创建出项目。</p>    <p>(1)创建名为ch12的项目目录。</p>    <p>(2)启动微信小程序开发工具,在启动界面中单击“添加项目”按钮,打开如图10所示的对话框。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/f8fabd6be77e7a557431a09c30002c31.png"></p>    <p>图10 添加项目</p>    <p>(3)在图10所示对话框中填写好相应的项目名称,并选择保存项目的目录,单击“添加项目”按钮即可创建好一个项目的框架。</p>    <p>(4)打开app.json文件,修改页面数组、修改显示标题并增加tabBar的设置,具体内容如下:</p>    <pre>  <code class="language-javascript">{    "pages":[      "pages/index/index",      "pages/play/play",      "pages/list/list",      "pages/search/search",      "pages/logs/logs"    ],    "window":{      "backgroundTextStyle":"light",      "navigationBarBackgroundColor": "#fff",      "navigationBarTitleText": "微音乐",      "navigationBarTextStyle":"black"    },    "tabBar": {      "color":"#818181",      "backgroundColor":"black",      "selectedColor":"green",      "list": [{        "pagePath": "pages/index/index",        "text": "音乐列表",        "iconPath":"/images/music.png",        "selectedIconPath":"/images/music-s.png"      }, {        "pagePath": "pages/play/play",        "iconPath":"/images/playing.png",        "selectedIconPath":"/images/playing-s.png",        "text": "正在播放"      }, {        "pagePath": "pages/search/search",        "iconPath":"/images/search.png",        "selectedIconPath":"/images/search-s.png",        "text": "搜索"      }]    }  }</code></pre>    <p>由于在pages数组中增加了3个页面,但这3个页面还没有创建,因此保存app.json时会出现错误提示,可以不管这个提示。当然,也可以将各页面创建好之后再修改app.json文件。</p>    <p>(5)为了使项目不提示错误,接下来在pages目录中分别创建list、play和search等3个子目录,并分别创建对应的wxml、js、wxss等文件。这样,项目就不会出现错误提示了。</p>    <p>至此,项目结构搭建完成,接下来分别开发4个页面代码即可。</p>    <h2>创建配置文件</h2>    <p>在项目中要使用到易源接口提供的QQ音乐API,这个API的接入点地址和身份认证参数之类的串在一起比较长,并且在多个页面中需要使用到,因此最好将这些内容封装在一个外部文件中,需要时引入即可。</p>    <p>在项目根目录创建一个名为config.js的文件,编写如下代码:</p>    <pre>  <code class="language-javascript">(function(module){      var exports=module.exports={};      //易源接口应用ID      var appid=27426;       //接口密钥      var secret="f7a6a43aef0649b5bd1a051e8f5aa536";        //GET方式的参数      var param="?showapi_appid=" + appid+"&showapi_sign=" + secret;        //热门榜单访问接口      var hotUrl = "http://route.showapi.com/213-4" + param;        //根据歌名、人名查询歌曲接口      var searchByNameUrl ="http://route.showapi.com/213-1" + param;        var searchByIdUrl = "http://route.showapi.com/213-2" + param;        module.exports = {          config: {              hotUrl:hotUrl,              searchByNameUrl:searchByNameUrl,              searchByIdUrl:searchByIdUrl                      }      };  })(module);</code></pre>    <p>以上代码将易源接口的接入点URL、appid和secret等都封装起来,并以config对象的属性形式提供。其他页面引入config.js之后,就可使用config.hotUrl这样的形式直接引用了。</p>    <h2>音乐分类列表</h2>    <p>音乐分类列表作为本项目的主页面,将其代码编写在index页面中。因此,将创建项目时自动创建的index.wxml、index.js等文件中原有内容删除,然后在这里编写相应的代码即可。</p>    <h2>开发页面文件</h2>    <p>打开index.wxml文件,删除原有内容,重新输入以下wxml代码:</p>    <pre>  <code class="language-javascript"><view class="container">    <view class="rank-list">      <block  wx:for="{{ranks}}"  wx:key="{{item.type}}">        <view class="rank-item">          <navigator url="/pages/list/list?type={{item.type}}" class="text">               {{item.text}}</navigator>          <view class="arrow"/>        </view>      </block>    </view>  </view></code></pre>    <p>可以看出,音乐分类列表的页面布局代码很简单,只是从ranks中取出数据,循环渲染到页面中即可,每一项是一个分类,单击分类后导航到list页面,并将分类信息传递到list页面进行处理。</p>    <h2>开发页面样式文件</h2>    <p>在index.wxml文件中,为每一个组件都设置class属性,接下来在index.wxss中编写对应的样式代码即可。打开index.wxss文件,删除原有内容,然后输入以下样式代码:</p>    <pre>  <code class="language-javascript">.rank-list {    width: 100%;  }    /*每一个分类*/  .rank-item {    width: 100%;    text-align: left;    height: 3rem;    line-height: 3rem;    border-bottom: 1px solid #eee;    position: relative;  }    /*分类文本*/  .rank-item .text {    padding-left: 1rem;  }    /*分类名右侧的箭头图标*/  .rank-item .arrow {    width: 10px;    height: 10px;    border-top: 2px solid #999;    border-right: 2px solid #999;    position: absolute;    right: 20px;    transform: rotate(45deg);    top: 20px;  }</code></pre>    <h2>开发页面逻辑代码</h2>    <p>在index.wxml文件中绑定了一个名为ranks的变量,这个对象中保存了音乐分类的信息,需要在逻辑代码中进行定义。打开index.js文件,删除原有内容,输入以下JS代码:</p>    <pre>  <code class="language-javascript">Page({    data:{      //音乐分类      ranks:[        {type:26,text:"热歌"},        {type:23,text:"销量"},        {type:18,text:"民谣"},        {type:19,text:"摇滚"},        {type:5,text:"内地"},        {type:6,text:"港台"},        {type:16,text:"韩国"},        {type:17,text:"日本"},        {type:3,text:"欧美"}        ],    },  })</code></pre>    <p>可以看出,这里的JS代码很简单,只是定义了一个音乐分类的数组。最终反映在界面上的分类排列顺序是以这个数组中各元素的顺序为准的,因此,可以在这里进行调整,使最终显示的分类顺序符合自己的要求。</p>    <p>将index.wxml、index.wxss和index.js这3个文件编写好之后,保存,在开发工具左侧的模拟器中就可看到如图11所示的效果。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/719542ae8703f1475d05f97f6b00262d.png"></p>    <p>图11 音乐分类列表</p>    <p>至此,音乐列表页面开发完成。这个页面很简单,也不需要访问网络,只是将固定的音乐分类显示出来即可。</p>    <h2>音乐列表</h2>    <p>在图11所示的音乐分类列表中单击某一个分类,就会显示该分类的音乐列表,接下来就来开发音乐列表的相关代码。</p>    <h2>开发页面文件</h2>    <p>音乐列表UI如图6所示,上方一个图片区域,下面是音乐列表。由于音乐列表的数量可能很多,一屏显示不完,因此使用scroll-view组件进行滚动显示。</p>    <p>打开list.wxml文件,在其中编写以下代码:</p>    <pre>  <code class="language-javascript"><scroll-view  scroll-y="true" >      <view class="board">          <image src="{{board}}" />      </view>        <view class="songlist">          <block wx:for="{{songlist}}"  wx:key="song_id">              <view class="songitem">                  <navigator url="/pages/play/play?songid={{item.songid}}"               class="song-play"><image src="/images/play.png" /></navigator>                  <navigator url="/pages/play/play?songid={{item.songid}}"                              class="song-detail">                      <view class="song-title">{{item.songname}}</view>                      <view class="song-subtitle">{{item.singername}} -                                 {{item.seconds}}</view>                  </navigator>              </view>          </block>      </view>      <loading hidden="{{!loading}}">      正在加载音乐……    </loading>  </scroll-view></code></pre>    <p>在以上代码中,首先使用image组件绑定了一个名为board的变量显示一幅图片(专辑封面图片);接下来显示分类的音乐列表,这里使用循环渲染songlist这个数组中的内容,将音乐的名称、歌手名称等信息显示出来,并通过navigator组件进行导航,当用户单击音乐时导航到play页面进行播放;最后,在下方添加了一个loading组件,用来显示加载音乐列表时的提示信息。</p>    <h2>开发页面样式文件</h2>    <p>根据上面的wxml文件中定义的class,编写对应的样式代码。打开list.wxss文件,在其中输入以下样式代码:</p>    <pre>  <code class="language-javascript">/*顶部专辑封面图片*/  .board image{      width: 100%;      height: 300px;      border-bottom: 1px solid #eee;  }    /*音乐列表*/  .songlist {    width: 100%;    overflow-x: hidden;    overflow-y: visible;    font-size: 0.8rem;  }    /*每一个音乐项目*/  .songitem{    height: 3rem;    line-height: 1.5rem;    display: flex;    border-bottom: 1px solid #eee;    padding: 10rpx;    width: 100%;  }    /*选择的音乐项目*/  .songitem:active {    background: #eee;  }    /*左侧的播放图标*/  .song-play {    width: 10%;    text-align: center;    vertical-align: middle;  }  .song-play image {    line-height: 3rem;    width: 50rpx;    height: 50rpx;    padding-top: 13px;  }  /*音乐项目的细节内容*/  .song-detail {    white-space: nowrap;    width: 90%;  }  /*音乐标题*/  .song-title {    font-size: 1rem;  }  /*副标题*/  .song-subtitle {    color: #555;  }</code></pre>    <p>以上样式代码中,每一项前面都有注释,与wxml对照分析,很快就能搞明白其作用,这里不再赘述。</p>    <h2>开发页面逻辑代码</h2>    <p>接下来开发页面的逻辑代码,打开list.js文件,输入以下代码:</p>    <pre>  <code class="language-javascript">var config=require('../../config.js'); //导入配置文件    //将秒数转换为分秒的表示形式  var formatSeconds = function(value) {      var time = parseFloat(value);      var m= Math.floor(time/60);      var s= time - m*60;         return  [m, s].map(formatNumber).join(':');        function formatNumber(n) {        n = n.toString()        return n[1] ? n : '0' + n      }  }    Page({    data:{      board:'', //顶部图片      songlist:[], //音乐列表      loading:false, //加载标志    },    //页面加载事件    onLoad:function(options){          var self = this;      var topid = options.type; //获取页面跳转传过来的参数        this.setData({        loading:true   //显示加载提示信息      })        //加载歌曲列表      wx.request({        url:config.config.hotUrl, //热门榜单接口        data:{topid:topid},       //歌曲类别编号          success:function(e){            if(e.statusCode == 200){            var songlist=e.data.showapi_res_body.pagebean.songlist;            //将时长转换为分秒的表示形式            for(var i=0;i<songlist.length;i++)            {              songlist[i].seconds = formatSeconds(songlist[i].seconds);            }              self.setData({              //获取第1首歌曲的图片作为该页顶部图片              board:e.data.showapi_res_body.pagebean.songlist[0].albumpic_big,              //保存歌曲列表              songlist:songlist,              loading:false //隐藏加载提示信息            });          //将歌曲列表保存到本地缓存中        wx.setStorageSync('songlist',songlist);          }        }      });    }    })</code></pre>    <p>以上代码大部分都添加了注释,参考注释应该很容易读懂。程序首先导入config.js文件,方便调用易源接口网站提供的API,接着定义了一个formatSeconds函数,该函数可以将以秒为单位表示的音乐时长转换为以分秒表示的形式。</p>    <p>在Page函数中,代码主要分两部分。一个数据初始化部分,定义了一个名为board的变量,用来保存页面顶部显示的专辑封面图片URL地址。另外一部分就是onLoad页面加载事件处理,这是本页面的核心代码。在这段代码中,首先从传入页面的参数type中获取音乐分类ID,然后调用易源接口提供的API获取对应分类的音乐列表,得到音乐列表之后,调用前面定义的formatSeconds将音乐时长转换为分秒表示的形式,然后将音乐列表更新到页面数据中,这样,页面上就会显示获取到的音乐列表。</p>    <p>在onLoad代码的最后,还将获取到的音乐列表缓存到本地。在音乐播放页面play中就可看到这里缓存数据的作用了。</p>    <p>这样,音乐列表页面的开发完成,在开发工具的模拟器中预览,首先显示上一小节开发的音乐分类列表(如图11所示)。单击一个分类之后,将显示如图12所示的音乐列表,上方显示的是第1首音乐的专辑封面图片。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/ab8eea958b247be277fb5e73efcbe745.png"></p>    <p>图12 音乐列表</p>    <h2>播放音乐</h2>    <p>在图12所示音乐列表中,单击一首音乐,将导航到播放音乐界面。下面开发播放音乐界面的相关代码。</p>    <h2>开发页面文件</h2>    <p>如图7所示的播放音乐界面比较简单,上方显示专辑图片,下方显示音乐名称、歌手名称和一个播放按钮即可。打开play.wxml文件,在其中编写以下代码:</p>    <pre>  <code class="language-javascript"><view class="playing container">      <view class="thumbnail">          <image src="{{song.albumpic_big}}" />      </view>      <view class="detail">          <view class="title">{{song.songname}}</view>          <view class="author">{{song.singername}}</view>          <view class="action">              <view class="act-toggle" bindtap="playToggle">                  <image src="/images/{{isPlaying ? 'pause' : 'play'}}.png" />              </view>          </view>      </view>  </view></code></pre>    <h2>开发页面样式文件</h2>    <p>根据play.wxml文件中设置的class属性,编写对应的样式文件。打开play.wxss文件,编写以下样式代码:</p>    <pre>  <code class="language-javascript">/*专辑封面图片*/  .thumbnail image{      height: 300px;      width: 100%;  }    /*音乐名称*/  .title{      text-align: center;      font-size: 1.3rem;      margin-top: 20rpx;      margin-bottom: 20rpx;  }    /*歌手名称*/  .author{      text-align: center;      color: #555;  }    /*播放*/  .action{      text-align: center;      margin-top: 1rem;    }    /*播放图标*/  .action image{      width: 200rpx;      height: 200rpx;  }</code></pre>    <p>样式文件中每个class前面都有相应的注释。</p>    <h2>开发页面逻辑代码</h2>    <p>播放音乐页面的代码相对较多,下面先进行简单分析。</p>    <p>在进入播放页面时,首先判断以下几种情况:</p>    <p>(1)未传入songid参数,如直接在下方tab中单击“正在播放”时进入该页面。这时,如果之前播放过音乐,则可继续播放之前那首音乐(要获取之前播放过的那首音乐,可将其缓存到本地)。如果之前没有播放过音乐,则显示“未选择歌曲”,按播放按钮时不起作用。</p>    <p>(2)若传入了songid参数,由于songid只是音乐中的一个编号,并没有音乐本身的相关信息(如音乐名称、歌手名称、音乐链接地址等)。只有这个编号,无法调用wx.playBackgroundAudio这个API进行播放。这时,list页面中将音乐列表songlist缓存在本地就有作用了。在play页面中,从缓存中取出songlist这个音乐列表,然后用songid在songlist这个数组中查询到相应的音乐,就将其取出来播放,同时,将该音乐缓存到本地,以备无songid参数传入时播放该音乐。</p>    <p>根据以上分析,在play.js中编写代码如下:</p>    <pre>  <code class="language-javascript">var config=require('../../config.js'); //导入配置文件    Page({    data:{      song:{},  //传入的歌曲信息      isPlaying:false, //播放状态    },      //页面载入事件处理函数    onLoad:function(options){      var self = this;          var songid = options.songid; //获取页面跳转传过来的参数(歌曲对象)        if(songid === undefined){ //未传入歌曲ID        var curSong=wx.getStorageSync('curSong') || {}; //从缓存中获取歌曲          if(curSong === undefined){ //缓存中无歌曲          var song={songname:'未选择歌曲'}; //显示未选择歌曲          this.setData({            song:song          })          }else{          this.setData({            song:curSong          });        }        }else{        var songlist=wx.getStorageSync('songlist') || []; //从缓存中取出歌曲列表        //在歌曲列表中查找songid指定的歌曲        for(var i=0;i<songlist.length;i++){           if(songlist[i].songid == songid){  //找到对应的歌曲                    this.setData({              song:songlist[i]   //更新歌曲            });            break;          }        }        //缓存正在播放的歌曲        wx.setStorageSync('curSong',this.data.song);      }    },        //播放/暂停    playToggle:function(){      var self = this;      //没有歌曲要播放,则直接退出      if(this.data.song.songname =='未选择歌曲'){        return;      }            if(this.data.isPlaying){ //正在播放        wx.stopBackgroundAudio(); //停止播放歌曲        }else{//未播放,则开始播放          //播放歌曲        wx.playBackgroundAudio({          dataUrl: this.data.song.url || this.data.song.m4a,          success: function(res){ }        })      }        //更新播放状态      this.setData({        isPlaying:!this.data.isPlaying      });     }    })</code></pre>    <p>以上代码大部分都添加了注释,配合前面的分析,应该很容易读明白。在wx.playBackgroundAudio函数的参数中,dataUrl的参数使用以下形式:</p>    <p>dataUrl: this.data.song.url || this.data.song.m4a,</p>    <p>这是因为,下一小节的搜索结果中返回的音乐文件是使用m4a来取得音乐的流媒体。</p>    <p>保存好播放音乐页面的相关文件之后,进行调试,在图12所示音乐列表中单击一首音乐,进入播放界面,如图13所示。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/aabaf7e054efefa8ee4da0fafc252fd5.png"></p>    <p>图13 音乐播放</p>    <p>在图13所示界面中,单击播放图标,即可听到音乐声,同时播放图标变为了暂停图标。</p>    <h2>搜索音乐</h2>    <p>最后,开发本案例的搜索音乐页面。</p>    <h2>开发页面文件</h2>    <p>参照图8所示,该界面很简单,在上方添加一个搜索输入框和一个搜索按钮,下面则是显示搜索结果的列表。打开search.wxml文件,在其中编写以下代码:</p>    <pre>  <code class="language-javascript"><view class="container">      <view class="search-area">           <input bindinput="inputing" placeholder="请输入搜索关键字"                value="{{value}}" />          <button type="primary" size="mini" bindtap="bindSearch"                 loading="{{loading}}"> 立即搜索 </button>      </view>        <view class="songlist">          <block wx:for="{{list}}" wx:key="{{index}}">              <view class="songitem">                  <navigator url="/pages/play/play?songid={{item.songid}}"                         class="song-play">                  <image src="/images/play.png" /></navigator>                  <navigator url="/pages/play/play?songid={{item.songid}}"                           class="song-detail">                      <view class="song-title">{{item.songname}}-                               {{item.singername}}</view>                      <view class="song-subtitle">{{item.albumname}}</view>                  </navigator>              </view>          </block>      </view>        <loading hidden="{{!loading}}">          正在搜索音乐...      </loading>  </view></code></pre>    <p>以上代码中,除了图8所示设计界面元素之外,在下方还添加了一个loading组件,用来显示提示信息。</p>    <h2>开发页面样式文件</h2>    <p>接着根据searrch.wxml中使用的class属性编写样式文件,从上面的代码可看出,其中很多class与list.wxml中定义的相同,因此,可以进行复用。最好的方法是将这两个页面中重复的class定义剪切粘贴到app.wxss文件中,这样,list.wxml和search.wxml这两个页面文件都可以使用这些样式了。</p>    <p>然后,将search.wxml中特有的class进行单独定义,打开search.wxss文件,编写以下样式代码:</p>    <pre>  <code class="language-javascript">.search-area{      background: #f4f4f4;      padding: 1rem 0.5rem;  }    .search-area input{      background: #fff;      border-radius: 3px;      height: 2rem;      line-height: 2rem;      margin-bottom: 0.5rem;      padding-left: 0.5rem;      font-size: 0.8rem;  }</code></pre>    <h2>开发页面逻辑代码</h2>    <p>接下来开发搜索页面的逻辑代码,打开search.js文件,编写以下代码:</p>    <pre>  <code class="language-javascript">var config=require('../../config.js');  //导入配置文件    Page({    data:{      value:'', //搜索关键字      loading:false, //按键前的loading图标      list:[], //搜索结果    },      //保存输入的关键字      inputing:function(e){      this.setData({        value:e.detail.value  //更新搜索关键字      });    },      //立即搜索按钮    bindSearch:function(){      var self=this;        this.setData({        loading:!self.data.loading //更新立即搜索按钮的loading图标      });        //开始搜索      wx.request({        url:config.config.searchByNameUrl, //搜索接口        data:{keyword:self.data.value},    //搜索关键字          success:function(e){                  if(e.statusCode == 200){ //搜索成功              self.setData({                          list:e.data.showapi_res_body.pagebean.contentlist,  //更新搜索结果              loading:!self.data.loading            });       //将歌曲列表保存到本地缓存中    wx.setStorageSync('songlist',e.data.showapi_res_body.pagebean.contentlist);          }        }      });    }  })</code></pre>    <p>以上代码首先导入配置文件。</p>    <p>接着初始化数据,在初始化数据部分定义了3个变量,value用来保存用户输入的查询关键字,而loading变量用来控制是否显示查询提示信息,list数组则用来保存查询到的音乐列表。</p>    <p>在搜索按钮的事件处理函数中,使用wx.request函数调用配置文件中定义的接口,并传入关键字进行搜索,如果搜索成功,则将音乐文件列表更新到list数组中,同时还要将音乐文件列表缓存到本地,方便play页面播放时查找。</p>    <p>虽然搜索结果中的音乐列表与list页面中的音乐列表有些字段不相同,但在播放页面中,已经进行了逻辑合并处理,因此,这些差异并不会影响play页面中的播放。</p>    <p>保存搜索页面的文件,最后就可以测试搜索的效果了。</p>    <p>在调试页面中,单击下方tab中的“搜索”进入搜索页面,输入搜索关键字,单击“立即搜索”按钮,下方将显示搜索的结果,如图14所示。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/9e828d9fa1739f4179c40eb3201e1411.png"></p>    <p>图14 搜索结果</p>    <p>在图14所示的列表中,单击某一首音乐,即可进入图13所示的播放界面,可播放收听该音乐。</p>    <p>至此,本案例开发完成。由于篇幅所限,这里就不截图显示测试的各界面了,读者可自行测试效果。</p>    <p> </p>    <p> </p>    <p>来自:http://geek.csdn.net/news/detail/188145</p>    <p> </p>