微信开发公众平台自定义菜单需要花钱认证才能实现,不想花钱只能玩测试账号了,不过这并不影响开发。我的开发都是基于柳峰老师的微信公众平台应用开发做的。

只要我们使用公众平台测试账号就可以开发自定义菜单了,比较方便,测试账号开放了很多接口,很方便。

在开发自定义菜单的时候可以参考微信公众平台开发者文档的自定义菜单创建。

一、自定义菜单

1、自定义菜单最多包括3个一级菜单,每个一级菜单最多包含5个二级菜单。

2、一级菜单最多4个汉字,二级菜单最多7个汉字,多出来的部分将会以“...”代替。

3、创建自定义菜单后,菜单的刷新策略是,在用户进入公众号会话页或公众号profile页时,如果发现上一次拉取菜单的请求在5分钟以前,就会拉取一下菜单,如果菜单有更新,就会刷新客户端的菜单。测试时可以尝试取消关注公众账号后再次关注,则

可以看到创建后的效果。

自定义菜单接口可实现多种类型按钮,如下:

1、click:点击推事件

用户点击click类型按钮后,微信服务器会通过消息接口推送消息类型为event 的结构给开发者(参考消息接口指南),并且带上按钮中开发者填写的key值,开发者可以通过自定义的key值与用户进行交互;

2、view:跳转URL

用户点击view类型按钮后,微信客户端将会打开开发者在按钮中填写的网页URL,可与网页授权获取用户基本信息接口结合,获得用户基本信息。

3、scancode_push:扫码推事件

用户点击按钮后,微信客户端将调起扫一扫工具,完成扫码操作后显示扫描结果(如果是URL,将进入URL),且会将扫码的结果传给开发者,开发者可以下发消息。

4、scancode_waitmsg:扫码推事件且弹出“消息接收中”提示框

用户点击按钮后,微信客户端将调起扫一扫工具,完成扫码操作后,将扫码的结果传给开发者,同时收起扫一扫工具,然后弹出“消息接收中”提示框,随后可能会收到开发者下发的消息。

5、pic_sysphoto:弹出系统拍照发图

用户点击按钮后,微信客户端将调起系统相机,完成拍照操作后,会将拍摄的相片发送给开发者,并推送事件给开发者,同时收起系统相机,随后可能会收到开发者下发的消息。

6、pic_photo_or_album:弹出拍照或者相册发图

用户点击按钮后,微信客户端将弹出选择器供用户选择“拍照”或者“从手机相册选择”。用户选择后即走其他两种流程。

7、pic_weixin:弹出微信相册发图器

用户点击按钮后,微信客户端将调起微信相册,完成选择操作后,将选择的相片发送给开发者的服务器,并推送事件给开发者,同时收起相册,随后可能会收到开发者下发的消息。

8、location_select:弹出地理位置选择器

用户点击按钮后,微信客户端将调起地理位置选择工具,完成选择操作后,将选择的地理位置发送给开发者的服务器,同时收起位置选择工具,随后可能会收到开发者下发的消息。

9、media_id:下发消息(除文本消息)

用户点击media_id类型按钮后,微信服务器会将开发者填写的永久素材id对应的素材下发给用户,永久素材类型可以是图片、音频、视频、图文消息。请注意:永久素材id必须是在“素材管理/新增永久素材”接口上传后获得的合法id。

10、view_limited:跳转图文消息URL

用户点击view_limited类型按钮后,微信客户端将打开开发者在按钮中填写的永久素材id对应的图文消息URL,永久素材类型只支持图文消息。请注意:永久素材id必须是在“素材管理/新增永久素材”接口上传后获得的合法id

二、访问自定义菜单接口

1:获取access_token

自定义菜单的创建、查询和删除需要调用公众平台开放的自定义菜单接口,而调用该接口需要先获取access_token(接口访问凭证),这些接口全都是基于https协议的,因此我们先要解决如何在Java程序中发送https请求的问题。

获取接口访问凭证access_token:

获取access_token是通过GET方式访问如下链接:

https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET

链接中有三个参数,分别是grant_type、appid和secret。根据图中的参数说明,grant_type传固定值client_credential,而appid和secret就是申请完自定义菜单后微信分配给我们的。

请求发送成功后,微信服务器会返回一个json串,包含access_token和expires_in两个元素。其中,access_token就是我们最终需要的凭证,而expires_in是凭证的有效期,单位是秒,7200秒也就是2个小时。这就意味着,不是每次访问特殊接口,都需要重新获取一次access_token,只要access_token还在有效期内,就一直可以使用。

