目录
1 前言
本人不是专门从事设计的人,但是空闲时间也会关注一些设计方面的知识。这段时间做了差不多4-5个小程序,基本上小程序上通用的接口都使用过了。本文对于在开发文档上已经描写清楚的不会过多进行讲述。
2 设计
2.1 写给新手设计师
如果你不是属于对于手机端设计很有心得的大牛的话,我还建议你先上微信公众号的小程序设计指南认真观看,网站如下:
微信设计指南
在文章的最后有提供微信风格的基础控件库下载,这个可以让你快速熟悉微信上所具有的一些控件。在首次做小程序设计的时候,尽量不要说是只做一个完完全全静态的小程序界面,我觉得小程序本身应该更加注重的是UE(即用户体验)和IxD(用户交互),所以你可在不考虑美观性多强前,先尝试着将控件放入设计中。
我个人觉得你的界面可以做的一般般,但是一个好的交互和体验,对于用户的感官会更加的舒服。而且我见过几个小程序设计图。我个人感觉小程序在美观上不如APP或web APP,也许是我做过的几套里的设计都太一般了吧。但是小程序的体验交互还是不错的。
2.2 尺寸说明
对于小程序的设计,个人比较建议还是以iphone6为主,我觉得这个尺寸下的图会更接近于手机上的效果,而且更方便与开发人员的开发。按照微信小程序配套的尺寸单位计算来看:
我们会发现iphon6的屏幕下对于尺寸的计算会更加方便,为2:1。
3 开发
3.1 概述
我们生成项目目录后,基本可以分为两类,一类以app命名的文件,就是作为一个全局配置文件,一类是自定义的page文件,像pages/log下定义的页面本身的文件。
关于page文件,一个page文件需包括wxml,wxss,js,json。其中wxml对应html界面,wxss对应css界面,json文件不是必选的,但一般可以全部创建出来。
3.2 关于JSON配置
JSON配置大致上可以分为两种。一种是页面page的JSON配置,另一种是全局app的JSON配置。
3.2.1 app.json文件
App.json文件存在于项目的根目录下,决定页面文件的路径、窗口表现、设置网络超时时间、设置多 tab。
有5项配置,分别是:pages、windows、tabBar、networkTimeout、debug.
Pages: 用于配置页面路径信息。接受一个字符串数组,每一项都对应页面(路径+文件名)信息,数组的第一项作为首页进行访问。小程序增减页面都需要对pages进行修改,否则会出现错误。文件名不需要写后缀wxml,wxss,js,json,因为框架会自动同一个路径下的4个文件。
例如: “/pages/example/example”,其中前面的/pages/example是作为路径,而后面的example就是page文件名,框架会自动加载/pages/example/下面的所有example,且后缀为wxml,wxss,js,json的文件。
Windows: 用于设置小程序的状态栏、导航条、标题、窗口背景色。
TabBar: 如果小程序是一个多 tab 应用(客户端窗口的底部或顶部有 tab 栏可以切换页面),可以通过 tabBar 配置项指定 tab 栏的表现,以及 tab 切换时显示的对应页面。
tabBar 中的 list 是一个数组,只能配置最少2个、最多5个 tab,tab 按数组的顺序排序。
如果你需要动态设置tabBar的路径信息,那么这个将无法满足需求,这个配置在app.json文件中,无法快捷的在程序执行期间进行切换
3.2.2 page.json文件
每一个小程序页面也可以使用.json文件来对本页面的窗口表现进行配置。 页面的配置比app.json全局配置简单得多,只是设置 app.json 中的 window 配置项的内容,页面中配置项会覆盖 app.json 的 window 中相同的配置项。 页面的.json只能设置 window 相关的配置项,以决定本页面的窗口表现。
如果页面中需要加载自定义组件那么将会使用到一个属性usingComponents属性。这个将在后面的自定义组件中讲到。
3.3 API开发
3.3.1 蓝牙开发
3.3.1.1 概述
因为写过关于蓝牙智能家居的项目,所以会将重点放在这里。
蓝牙智能家居连接的业务逻辑如下,
- 初始化蓝牙设备
- 打开蓝牙搜索服务
- 搜索蓝牙列表
- 连接设备
- 查找、匹配服务
- 查找、匹配特征
- 打开订阅
- 请求握手,写入设备
- 监听设备返回信息进行处理
3.3.1.2 使用方法
- 从git上下载开发库GIT地址
- 将BleTool.js和Tool.js放入在小程序项目下的lib文件(否则需要修改成自己的文件路径)
- 在App.js文件中引入BleTool.js文件
const BLEManage = require('lib/BleTool.js')
- 在App.js文件中onLaunch初始化蓝牙库
this.globalData.bleManage = new BLEManage(this)
- 使用方法:
获取设备列表:
this.globalData.bleManage.getDeviceList([搜索次数],[搜索间隔],[回调方法])
查找并连接设备:
this.globalData.bleManage.getDevices([设备MAC],[回调方法],[监听响应参数])
3.3.1.3 注意事项
- 因为最好不用一直保持蓝牙设备在线,所以需要监听蓝牙连接状态,但可以根据需求选择。
- 最好将蓝牙的状态保存下来,将蓝牙类绑定到app.globaldata下面,方便全局 操控蓝牙。
- 当搜索到设备后,这时设备掉线,会出现无法连接到设备,但是从设备列表能看到设备的问题,需要重启适配器再搜索即可正常。
- 推出小程序的时候,最好将小程序的蓝牙状态保存,然后等切回小程序的时候,在进行断开重连,确保小程序在后台运行的时候,蓝牙的状态信息保持一致。否则会出现异常。
- IOS系统更新13.1以后,出现蓝牙需要开放授权。位置是:设置->隐私->蓝牙->对应的应用。
3.3.1.4 代码说明
- 首先,初始化需要保存的蓝牙状态,这个将用于获取和监听全局的蓝牙状态。
constructor() {
let that = this;
//记录自身,防止某些函数this指向问题
that.self = this;
//是否开启调试
that.debug = false;
//操作状态控制
that.onlyOne = false; //唯一控制
that.silent = false; //静默连接
that.rootChangeStop = false; //路由修改是否停止
that.canRun = false; //蓝牙库是否可以继续运行
that.rootPage = null;
//属性
that.available = false;
that.searchState = false;
that.platform = null;
that.connectState = false;
//蓝牙搜索计时器
that.getDeviceAllTime = 3;
that.getDeviceTime = that.getDeviceAllTime;
that.getDeviceTimeState = true;
that.getDeviceTimeOut = null;
that.getDeviceRSSICount = 0;
that.getCanDevice = false;
//蓝牙连接计时
that.getConnectDeviceAllTime = 2;
that.getConnectDeviceTime = that.getConnectDeviceAllTime;
that.getConnectDeviceTimeState = true;
that.connectId = null;
//显示计时器
that.showMentionTimer = null;
that.listenAdapterStateChange();
that.listenConnection();
that.openBLEAdapter(function() {
that.closeBeforeConnect();
});
- 初始化了蓝牙对象的时候,打开适配器;适配器打开成功,则开启搜索服务;打开失败则,提示用户需要打开蓝牙。
listenAdapterStateChange 则是用来监听适配器的状态改变,如果用户关闭蓝牙,则方法会返回false ,这时我们就可以将蓝牙对象的openAdapter的状态标记为false,即未开启,这里的延时1s搜索是因为说是有些设备可能存在状态先响应然后再启动的现象,然后就无法搜索到设备了。
/**
* 打开蓝牙适配器
*/
openBLEAdapter(callBack) {
let that = this
console.log("openBLEAdapter")
wx.openBluetoothAdapter({
success: function(res) {
console.log('openBLEAdapter_success')
that.checkAvailable({
toast: true
}, true)
that.showMention("开启蓝牙成功", true);
// setTimeout(function() {
// that.startSearchDevice(callBack);
// }, 1000)
callBack();
},
fail: function(res) {
console.log('openBLEAdapter_fail', res)
that.checkAvailable({
toast: true
}, false)
}
})
}
/**
* 监听适配器状态
*/
listenAdapterStateChange() {
var that = this
wx.onBluetoothAdapterStateChange(function(res) {
console.log("listenAdapterStateChange", res)
that.searchState = res.discovering
let available = that.checkAvailable({
toast: true
}, res.available);
if (!res.available) {
that.onlyOne = false; //唯一控制
that.silent = false; //静默连接
that.rootChangeStop = false; //路由修改是否停止
}
// if (res.available) {
// // if (!that.available) {
// that.available = res.available
// // that.startSearchDevice();
// // }
// } else {
// that.available = res.available
// console.log("请打开蓝牙2")
// that.showMention('请打开蓝牙2', true)
// }
})
}
/**
* 开始搜索
*/
startSearchDevice(callBack) {
let that = this;
console.log("startSearchDevice")
if (that.checkAvailable({})) {
if (!that.searchState) {
// if (that.platform == "ios") {
console.log("延时1s搜索")
that.showMention('准备搜索设备', true, 1)
setTimeout(function() {
that.wechatSearchDevice(callBack);
}, 1000)
// } else {
// console.log("立即搜索")
// that.wechatSearchDevice();
// }
}
} else {
that.showMention("请打开蓝牙", true);
}
}
/**
* 开始搜索微信方法
*/
wechatSearchDevice(callBack) {
let that = this;
wx.startBluetoothDevicesDiscovery({
// services: [MainService],
success: function(res) {
that.searchState = true;
console.log("成功,准备开始搜索: ")
console.log(res)
that.showMention('开始搜索设备', true)
if (callBack != undefined) {
callBack();
}
},
fail: function(res) {
that.available = false
that.searchState = false;
console.log("开启搜索失败: ")
that.showToast({
title: '开启搜索失败',
})
}
})
}
- 搜索蓝牙设备的2种方式
开始搜索蓝牙设备了,如果你需要从周围可能存在的蓝牙设备中,选取你想要的,那么你可以在蓝牙设备的名字上动动手脚了。比如,你可加上前缀FFFF-这样的。然后 判断获取到的名字是否包含FFFF-前缀,有就加入到显示列表里面。
//微信获取设备列表
getDeviceList(count, time, callBack) {
let that = this;
that.searchCount = count;
// that.searchState = true;
if (that.searchInterval != null) {
clearInterval(that.searchInterval);
that.searchInterval = null;
}
that.searchInterval = setInterval(function() {
if (that.checkRoutePageChange()) {
return;
}
that.showMention("第" + (count - that.searchCount + 1) + "次搜索", false)
if (that.searchCount > 0) {
wx.getBluetoothDevices({
success: function(res) {
console.log(635, res)
}
})
that.searchCount--;
return;
} else {
that.showMention("搜索成功", true, 2)
clearInterval(that.searchInterval);
wx.getBluetoothDevices({
success: function(res) {
console.log("getDeviceListSuccess", res)
callBack(res.devices);
}
})
}
}, time * 1000)
}
也许你有时候需要的是查找并连接指定的设备,那么你可以通过下面这样的形式
/**
* 获取设备
*/
getDevices(mac, callBack, listenFun, options) {
let that = this;
console.log("getDevices", options)
mac = mac.toLowerCase(); //转换小写
if (!that.checkAvailable({
toast: true
})) {
return;
}
//检查是否有蓝牙操作在运行
if (that.checkOnlyOne({
toast: true
}, true)) {
return;
}
//配置属性
// that.onlyOne = true;
console.log("typeof options", typeof options)
if (typeof options == "object") {
that.slient = options.slient || false;
that.rootChangeStop = options.rootChangeStop || false;
} else {
that.slient = false;
that.rootChangeStop = false;
}
that.callBack = function() {
that.onlyOne = false;
callBack()
}
that.listenFun = listenFun
if (that.connectState && that.mac == mac) {
that.callBack()
return;
}
that.startSearchDevice();
that.rootPage = getCurrentPages()[getCurrentPages().length - 1].route;
that.showMention("开始搜索", true, 3)
that.resetSearchDevice(mac);
}
- 连接设备
蓝牙连接的过程就是不断根据与蓝牙设备所定好的一些协议,服务值,特征值等,主要是为了保证安全性。
/**
* 创建连接
*/
connectDevice(msg) {
let that = this;
console.log("connectDevice")
if (that.checkRoutePageChange()) {
return;
}
msg = msg == undefined ? '连接中' : msg;
that.showMention(msg, false)
wx.createBLEConnection({
deviceId: that.connectId,
success: function(res) {
console.log(res)
console.log("连接成功")
that.showMention('连接成功', true)
that.connectState = true;
that.searchService();
console.log("保存", that.connectId);
tool.storage.save("connectId", that.connectId)
},
fail: function(res) {
console.log("miss", res)
that.connectState = false;
that.getConnectDeviceTime--;
if (that.checkRoutePageChange()) {
return;
}
if (!that.checkAvailable({})) {
that.showMention("蓝牙关闭", true, 2)
} else {
if (that.getConnectDeviceTime > 0) {
that.showMention("连接失败", false)
setTimeout(function() {
// that.connectDevice("第" + (that.getConnectDeviceAllTime - that.getConnectDeviceTime) + "次重连中");
that.connectDevice("重连中");
}, 1000);
} else {
that.showMention("重连失败", true, 2);
wx.showModal({
title: '温馨提示',
content: '请确认设备正常通电,即将重启蓝牙适配器',
success: function(res) {
if (res.confirm) {
console.log('用户点击确定,开始重启重连')
that.restartBleAdapter(function() {
// that.getConnectDeviceTime = that.getConnectDeviceAllTime;
// that.connectDevice()
that.resetSearchDevice(that.mac)
})
} else if (res.cancel) {
console.log('用户点击取消, 取消重启重连')
}
}
})
}
}
}
})
}
/**
* 搜索服务
*/
searchService() {
console.log("searchService")
let that = this;
that.showMention('数据处理中', true, 3)
wx.getBLEDeviceServices({
//服务uid
deviceId: that.connectId,
success: function(res) {
console.log(res)
for (let i = 0; i < res.services.length; i++) {
let rs = res.services[i]
if (rs.uuid.indexOf(MainService) > -1) {
console.log('找到匹配服务', res.services[i].uuid)
that.mainservice = res.services[i].uuid;
that.characteristic()
break;
}
}
},
fail: function() {
wx.hideLoading();
console.log("搜索服务失败");
that.showToast({
title: '搜索服务失败',
})
}
})
}
/**
* 特征值获取
*/
characteristic() {
console.log("characteristic")
let that = this
wx.getBLEDeviceCharacteristics({
deviceId: that.connectId,
serviceId: that.mainservice,
success: function(res) {
console.log("特征id:", res)
for (let i = 0; i < res.characteristics.length; i++) { //写入的特征值
let ct = res.characteristics[i]
if (ct.uuid.indexOf(UpdateCharacteristic) > -1) {
console.log('找到更新匹配特征', ct.uuid)
that.updateuuid = ct.uuid;
that.openNotify(ct.uuid);
} else if (ct.uuid.indexOf(WriteCharacteristic) > -1) {
console.log('找到写匹配特征', ct.uuid)
that.writeuuid = ct.uuid;
} else if (ct.uuid.indexOf(ReadCharacteristic) > -1) {
console.log('找到读匹配特征', ct.uuid)
that.readuuid = ct.uuid
}
}
//连接成功后,不保持静默方式
that.slient = false;
// setTimeout(function() {
that.callBack();
// }, 1000)
},
fail: function(res) {
console.log('找不到特征值')
console.log(res);
wx.hideLoading();
that.showToast({
title: '找不到特征值',
})
}
})
}
/**
* 打开订阅
*/
openNotify(uuid) {
var that = this
console.log("openNotify")
wx.notifyBLECharacteristicValueChange({
deviceId: that.connectId,
serviceId: that.mainservice,
characteristicId: uuid,
state: true,
success: function(res) {
console.log("notify打开成功", res)
that.listenNotifyValueChange()
// console.log()
// wx.onBLECharacteristicValueChange(function (res) {
// console.log("订阅的值",res)
// })
},
fail: function(res) {
console.log("notify打开失败");
console.log(res)
wx.hideLoading();
that.showToast({
title: 'notify打开失败',
})
},
})
}
- 下面是关于一些基本的操作和监听
/**
* 写入数据
*/
writeCharacteristicValue(str, func) {
console.log("writeCharacteristicValue")
let that = this;
if (that.checkRoutePageChange()) {
return;
}
// console.log(that.connectId, that.mainservice, that.writeuuid, str);
if (!that.connectState) {
tool.showMention("未连接设备")
return;
}
func = func || function() {};
let resObj = {}
wx.writeBLECharacteristicValue({
deviceId: that.connectId,
serviceId: that.mainservice,
characteristicId: that.writeuuid,
value: tool.string2Buffer(str),
success: function(res) {
that.showToast({
title: '写入成功',
})
console.log("writeDate-->", res);
resObj.state = "success"
func(resObj)
},
fail: function() {
console.log("写入失败")
resObj.state = "fail"
func(resObj)
}
})
}
/**
* 监听特征值
*/
listenNotifyValueChange() {
console.log("listenNotifyValueChange")
var that = this
console.log("监听特征变化")
wx.onBLECharacteristicValueChange(function(res) {
console.log("收到数据")
// console.log('WriteDataType = ' + WriteDataType)
let value = res.value
console.log(value);
let value2 = tool.uint8Array2Str(value)
console.log("returnstr", value2)
let dataView = new DataView(tool.string2Buffer(value2))
that.listenFun(dataView);
// return;
})
}
/**
* 监听连接状态
*/
listenConnection() {
let that = this;
console.log("listenConnection")
wx.onBLEConnectionStateChange(function(res) {
console.log("connectState", res);
that.connectState = res.connected;
if (res.connected) {
that.showToast({
title: "连接成功",
})
} else {
console.log(that.available, false)
// if (that.available) {
// that.startSearchDevice()
// }
that.showToast({
title: "连接断开",
})
}
})
}
- 一些基本的字符串和数组的转换操作
//Tool.js
/*
16进制字符串转整形数组
*/
function str2Bytes(str) {
var len = str.length;
if (len % 2 != 0) {
return null;
}
var hexA = new Array();
for (var i = 0; i < len; i += 2) {
var s = str.substr(i, 2);
var v = parseInt(s, 16);
hexA.push(v);
}
return hexA;
}
/*
整形数组转buffer
*/
function array2Buffer(arr) {
let buffer = new ArrayBuffer(arr.length)
let dataView = new DataView(buffer)
for (let i = 0; i < arr.length; i++) {
dataView.setUint8(i, arr[i])
}
return buffer
}
/*
16进制字符串转数组
*/
function string2Buffer(str) {
let arr = str2Bytes(str);
return array2Buffer(arr)
}
/*
ArrayBuffer转十六进制字符串
*/
function uint8Array2Str(buffer) {
var str = "";
let dataView = new DataView(buffer)
for (let i = 0; i < dataView.byteLength; i++) {
var tmp = dataView.getUint8(i).toString(16)
if (tmp.length == 1) {
tmp = "0" + tmp
}
str += tmp
}
return str;
}
3.3.2 camera
在编写摄像机组件的时候,我发现了关于手机设备的兼容问题,拍照后返回的临时图片路径,将他显示到界面上,会出现明显的颜色变换,像是人会变阿凡达那样。问题没找出,因为大部分的机型不会有该问题,已在酷派手机上发现,可能是系统问题.
老规矩写的不清晰的地方,可以指出,我会不定时更新和修改。一起加油.
所有的伟大,都来自于一个勇敢的开始。