1. 程式人生 > >Java微信公眾平臺開發之自定義選單

Java微信公眾平臺開發之自定義選單

一、自定義選單的說明和按鈕型別

1、選單說明

1)自定義選單最多包括3個一級選單,每個一級選單最多包含5個二級選單。

2)一級選單最多4個漢字,二級選單最多7個漢字,多出來的部分將會以“...”代替。

3)建立自定義選單後,選單的重新整理策略是,在使用者進入公眾號會話頁或公眾號profile頁時,如果發現上一次拉取選單的請求在5分鐘以前,就會拉取一下選單,如果選單有更新,就會重新整理客戶端的選單。測試時可以嘗試取消關注公眾賬號後再次關注,則可以看到建立後的效果。

2、自定義選單介面可實現多種型別按鈕

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,是專門給第三方平臺旗下未微信認證(具體而言,是資質認證未通過)的訂閱號準備的事件型別,它們是沒有事件推送的,能力相對受限,其他型別的公眾號不必使用。

二、選單的建立/查詢/刪除

官方的click和view事件demo

  1. {

  2. "button":[

  3. {

  4. "type":"click",

  5. "name":"今日歌曲",

  6. "key":"V1001_TODAY_MUSIC"

  7. },

  8. {

  9. "name":"選單",

  10. "sub_button":[

  11. {

  12. "type":"view",

  13. "name":"搜尋",

  14. "url":"http://www.soso.com/"

  15. },

  16. {

  17. "type":"miniprogram",

  18. "name":"wxa",

  19. "url":"http://mp.weixin.qq.com",

  20. "appid":"wx286b93c14bbf93aa",

  21. "pagepath":"pages/lunar/index"

  22. },

  23. {

  24. "type":"click",

  25. "name":"贊一下我們",

  26. "key":"V1001_GOOD"

  27. }]

  28. }]

  29. }

其他型別(包括9、10)

  1. {

  2. "button": [

  3. {

  4. "name": "掃碼",

  5. "sub_button": [

  6. {

  7. "type": "scancode_waitmsg",

  8. "name": "掃碼帶提示",

  9. "key": "rselfmenu_0_0",

  10. "sub_button": [ ]

  11. },

  12. {

  13. "type": "scancode_push",

  14. "name": "掃碼推事件",

  15. "key": "rselfmenu_0_1",

  16. "sub_button": [ ]

  17. }

  18. ]

  19. },

  20. {

  21. "name": "發圖",

  22. "sub_button": [

  23. {

  24. "type": "pic_sysphoto",

  25. "name": "系統拍照發圖",

  26. "key": "rselfmenu_1_0",

  27. "sub_button": [ ]

  28. },

  29. {

  30. "type": "pic_photo_or_album",

  31. "name": "拍照或者相簿發圖",

  32. "key": "rselfmenu_1_1",

  33. "sub_button": [ ]

  34. },

  35. {

  36. "type": "pic_weixin",

  37. "name": "微信相簿發圖",

  38. "key": "rselfmenu_1_2",

  39. "sub_button": [ ]

  40. }

  41. ]

  42. },

  43. {

  44. "name": "傳送位置",

  45. "type": "location_select",

  46. "key": "rselfmenu_2_0"

  47. },

  48. {

  49. "type": "media_id",

  50. "name": "圖片",

  51. "media_id": "MEDIA_ID1"

  52. },

  53. {

  54. "type": "view_limited",

  55. "name": "圖文訊息",

  56. "media_id": "MEDIA_ID2"

  57. }

  58. ]

  59. }

1、根據例項開始封裝實體類

選單按鈕基類BasicButton.java

  1. public class BasicButton {

  2. private String name;

  3. public String getName() {

  4. return name;

  5. }

  6. public void setName(String name) {

  7. this.name = name;

  8. }

  9. }

選單Menu.java

  1. public class Menu {

  2. public final static String CLICK = "click"; // click選單

  3. public final static String VIEW = "view"; // url選單

  4. public final static String SCANCODE_WAITMSG = "scancode_waitmsg"; // 掃碼帶提示

  5. public final static String SCANCODE_PUSH = "scancode_push"; // 掃碼推事件

  6. public final static String PIC_SYSPHOTO = "pic_sysphoto"; // 系統拍照發圖

  7. public final static String PIC_PHOTO_OR_ALBUM = "pic_photo_or_album"; // 拍照或者相簿發圖

  8. public final static String PIC_WEIXIN = "pic_weixin"; // 微信相簿發圖

  9. public final static String LOCATION_SELECT = "location_select"; // 傳送位置

  10. private BasicButton[] button;

  11. public BasicButton[] getButton() {

  12. return button;

  13. }

  14. public void setButton(BasicButton[] button) {

  15. this.button = button;

  16. }

  17. }