2:菜单创建

创建菜单要调用菜单接口

接口调用请求说明

http请求方式:POST(请使用https协议) https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN

其实就是向地址https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN以POST方式提交一个JSON格式的菜单字符串。

三、封装通用请求方法(自定义信任管理器)

创建菜单需要两个接口,一个是获取access_token接口,一个是自定义菜单接口,都是https请求。

1:创建证书信任管理器

对于https请求,我们需要一个证书信任管理器,这个管理器类需要自己定义,但需要实现X509TrustManager接口,代码如下:

package org.liufeng.weixin.util; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import javax.net.ssl.X509TrustManager; /** * 证书信任管理器(用于https请求) * * @author liufeng * @date 2013-08-08 */ public class MyX509TrustManager implements X509TrustManager { public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { } public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { } public X509Certificate[] getAcceptedIssuers() { return null; } } 

证书管理器的作用就是信任指定所有证书。

2:创建通用https请求

通用https请求应该

1)支持HTTPS请求;

2)支持GET、POST两种方式;

3)支持参数提交,也支持无参数的情况;

package org.liufeng.weixin.util; import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.ConnectException; import java.net.URL; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManager; import net.sf.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * 公众平台通用接口工具类 * * @author liuyq * @date 2013-08-09 */ public class WeixinUtil { private static Logger log = LoggerFactory.getLogger(WeixinUtil.class); /** * 发起https请求并获取结果 * * @param requestUrl 请求地址 * @param requestMethod 请求方式(GET、POST) * @param outputStr 提交的数据 * @return JSONObject(通过JSONObject.get(key)的方式获取json对象的属性值) */ public static JSONObject httpRequest(String requestUrl, String requestMethod, String outputStr) { JSONObject jsonObject = null; StringBuffer buffer = new StringBuffer(); try { // 创建SSLContext对象,并使用我们指定的信任管理器初始化 TrustManager[] tm = { new MyX509TrustManager() }; SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE"); sslContext.init(null, tm, new java.security.SecureRandom()); // 从上述SSLContext对象中得到SSLSocketFactory对象 SSLSocketFactory ssf = sslContext.getSocketFactory(); URL url = new URL(requestUrl); HttpsURLConnection httpUrlConn = (HttpsURLConnection) url.openConnection(); httpUrlConn.setSSLSocketFactory(ssf); httpUrlConn.setDoOutput(true); httpUrlConn.setDoInput(true); httpUrlConn.setUseCaches(false); // 设置请求方式(GET/POST) httpUrlConn.setRequestMethod(requestMethod); if ("GET".equalsIgnoreCase(requestMethod)) httpUrlConn.connect(); // 当有数据需要提交时 if (null != outputStr) { OutputStream outputStream = httpUrlConn.getOutputStream(); // 注意编码格式,防止中文乱码 outputStream.write(outputStr.getBytes("UTF-8")); outputStream.close(); } // 将返回的输入流转换成字符串 InputStream inputStream = httpUrlConn.getInputStream(); InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8"); BufferedReader bufferedReader = new BufferedReader(inputStreamReader); String str = null; while ((str = bufferedReader.readLine()) != null) { buffer.append(str); } bufferedReader.close(); inputStreamReader.close(); // 释放资源 inputStream.close(); inputStream = null; httpUrlConn.disconnect(); jsonObject = JSONObject.fromObject(buffer.toString()); } catch (ConnectException ce) { log.error("Weixin server connection timed out."); } catch (Exception e) { log.error("https request error:{}", e); } return jsonObject; } } 

四、封装菜单实体类

1:按钮的分类

我们通常会把自定义菜单项看做是按钮,按钮的类型分成click(点击事件)和view(访问网页)。

click类型的按钮有type、name和key3个属性,而view类型的按钮有type、name和url3个属性

2:首先是调用获取凭证接口后,微信服务器会返回json格式的数据:{"access_token":"ACCESS_TOKEN","expires_in":7200},我们将其封装为一个AccessToken对象,对象有二个属性:token和expiresIn,代码如下:

package org.liufeng.weixin.pojo; /** * 微信通用接口凭证 * * @author liufeng * @date 2013-08-08 */ public class AccessToken { // 获取到的凭证 private String token; // 凭证有效时间,单位:秒 private int expiresIn; public String getToken() { return token; } public void setToken(String token) { this.token = token; } public int getExpiresIn() { return expiresIn; } public void setExpiresIn(int expiresIn) { this.expiresIn = expiresIn; } } 

接下来是对菜单结构的封装。因为我们是采用面向对象的编程方式,最终提交的json格式菜单数据就应该是由对象直接转换得到,而不是在程序代码中拼一大堆json数据。菜单结构封装的依据是公众平台API文档中给出的那一段json格式的菜单结构,如下所示:

click和view的请求示例

{"button":[{ "type":"click","name":"今日歌曲","key":"V1001_TODAY_MUSIC"},{"name":"菜单","sub_button":[{ "type":"view","name":"搜索","url":"http://www.soso.com/"},{"type":"view","name":"视频","url":"http://v.qq.com/"},{"type":"click","name":"赞一下我们","key":"V1001_GOOD"}]}]}

3:封装菜单结构

每个按钮对象都要一个共同的name属性,因此需要定义一个按钮对象基类,所有的按钮对象都需要继承该类。基类的代码如下:

package org.liufeng.weixin.pojo; /** * 按钮的基类 * * @author liufeng * @date 2013-08-08 */ public class Button { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } } 

接着是子菜单项的封装。这里对子菜单是这样定义的:没有子菜单的菜单项,有可能是二级菜单项,也有可能是不含二级菜单的一级菜单。这类子菜单项一定会包含三个属性:type、name和key,封装的代码如下:

package org.liufeng.weixin.pojo; /** * 普通按钮(子按钮) * * @author liufeng * @date 2013-08-08 */ public class CommonButton extends Button { private String type; private String key; public String getType() { return type; } public void setType(String type) { this.type = type; } public String getKey() { return key; } public void setKey(String key) { this.key = key; } } 

对父菜单项的定义:包含有二级菜单项的一级菜单。这类菜单项包含有二个属性:name和sub_button,而sub_button以是一个子菜单项数组。父菜单项的封装代码如下:

package org.liufeng.weixin.pojo; /** * 复杂按钮(父按钮) * * @author liufeng * @date 2013-08-08 */ public class ComplexButton extends Button { private Button[] sub_button; public Button[] getSub_button() { return sub_button; } public void setSub_button(Button[] sub_button) { this.sub_button = sub_button; } } 

对整个菜单进行封装,菜单对象包含多个菜单项(最多只能有3个),这些菜单项即可以是子菜单项(不含二级菜单的一级菜单),也可以是父菜单项(包含二级菜单的菜单项)

package org.liufeng.weixin.pojo; /** * 菜单 * * @author liufeng * @date 2013-08-08 */ public class Menu { private Button[] button; public Button[] getButton() { return button; } public void setButton(Button[] button) { this.button = button; } } 

这样我们就完成了菜单实体类的封装。

凭证access_token的获取方法

继续在先前通用请求方法的类WeixinUtil.java中加入以下代码,用于获取接口访问凭证:

// 获取access_token的接口地址(GET) 限200(次/天) public final static String access_token_url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET"; /** * 获取access_token * * @param appid 凭证 * @param appsecret 密钥 * @return */ public static AccessToken getAccessToken(String appid, String appsecret) { AccessToken accessToken = null; String requestUrl = access_token_url.replace("APPID", appid).replace("APPSECRET", appsecret); JSONObject jsonObject = httpRequest(requestUrl, "GET", null); // 如果请求成功 if (null != jsonObject) { try { accessToken = new AccessToken(); accessToken.setToken(jsonObject.getString("access_token")); accessToken.setExpiresIn(jsonObject.getInt("expires_in")); } catch (JSONException e) { accessToken = null; // 获取token失败 log.error("获取token失败 errcode:{} errmsg:{}", jsonObject.getInt("errcode"), jsonObject.getString("errmsg")); } } return accessToken; } 

自定义菜单的创建方法

继续在先前通用请求方法的类WeixinUtil.java中加入以下代码,用于创建自定义菜单:

// 菜单创建(POST) 限100(次/天) public static String menu_create_url = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN"; /** * 创建菜单 * * @param menu 菜单实例 * @param accessToken 有效的access_token * @return 0表示成功,其他值表示失败 */ public static int createMenu(Menu menu, String accessToken) { int result = 0; // 拼装创建菜单的url String url = menu_create_url.replace("ACCESS_TOKEN", accessToken); // 将菜单对象转换成json字符串 String jsonMenu = JSONObject.fromObject(menu).toString(); // 调用接口创建菜单 JSONObject jsonObject = httpRequest(url, "POST", jsonMenu); if (null != jsonObject) { if (0 != jsonObject.getInt("errcode")) { result = jsonObject.getInt("errcode"); log.error("创建菜单失败 errcode:{} errmsg:{}", jsonObject.getInt("errcode"), jsonObject.getString("errmsg")); } } return result; } 

调用封装的方法创建自定义菜单

package org.liufeng.weixin.main; import org.liufeng.weixin.pojo.AccessToken; import org.liufeng.weixin.pojo.Button; import org.liufeng.weixin.pojo.CommonButton; import org.liufeng.weixin.pojo.ComplexButton; import org.liufeng.weixin.pojo.Menu; import org.liufeng.weixin.util.WeixinUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * 菜单管理器类 * * @author liufeng * @date 2013-08-08 */ public class MenuManager { private static Logger log = LoggerFactory.getLogger(MenuManager.class); public static void main(String[] args) { // 第三方用户唯一凭证 String appId = "000000000000000000"; // 第三方用户唯一凭证密钥 String appSecret = "00000000000000000000000000000000"; // 调用接口获取access_token AccessToken at = WeixinUtil.getAccessToken(appId, appSecret); if (null != at) { // 调用接口创建菜单 int result = WeixinUtil.createMenu(getMenu(), at.getToken()); // 判断菜单创建结果 if (0 == result) log.info("菜单创建成功!"); else log.info("菜单创建失败,错误码:" + result); } } /** * 组装菜单数据 * * @return */ private static Menu getMenu() { CommonButton btn11 = new CommonButton(); btn11.setName("天气预报"); btn11.setType("click"); btn11.setKey("11"); CommonButton btn12 = new CommonButton(); btn12.setName("公交查询"); btn12.setType("click"); btn12.setKey("12"); CommonButton btn13 = new CommonButton(); btn13.setName("周边搜索"); btn13.setType("click"); btn13.setKey("13"); CommonButton btn14 = new CommonButton(); btn14.setName("历史上的今天"); btn14.setType("click"); btn14.setKey("14"); CommonButton btn21 = new CommonButton(); btn21.setName("歌曲点播"); btn21.setType("click"); btn21.setKey("21"); CommonButton btn22 = new CommonButton(); btn22.setName("经典游戏"); btn22.setType("click"); btn22.setKey("22"); CommonButton btn23 = new CommonButton(); btn23.setName("美女电台"); btn23.setType("click"); btn23.setKey("23"); CommonButton btn24 = new CommonButton(); btn24.setName("人脸识别"); btn24.setType("click"); btn24.setKey("24"); CommonButton btn25 = new CommonButton(); btn25.setName("聊天唠嗑"); btn25.setType("click"); btn25.setKey("25"); CommonButton btn31 = new CommonButton(); btn31.setName("Q友圈"); btn31.setType("click"); btn31.setKey("31"); CommonButton btn32 = new CommonButton(); btn32.setName("电影排行榜"); btn32.setType("click"); btn32.setKey("32"); CommonButton btn33 = new CommonButton(); btn33.setName("幽默笑话"); btn33.setType("click"); btn33.setKey("33"); ComplexButton mainBtn1 = new ComplexButton(); mainBtn1.setName("生活助手"); mainBtn1.setSub_button(new CommonButton[] { btn11, btn12, btn13, btn14 }); ComplexButton mainBtn2 = new ComplexButton(); mainBtn2.setName("休闲驿站"); mainBtn2.setSub_button(new CommonButton[] { btn21, btn22, btn23, btn24, btn25 }); ComplexButton mainBtn3 = new ComplexButton(); mainBtn3.setName("更多体验"); mainBtn3.setSub_button(new CommonButton[] { btn31, btn32, btn33 }); /** * 这是公众号xiaoqrobot目前的菜单结构,每个一级菜单都有二级菜单项 * * 在某个一级菜单下没有二级菜单的情况,menu该如何定义呢?* 比如,第三个一级菜单项不是“更多体验”,而直接是“幽默笑话”,那么menu应该这样定义:* menu.setButton(new Button[] { mainBtn1, mainBtn2, btn33 }); */ Menu menu = new Menu(); menu.setButton(new Button[] { mainBtn1, mainBtn2, mainBtn3 }); return menu; } } 

注意:在运行以上代码时,需要将appId和appSecret换成你自己公众号的。

响应菜单点击事件

package org.liufeng.course.service; import java.util.Date; import java.util.Map; import javax.servlet.http.HttpServletRequest; import org.liufeng.course.message.resp.TextMessage; import org.liufeng.course.util.MessageUtil; /** * 核心服务类 * * @author liufeng * @date 2013-05-20 */ public class CoreService { /** * 处理微信发来的请求 * * @param request * @return */ public static String processRequest(HttpServletRequest request) { String respMessage = null; try { // 默认返回的文本消息内容 String respContent = "请求处理异常,请稍候尝试!"; // xml请求解析 Map<String, String> requestMap = MessageUtil.parseXml(request); // 发送方帐号(open_id) String fromUserName = requestMap.get("FromUserName"); // 公众帐号 String toUserName = requestMap.get("ToUserName"); // 消息类型 String msgType = requestMap.get("MsgType"); // 回复文本消息 TextMessage textMessage = new TextMessage(); textMessage.setToUserName(fromUserName); textMessage.setFromUserName(toUserName); textMessage.setCreateTime(new Date().getTime()); textMessage.setMsgType(MessageUtil.RESP_MESSAGE_TYPE_TEXT); textMessage.setFuncFlag(0); // 文本消息 if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_TEXT)) { respContent = "您发送的是文本消息!"; } // 图片消息 else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_IMAGE)) { respContent = "您发送的是图片消息!"; } // 地理位置消息 else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_LOCATION)) { respContent = "您发送的是地理位置消息!"; } // 链接消息 else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_LINK)) { respContent = "您发送的是链接消息!"; } // 音频消息 else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_VOICE)) { respContent = "您发送的是音频消息!"; } // 事件推送 else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_EVENT)) { // 事件类型 String eventType = requestMap.get("Event"); // 订阅 if (eventType.equals(MessageUtil.EVENT_TYPE_SUBSCRIBE)) { respContent = "谢谢您的关注!"; } // 取消订阅 else if (eventType.equals(MessageUtil.EVENT_TYPE_UNSUBSCRIBE)) { // TODO 取消订阅后用户再收不到公众号发送的消息,因此不需要回复消息 } // 自定义菜单点击事件 else if (eventType.equals(MessageUtil.EVENT_TYPE_CLICK)) { // 事件KEY值,与创建自定义菜单时指定的KEY值对应 String eventKey = requestMap.get("EventKey"); if (eventKey.equals("11")) { respContent = "天气预报菜单项被点击!"; } else if (eventKey.equals("12")) { respContent = "公交查询菜单项被点击!"; } else if (eventKey.equals("13")) { respContent = "周边搜索菜单项被点击!"; } else if (eventKey.equals("14")) { respContent = "历史上的今天菜单项被点击!"; } else if (eventKey.equals("21")) { respContent = "歌曲点播菜单项被点击!"; } else if (eventKey.equals("22")) { respContent = "经典游戏菜单项被点击!"; } else if (eventKey.equals("23")) { respContent = "美女电台菜单项被点击!"; } else if (eventKey.equals("24")) { respContent = "人脸识别菜单项被点击!"; } else if (eventKey.equals("25")) { respContent = "聊天唠嗑菜单项被点击!"; } else if (eventKey.equals("31")) { respContent = "Q友圈菜单项被点击!"; } else if (eventKey.equals("32")) { respContent = "电影排行榜菜单项被点击!"; } else if (eventKey.equals("33")) { respContent = "幽默笑话菜单项被点击!"; } } } textMessage.setContent(respContent); respMessage = MessageUtil.textMessageToXml(textMessage); } catch (Exception e) { e.printStackTrace(); } return respMessage; } } 

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。

微信公众帐号开发-自定义菜单的创建及菜单事件响应的实例相关推荐

  1. 微信公众帐号开发教程第6篇-消息及消息处理工具的封装

    本篇内容主要讲解如何将微信公众平台定义的消息及消息相关的操作封装成工具类,方面后期的使用  官方文档 接下来要做的就是将消息请求.回复中定义的消息进行封装,建立与之对应的Java类(Java是一门面向 ...

  2. 微信公众帐号开发教程第2篇-开发模式启用及接口配置

    编辑模式与开发模式 微信公众帐号申请成功后,要想接收处理用户的请求,就必须要在"高级功能"里进行配置,点击"高级功能",将看到如下界面: 从上图中可以看到,高级 ...

  3. [027] 微信公众帐号开发教程第3篇-开发模式启用及接口配置

    编辑模式与开发模式 微信公众帐号申请成功后,要想接收处理用户的请求,就必须要在"高级功能"里进行配置,点击"高级功能",将看到如下界面: 从上图中可以看到,高级 ...

  4. 微信公众帐号开发教程第1篇-引言(转)

    接触微信公众帐号已经有两个多月的时间了,在这期间,除了陆续完好个人公众帐号xiaoqrobot以外,还带领团队为公司开发了两个企业应用:一个是普通类型的公众帐号,还有一个是会议类型的公众帐号.经过这3 ...

  5. 如何在微信公众帐号开发模式下,通过程序代码向用户发送符号表情。

    本文重点介绍如何在微信公众帐号开发模式下,通过程序代码向用户发送符号表情.部分代码来自:FreeEIM 至于如何识别用户发送的是符号表情,就不在此讲解了,留给大家一点学习思考的空间.我只是给大家一个提 ...

  6. 微信公众帐号开发教程第11篇-符号表情的发送(上)

    题外话(可以略过) 相信这篇文章已经让大家等的太久了,不是我故弄玄虚.吊大家胃口,而是写一篇文章真的需要花太多的时间.也许一篇文章,你们花3-5分钟就看完了.就学会掌握了,而我却要花2-3个小时的时间 ...

  7. [11] 微信公众帐号开发教程第11篇-符号表情的发送(上)

    题外话(可以略过) 相信这篇文章已经让大家等的太久了,不是我故弄玄虚.吊大家胃口,而是写一篇文章真的需要花太多的时间.也许一篇文章,你们花3-5分钟就看完了.就学会掌握了,而我却要花2-3个小时的时间 ...

  8. [025] 微信公众帐号开发教程第1篇-引言

    接触微信公众帐号已经有两个多月的时间了,在这期间,除了陆续完善个人公众帐号xiaoqrobot以外,还带领团队为公司开发了两个企业应用:一个是普通类型的公众帐号,另一个是会议类型的公众帐号.经过这3个 ...

  9. [035] 微信公众帐号开发教程第11篇-符号表情的发送(上)

    题外话(可以略过) 相信这篇文章已经让大家等的太久了,不是我故弄玄虚.吊大家胃口,而是写一篇文章真的需要花太多的时间.也许一篇文章,你们花3-5分钟就看完了.就学会掌握了,而我却要花2-3个小时的时间 ...

最新文章

  1. datatable无法设置横向滚动条(设置无效)
  2. SOLID面向对象模式浅析
  3. 四线接近开关接线图_开关、电机、断路器、电热偶、电表接线图!非常齐全,快囤起来吧...
  4. matlab ode45求解齿轮动力学,ode45求解多自由度动力学方程实例.doc
  5. OpenCASCADE 开发框架(一)
  6. 电力系统分析第三版 第四章 电力系统潮流的计算机算法 习题答案,电力系统分析 第四章 电力系统潮流的计算机算法.ppt...
  7. 《每日一荐月刊》2019-11
  8. 儿童手表语音卡安全吗?
  9. 音视频技术开发周刊 | 164
  10. Redhat之NIS
  11. SpringBoot在controller返回一个HTML页面
  12. Domino V12 Beta新玩法
  13. 图像渲染测试软件,Arnold渲染器之aiImage(ai图像)着色器介绍及渲染测试
  14. Python 绘制圆锥体(3D图)
  15. OSChina 周日乱弹 ——拆散她们,帮她们过节!
  16. Transformers
  17. Ubuntu查找软件源
  18. qt制作一个画板_基于Qt的画图板的设计与实现(含录像)
  19. 数仓(十)从0到1简单搭建加载数仓DWS层
  20. 三国志战略版S3开荒阵容搭配推荐

热门文章

  1. Hangfire详解
  2. Window10主硬盘更换及系统重装
  3. 服务器怎么设置自动连接wifi,笔记本无线网络连接IP地址设置(自动获取IP)
  4. 惊了!这里竟然有你的生日和银行卡密码?
  5. 纯CSS实现超美选项卡
  6. 新的打卡地诞生!三星家电出没网红咖啡厅
  7. Linux文件管理 | Liunx 常用命令
  8. elasticsearch进阶(3)—— ilm policy
  9. C#字符串与ASII16(HEX)进制相互转换
  10. 易基因项目文章|WGBS+RNA-seq揭示PM2.5引起男性生殖障碍的DNA甲基化调控机制