首页 > 代码库 > 玩玩微信公众号Java版之四:自定义公众号菜单

玩玩微信公众号Java版之四:自定义公众号菜单

序:
微信公众号基本的菜单很难满足个性化及多功能的实现,那么微信能否实现自定菜单呢,具体的功能又如何去实现么?下面就来学习一下微信自定义公众号菜单吧!
自定义菜单接口可实现多种类型按钮,如下:
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。
请注意,3到8的所有事件,仅支持微信iPhone5.4.1以上版本,和Android5.4以上版本的微信用户,旧版本微信用户点击后将没有回应,开发者也不能正常接收到事件推送。9和10,是专门给第三方平台旗下未微信认证(具体而言,是资质认证未通过)的订阅号准备的事件类型,它们是没有事件推送的,能力相对受限,其他类型的公众号不必使用。
 
参考页面:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141013
 
开始准备:
 
菜单创建接口调用请求说明
http请求方式:POST(请使用https协议) https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN
参数说明
参数 是否必须 说明
button 一级菜单数组,个数应为1~3个
sub_button 二级菜单数组,个数应为1~5个
type 菜单的响应动作类型,view表示网页类型,click表示点击类型,miniprogram表示小程序类型
name 菜单标题,不超过16个字节,子菜单不超过60个字节
key click等点击类型必须 菜单KEY值,用于消息接口推送,不超过128字节
url view、miniprogram类型必须 网页链接,用户点击菜单可打开链接,不超过1024字节。type为miniprogram时,不支持小程序的老版本客户端将打开本url。
media_id media_id类型和view_limited类型必须 调用新增永久素材接口返回的合法media_id
appid miniprogram类型必须 小程序的appid(仅认证公众号可配置)
pagepath miniprogram类型必须 小程序的页面路径
返回结果
 正确时的返回JSON数据包如下:{"errcode":0,"errmsg":"ok"}
错误时的返回JSON数据包如下(示例为无效菜单名长度):{"errcode":40018,"errmsg":"invalid button name size"}
 
菜单查询接口:
开发者还可使用接口查询自定义菜单的结构。另外请注意,在设置了个性化菜单后,使用本自定义菜单查询接口可以获取默认菜单和全部个性化菜单信息。
请求说明
http请求方式:GET
https://api.weixin.qq.com/cgi-bin/menu/get?access_token=ACCESS_TOKEN
 
菜单删除接口
使用接口创建自定义菜单后,开发者还可使用接口删除当前使用的自定义菜单。另请注意,在个性化菜单时,调用此接口会删除默认菜单及全部个性化菜单。
请求说明
http请求方式:GET
https://api.weixin.qq.com/cgi-bin/menu/delete?access_token=ACCESS_TOKEN
返回说明
对应创建接口,正确的Json返回结果:
{"errcode":0,"errmsg":"ok"}
 
动手实现:
对于菜单中按钮属性,可定义个菜单按钮基类
技术分享
 
技术分享
 1 /**
 2  * 微信菜单基类
 3  * @author Damon
 4  */
 5 public class MenuButton
 6 {
 7     /**
 8      * 菜单的响应动作类型,view表示网页类型,click表示点击类型,miniprogram表示小程序类型
 9      */
10     private String type = "";
11 
12     /**
13      * 菜单标题,不超过16个字节,子菜单不超过60个字节
14      */
15     private String name = "";
16 
17     public String getType()
18     {
19         return type;
20     }
21 
22     public void setType(String type)
23     {
24         this.type = type;
25     }
26 
27     public String getName()
28     {
29         return name;
30     }
31 
32     public void setName(String name)
33     {
34         this.name = name;
35     }
36 
37 }
View Code