view型別按鈕類ViewButton.java,其他的型別可以照此一一封裝

  1. public class ViewButton extends BasicButton {

  2. private String type = Menu.VIEW;

  3. private String url;

  4. public String getType() {

  5. return type;

  6. }

  7. public void setType(String type) {

  8. this.type = type;

  9. }

  10. public String getUrl() {

  11. return url;

  12. }

  13. public void setUrl(String url) {

  14. this.url = url;

  15. }

  16. }

一級選單包含二級選單的封裝ComplexMenu.java

  1. public class ComplexMenu extends BasicButton {

  2. private BasicButton[] sub_button;

  3. public BasicButton[] getSub_button() {

  4. return sub_button;

  5. }

  6. public void setSub_button(BasicButton[] sub_button) {

  7. this.sub_button = sub_button;

  8. }

  9. }

2.封裝完畢,組裝選單

  1. private static Menu getMenu() {

  2. ViewButton btn11 = new ViewButton();

  3. btn11.setName("測試11");

  4. btn11.setUrl("http://www.qq.com");

  5. ClickButton btn21 = new ClickButton();

  6. btn21.setName("測試21");

  7. btn21.setKey("21");

  8. ClickButton btn22 = new ClickButton();

  9. btn22.setName("測試22");

  10. btn22.setKey("22");

  11. //一級選單(沒有二級選單)

  12. ComplexMenu mainBtn1 = new ComplexMenu();

  13. mainBtn1.setName("測試1");

  14. mainBtn1.setSub_button(new BasicButton[] { btn11});

  15. //一級選單(有二級選單)

  16. ComplexMenu mainBtn2 = new ComplexMenu();

  17. mainBtn2.setName("測試2");

  18. mainBtn2.setSub_button(new BasicButton[] { btn21, btn22 });

  19. Menu menu = new Menu();

  20. menu.setButton(new BasicButton[] { mainBtn1, mainBtn2 });

  21. return menu;

  22. }

3.自定義選單的建立

  1. /**

  2. * 建立的選單

  3. *

  4. * @param menu 選單項

  5. * @param token 授權token

  6. * @return {"errcode":0,"errmsg":"ok"}

  7. */

  8. public ResultState createMenu(Menu menu, String token) {

  9. TreeMap<String, String> map = new TreeMap<String, String>();

  10. map.put("access_token", token);

  11. String jsonData = JsonUtil.toJson(menu).toString();

  12. String result = HttpReqUtil.HttpsDefaultExecute(HttpReqUtil.POST_METHOD, WechatConfig.MENU_CREATE_URL, map, jsonData);

  13. return JsonUtil.fromJson(result, ResultState.class);

  14. }

4、自定義選單的查詢

返回的例項

  1. 對應建立介面,正確的Json返回結果:

  2. {

  3. "menu": {

  4. "button": [

  5. {

  6. "type": "click",

  7. "name": "今日歌曲",

  8. "key": "V1001_TODAY_MUSIC",

  9. "sub_button": [ ]

  10. },

  11. {

  12. "type": "click",

  13. "name": "歌手簡介",

  14. "key": "V1001_TODAY_SINGER",

  15. "sub_button": [ ]

  16. },

  17. {

  18. "name": "選單",

  19. "sub_button": [

  20. {

  21. "type": "view",

  22. "name": "搜尋",

  23. "url": "http://www.soso.com/",

  24. "sub_button": [ ]

  25. },

  26. {

  27. "type": "view",

  28. "name": "視訊",

  29. "url": "http://v.qq.com/",

  30. "sub_button": [ ]

  31. },

  32. {

  33. "type": "click",

  34. "name": "贊一下我們",

  35. "key": "V1001_GOOD",

  36. "sub_button": [ ]

  37. }

  38. ]

  39. }

  40. ]

  41. }

  42. }

  1. /**

  2. * 獲取自定義選單

  3. *

  4. * @param token

  5. * @return

  6. */

  7. public String getMenu(String token) {

  8. TreeMap<String, String> map = new TreeMap<String, String>();

  9. map.put("access_token", token);

  10. String result = HttpReqUtil.HttpsDefaultExecute(HttpReqUtil.GET_METHOD, WechatConfig.MENU_GET_URL, map, "");

  11. return result;

  12. }

選單所有屬性MenuAttr.java

  1. /**

  2. * 選單所有屬性

  3. * @author phil

  4. *

  5. */

  6. public class MenuAttr extends BasicMenu {

  7. private String type;

  8. private String url;

  9. private String key;

  10. private String sub_button;

  11. get/set方法

  12. }