对于子菜单,可以继承基类,同时对本身新增属性对定义:技术分享

 
技术分享
 1 /**
 2  * 子菜单
 3  * @author Damon
 4  *
 5  */
 6 public class SubMenuButton extends MenuButton
 7 {
 8     /**
 9      * click等点击类型必须 菜单KEY值,用于消息接口推送,不超过128字节
10      */
11     private String key  ="";
12     
13     /**
14      * view、miniprogram类型必须 网页链接,用户点击菜单可打开链接,不超过1024字节。
15      * type为miniprogram时,不支持小程序的老版本客户端将打开本url。
16      */
17     private String url="";
18     
19     /**
20      * media_id类型和view_limited类型必须 调用新增永久素材接口返回的合法media_id
21      */
22     private String media_id = "";
23 
24     /**
25      * miniprogram类型必须 小程序的appid(仅认证公众号可配置)
26      */
27     private String appid = "";
28 
29     /**
30      * miniprogram类型必须 小程序的页面路径
31      */
32     private String pagepath = "";
33 
34     public String getKey()
35     {
36         return key;
37     }
38 
39     public void setKey(String key)
40     {
41         this.key = key;
42     }
43 
44     public String getUrl()
45     {
46         return url;
47     }
48 
49     public void setUrl(String url)
50     {
51         this.url = url;
52     }
53 
54     public String getMedia_id()
55     {
56         return media_id;
57     }
58 
59     public void setMedia_id(String media_id)
60     {
61         this.media_id = media_id;
62     }
63 
64     public String getAppid()
65     {
66         return appid;
67     }
68 
69     public void setAppid(String appid)
70     {
71         this.appid = appid;
72     }
73 
74     public String getPagepath()
75     {
76         return pagepath;
77     }
78 
79     public void setPagepath(String pagepath)
80     {
81         this.pagepath = pagepath;
82     }
83 
84 }
View Code

对导航窗口进行定义,即包含多个子菜单:

技术分享
 1 /**
 2  * 导航菜单
 3  * @author Damon
 4  */
 5 public class LevelMenu extends MenuButton
 6 {
 7 
 8     /**
 9      * 包含多个子菜单 定义名称与json中一致,不然解析名称对不上
10      */
11     private SubMenuButton[] sub_button;
12 
13     public SubMenuButton[] getSub_button()
14     {
15         return sub_button;
16     }
17 
18     public void setSub_button(SubMenuButton[] sub_button)
19     {
20         this.sub_button = sub_button;
21     }
22 
23 
24 }
View Code

技术分享

最终封装一个菜单类,方便数据封装和解析:
 
技术分享
 1 /**
 2  * 微信菜单类
 3  * @author Damon
 4  */
 5 public class Menu
 6 {
 7 
 8     /* 定义名称与json中一致,不然解析名称对不上 */
 9     private MenuButton[] button;
10 
11     public MenuButton[] getButton()
12     {
13         return button;
14     }
15 
16     public void setButton(MenuButton[] button)
17     {
18         this.button = button;
19     }
20 
21 }
View Code

基本对象都定义好了,下一步就是进行接口调用,实现创建自定义菜单:

实现主要分3步:
1、获取有效的 access_token
2、组件菜单;
3、调用接口,实现创建
 
技术分享
 1     /**
 2      * 创建菜单
 3      * @param menu
 4      * @return
 5      */
 6     public boolean createMenu()
 7     {
 8 
 9         // 1、获取access_token
10         // 使用测试 wx9015ccbcccf8d2f5 02e3a6877fa5fdeadd78d0f6f3048245
11         WeChatTokenService tWeChatTokenService = new WeChatTokenService();
12         String tAccess_Token = tWeChatTokenService.getToken("wx9015ccbcccf8d2f5", "02e3a6877fa5fdeadd78d0f6f3048245").getToken();
13 
14         // 2、组建菜单
15         String tMenuJSON = JSONObject.fromObject(getMenu()).toString();
16 
17         // 3、请求调用
18         String result = createMenubyHttps(tAccess_Token, tMenuJSON);
19 
20         System.out.println(result);
21 
22         return true;
23     }
View Code

 其中 access_token的获取可参考之前第三节的,这样每个token都有2个小时的有效时间,不用频繁获取。

组件菜单,详细如下:技术分享
技术分享
 
技术分享
 1     /**
 2      * 定义菜单属性
 3      * @return
 4      */
 5     private Menu getMenu()
 6     {
 7         Menu menu = new Menu();
 8 
 9         // 建3个导航菜单
10         LevelMenu tLevelMenuOne = new LevelMenu();
11         tLevelMenuOne.setName("Damon");
12         LevelMenu tLevelMenuTwo = new LevelMenu();
13         tLevelMenuTwo.setName("Panou");
14         LevelMenu tLevelMenuThree = new LevelMenu();
15         tLevelMenuThree.setName("Papaw");
16 
17         // 第一个导航菜单的子菜单
18         SubMenuButton tSubMenuButton_oneone = new SubMenuButton();
19         tSubMenuButton_oneone.setType(SysCon.WECHAT_MENU_TYPE_CLICK);
20         tSubMenuButton_oneone.setName("play basketball");
21         tSubMenuButton_oneone.setKey("11");
22 
23         SubMenuButton tSubMenuButton_onetwo = new SubMenuButton();
24         tSubMenuButton_onetwo.setType(SysCon.WECHAT_MENU_TYPE_CLICK);
25         tSubMenuButton_onetwo.setName("swimming");
26         tSubMenuButton_onetwo.setKey("12");
27 
28         // 加入导航菜单
29         tLevelMenuOne.setSub_button(new SubMenuButton[]
30         { tSubMenuButton_oneone, tSubMenuButton_onetwo });
31 
32         // 第二 个导航菜单的子菜单
33         SubMenuButton tSubMenuButton_twoone = new SubMenuButton();
34         tSubMenuButton_twoone.setType(SysCon.WECHAT_MENU_TYPE_CLICK);
35         tSubMenuButton_twoone.setName("watching TV");
36         tSubMenuButton_twoone.setKey("21");
37 
38         SubMenuButton tSubMenuButton_twotwo = new SubMenuButton();
39         tSubMenuButton_twotwo.setType(SysCon.WECHAT_MENU_TYPE_CLICK);
40         tSubMenuButton_twotwo.setName("play games");
41         tSubMenuButton_twotwo.setKey("22");
42 
43         SubMenuButton tSubMenuButton_twothree = new SubMenuButton();
44         tSubMenuButton_twothree.setType(SysCon.WECHAT_MENU_TYPE_CLICK);
45         tSubMenuButton_twothree.setName("shopping");
46         tSubMenuButton_twothree.setKey("23");
47 
48         // 加入导航菜单
49         tLevelMenuTwo.setSub_button(new SubMenuButton[]
50         { tSubMenuButton_twoone, tSubMenuButton_twotwo, tSubMenuButton_twothree });
51 
52         // 第三个导航菜单的子菜单
53         SubMenuButton tSubMenuButton_threeone = new SubMenuButton();
54         tSubMenuButton_threeone.setType(SysCon.WECHAT_MENU_TYPE_CLICK);
55         tSubMenuButton_threeone.setName("cring");
56         tSubMenuButton_threeone.setKey("31");
57 
58         SubMenuButton tSubMenuButton_threetwo = new SubMenuButton();
59         tSubMenuButton_threetwo.setType(SysCon.WECHAT_MENU_TYPE_CLICK);
60         tSubMenuButton_threetwo.setName("laughing");
61         tSubMenuButton_threetwo.setKey("32");
62 
63         // 加入导航菜单
64         tLevelMenuThree.setSub_button(new SubMenuButton[]
65         { tSubMenuButton_threeone, tSubMenuButton_threetwo });
66 
67         menu.setButton(new MenuButton[]
68         { tLevelMenuOne, tLevelMenuTwo, tLevelMenuThree });
69 
70         return menu;
71 
72     }
View Code

技术分享

通过对象的封装进行获取,然后转成JSON格式。
接着就是通过https进行接口调用(这里用到之前工具类中HTTPS的请求方法):
技术分享
 1     /**
 2      * 请求调用,设置菜单信息
 3      * @param url
 4      * @param requestData
 5      * @return
 6      */
 7     private String createMenubyHttps(String access_token, String requestData)
 8     {
 9         // 返回报文
10         String strResp = "";
11         String path = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token=" + access_token;
12         try
13         {
14             strResp = WeChatUtil.doHttpsPost(path, requestData);
15         }
16         catch (HttpException e)
17         {
18             // 发生致命的异常,可能是协议不对或者返回的内容有问题
19             System.out.println("Please check your provided http address!" + e);
20             e.printStackTrace();
21         }
22         catch (IOException e)
23         {
24             // 发生网络异常
25         }
26         catch (Exception e)
27         {
28             System.out.println(e);
29         }
30         finally
31         {}
32         return strResp;
33 
34     }
View Code

技术分享

 自定义菜单的基本调用就实现了,可以看到效果图: 技术分享
 技术分享
 
这里写的都是基本的点击事件,但是没有实现,具体的自己去发挥了~

玩玩微信公众号Java版之四:自定义公众号菜单