返回的選單類MenuReturn.java

  1. /**

  2. * 返回的選單類

  3. * @author phil

  4. *

  5. */

  6. public class MenuReturn extends BasicMenu{

  7. private MenuAttr[] sub_button;

  8. public MenuAttr[] getSub_button() {

  9. return sub_button;

  10. }

  11. public void setSub_button(MenuAttr[] subButton) {

  12. sub_button = subButton;

  13. }

  14. }

將json格式的字串轉換為Menu物件

  1. /**

  2. * 將json格式的字串轉換為Menu物件

  3. * @param json

  4. * @return

  5. */

  6. public List<MenuReturn> converMenu(String json) {

  7. List<MenuReturn> list = new ArrayList<MenuReturn>();

  8. if (json!= null && !"".equals(json)) {

  9. JSONObject object = JSONObject.parseObject(json);

  10. JSONArray array = object.getJSONObject("menu").getJSONArray("button");

  11. for (int i = 0; i < array.size(); i++) {

  12. MenuReturn mr= new MenuReturn();

  13. mr= array.getObject(i, MenuReturn.class);

  14. list.add(mr);

  15. }

  16. }

  17. return list;

  18. }

注:此處用的fastjson

此處方法待改進,有更好的請指教一二

5、自定義選單的刪除

  1. /**

  2. * 刪除自定義選單

  3. *

  4. * @param token

  5. * @return

  6. */

  7. public boolean deleteMenu(String token) {

  8. boolean falg = true;

  9. TreeMap<String, String> map = new TreeMap<String, String>();

  10. map.put("access_token", token);

  11. String result = HttpReqUtil.HttpsDefaultExecute(HttpReqUtil.GET_METHOD, WechatConfig.MENU_DELTE_URL, map, "");

  12. ResultState state = JsonUtil.fromJson(result, ResultState.class);

  13. if (state.getErrcode()!= 0|| state.getErrmsg() != "ok") {

  14. falg = false;

  15. }

  16. return falg;

  17. }

三、自定義選單事件推送

使用者點選自定義選單後,微信會把點選事件推送給開發者,請注意,點選選單彈出子選單,不會產生上報。請注意,第3個到第8個的所有事件,僅支援微信iPhone5.4.1以上版本,和Android5.4以上版本的微信使用者,舊版本微信使用者點選後將沒有迴應,開發者也不能正常接收到事件推送。

1、解析微信推送的xml資料包

  1. /**

  2. * 解析微信發來的請求(XML)

  3. * xml示例

  4. * <xml>

  5. <ToUserName><![CDATA[toUser]]></ToUserName>

  6. <FromUserName><![CDATA[FromUser]]></FromUserName>

  7. <CreateTime>123456789</CreateTime>

  8. <MsgType><![CDATA[event]]></MsgType>

  9. <Event><![CDATA[CLICK]]></Event>

  10. <EventKey><![CDATA[EVENTKEY]]></EventKey>

  11. </xml>

  12. * @param request

  13. * @return

  14. * @throws Exception

  15. */

  16. public static Map<String, String> parseXml(HttpServletRequest request) throws Exception {

  17. // 將解析結果儲存在HashMap中

  18. Map<String, String> map = new HashMap<String, String>();

  19. // 從request中取得輸入流

  20. InputStream inputStream = request.getInputStream();

  21. // 讀取輸入流

  22. SAXReader reader = new SAXReader();

  23. Document document = reader.read(inputStream);

  24. // 得到xml根元素

  25. Element root = document.getRootElement();

  26. // 得到根元素的所有子節點

  27. List<Element> elementList = root.elements();

  28. // 遍歷所有子節點

  29. for (Element e : elementList)

  30. map.put(e.getName(), e.getText());

  31. // 釋放資源

  32. inputStream.close();

  33. inputStream = null;

  34. return map;

  35. }

2、利用map的get(key)獲取value

MsgType 訊息型別,event Event          事件型別,CLICK EventKey 事件KEY值,與自定義選單介面中KEY值對應

注:key是引數名

附:WechatConfig.java

  1. // 建立選單

  2. public static final String MENU_CREATE_URL = "https://api.weixin.qq.com/cgi-bin/menu/create";

  3. // 查詢自定義選單

  4. public static final String MENU_GET_URL = "https://api.weixin.qq.com/cgi-bin/menu/get";

  5. // 刪除自定義選單

  6. public static final String MENU_DELTE_URL = "https://api.weixin.qq.com/cgi-bin/menu/delete";