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
-
{
-
"button":[
-
{
-
"type":"click",
-
"name":"今日歌曲",
-
"key":"V1001_TODAY_MUSIC"
-
},
-
{
-
"name":"選單",
-
"sub_button":[
-
{
-
"type":"view",
-
"name":"搜尋",
-
"url":"http://www.soso.com/"
-
},
-
{
-
"type":"miniprogram",
-
"name":"wxa",
-
"url":"http://mp.weixin.qq.com",
-
"appid":"wx286b93c14bbf93aa",
-
"pagepath":"pages/lunar/index"
-
},
-
{
-
"type":"click",
-
"name":"贊一下我們",
-
"key":"V1001_GOOD"
-
}]
-
}]
-
}
其他型別(包括9、10)
-
{
-
"button": [
-
{
-
"name": "掃碼",
-
"sub_button": [
-
{
-
"type": "scancode_waitmsg",
-
"name": "掃碼帶提示",
-
"key": "rselfmenu_0_0",
-
"sub_button": [ ]
-
},
-
{
-
"type": "scancode_push",
-
"name": "掃碼推事件",
-
"key": "rselfmenu_0_1",
-
"sub_button": [ ]
-
}
-
]
-
},
-
{
-
"name": "發圖",
-
"sub_button": [
-
{
-
"type": "pic_sysphoto",
-
"name": "系統拍照發圖",
-
"key": "rselfmenu_1_0",
-
"sub_button": [ ]
-
},
-
{
-
"type": "pic_photo_or_album",
-
"name": "拍照或者相簿發圖",
-
"key": "rselfmenu_1_1",
-
"sub_button": [ ]
-
},
-
{
-
"type": "pic_weixin",
-
"name": "微信相簿發圖",
-
"key": "rselfmenu_1_2",
-
"sub_button": [ ]
-
}
-
]
-
},
-
{
-
"name": "傳送位置",
-
"type": "location_select",
-
"key": "rselfmenu_2_0"
-
},
-
{
-
"type": "media_id",
-
"name": "圖片",
-
"media_id": "MEDIA_ID1"
-
},
-
{
-
"type": "view_limited",
-
"name": "圖文訊息",
-
"media_id": "MEDIA_ID2"
-
}
-
]
-
}
1、根據例項開始封裝實體類
選單按鈕基類BasicButton.java
-
public class BasicButton {
-
private String name;
-
public String getName() {
-
return name;
-
}
-
public void setName(String name) {
-
this.name = name;
-
}
-
}
選單Menu.java
-
public class Menu {
-
public final static String CLICK = "click"; // click選單
-
public final static String VIEW = "view"; // url選單
-
public final static String SCANCODE_WAITMSG = "scancode_waitmsg"; // 掃碼帶提示
-
public final static String SCANCODE_PUSH = "scancode_push"; // 掃碼推事件
-
public final static String PIC_SYSPHOTO = "pic_sysphoto"; // 系統拍照發圖
-
public final static String PIC_PHOTO_OR_ALBUM = "pic_photo_or_album"; // 拍照或者相簿發圖
-
public final static String PIC_WEIXIN = "pic_weixin"; // 微信相簿發圖
-
public final static String LOCATION_SELECT = "location_select"; // 傳送位置
-
private BasicButton[] button;
-
public BasicButton[] getButton() {
-
return button;
-
}
-
public void setButton(BasicButton[] button) {
-
this.button = button;
-
}
-
}
view型別按鈕類ViewButton.java,其他的型別可以照此一一封裝
-
public class ViewButton extends BasicButton {
-
private String type = Menu.VIEW;
-
private String url;
-
public String getType() {
-
return type;
-
}
-
public void setType(String type) {
-
this.type = type;
-
}
-
public String getUrl() {
-
return url;
-
}
-
public void setUrl(String url) {
-
this.url = url;
-
}
-
}
一級選單包含二級選單的封裝ComplexMenu.java
-
public class ComplexMenu extends BasicButton {
-
private BasicButton[] sub_button;
-
public BasicButton[] getSub_button() {
-
return sub_button;
-
}
-
public void setSub_button(BasicButton[] sub_button) {
-
this.sub_button = sub_button;
-
}
-
}
2.封裝完畢,組裝選單
-
private static Menu getMenu() {
-
ViewButton btn11 = new ViewButton();
-
btn11.setName("測試11");
-
btn11.setUrl("http://www.qq.com");
-
ClickButton btn21 = new ClickButton();
-
btn21.setName("測試21");
-
btn21.setKey("21");
-
ClickButton btn22 = new ClickButton();
-
btn22.setName("測試22");
-
btn22.setKey("22");
-
//一級選單(沒有二級選單)
-
ComplexMenu mainBtn1 = new ComplexMenu();
-
mainBtn1.setName("測試1");
-
mainBtn1.setSub_button(new BasicButton[] { btn11});
-
//一級選單(有二級選單)
-
ComplexMenu mainBtn2 = new ComplexMenu();
-
mainBtn2.setName("測試2");
-
mainBtn2.setSub_button(new BasicButton[] { btn21, btn22 });
-
Menu menu = new Menu();
-
menu.setButton(new BasicButton[] { mainBtn1, mainBtn2 });
-
return menu;
-
}
3.自定義選單的建立
-
/**
-
* 建立的選單
-
*
-
* @param menu 選單項
-
* @param token 授權token
-
* @return {"errcode":0,"errmsg":"ok"}
-
*/
-
public ResultState createMenu(Menu menu, String token) {
-
TreeMap<String, String> map = new TreeMap<String, String>();
-
map.put("access_token", token);
-
String jsonData = JsonUtil.toJson(menu).toString();
-
String result = HttpReqUtil.HttpsDefaultExecute(HttpReqUtil.POST_METHOD, WechatConfig.MENU_CREATE_URL, map, jsonData);
-
return JsonUtil.fromJson(result, ResultState.class);
-
}
4、自定義選單的查詢
返回的例項
-
對應建立介面,正確的Json返回結果:
-
{
-
"menu": {
-
"button": [
-
{
-
"type": "click",
-
"name": "今日歌曲",
-
"key": "V1001_TODAY_MUSIC",
-
"sub_button": [ ]
-
},
-
{
-
"type": "click",
-
"name": "歌手簡介",
-
"key": "V1001_TODAY_SINGER",
-
"sub_button": [ ]
-
},
-
{
-
"name": "選單",
-
"sub_button": [
-
{
-
"type": "view",
-
"name": "搜尋",
-
"url": "http://www.soso.com/",
-
"sub_button": [ ]
-
},
-
{
-
"type": "view",
-
"name": "視訊",
-
"url": "http://v.qq.com/",
-
"sub_button": [ ]
-
},
-
{
-
"type": "click",
-
"name": "贊一下我們",
-
"key": "V1001_GOOD",
-
"sub_button": [ ]
-
}
-
]
-
}
-
]
-
}
-
}
-
/**
-
* 獲取自定義選單
-
*
-
* @param token
-
* @return
-
*/
-
public String getMenu(String token) {
-
TreeMap<String, String> map = new TreeMap<String, String>();
-
map.put("access_token", token);
-
String result = HttpReqUtil.HttpsDefaultExecute(HttpReqUtil.GET_METHOD, WechatConfig.MENU_GET_URL, map, "");
-
return result;
-
}
選單所有屬性MenuAttr.java
-
/**
-
* 選單所有屬性
-
* @author phil
-
*
-
*/
-
public class MenuAttr extends BasicMenu {
-
private String type;
-
private String url;
-
private String key;
-
private String sub_button;
-
get/set方法
-
}
返回的選單類MenuReturn.java
-
/**
-
* 返回的選單類
-
* @author phil
-
*
-
*/
-
public class MenuReturn extends BasicMenu{
-
private MenuAttr[] sub_button;
-
public MenuAttr[] getSub_button() {
-
return sub_button;
-
}
-
public void setSub_button(MenuAttr[] subButton) {
-
sub_button = subButton;
-
}
-
}
將json格式的字串轉換為Menu物件
-
/**
-
* 將json格式的字串轉換為Menu物件
-
* @param json
-
* @return
-
*/
-
public List<MenuReturn> converMenu(String json) {
-
List<MenuReturn> list = new ArrayList<MenuReturn>();
-
if (json!= null && !"".equals(json)) {
-
JSONObject object = JSONObject.parseObject(json);
-
JSONArray array = object.getJSONObject("menu").getJSONArray("button");
-
for (int i = 0; i < array.size(); i++) {
-
MenuReturn mr= new MenuReturn();
-
mr= array.getObject(i, MenuReturn.class);
-
list.add(mr);
-
}
-
}
-
return list;
-
}
注:此處用的fastjson
此處方法待改進,有更好的請指教一二
5、自定義選單的刪除
-
/**
-
* 刪除自定義選單
-
*
-
* @param token
-
* @return
-
*/
-
public boolean deleteMenu(String token) {
-
boolean falg = true;
-
TreeMap<String, String> map = new TreeMap<String, String>();
-
map.put("access_token", token);
-
String result = HttpReqUtil.HttpsDefaultExecute(HttpReqUtil.GET_METHOD, WechatConfig.MENU_DELTE_URL, map, "");
-
ResultState state = JsonUtil.fromJson(result, ResultState.class);
-
if (state.getErrcode()!= 0|| state.getErrmsg() != "ok") {
-
falg = false;
-
}
-
return falg;
-
}
三、自定義選單事件推送
使用者點選自定義選單後,微信會把點選事件推送給開發者,請注意,點選選單彈出子選單,不會產生上報。請注意,第3個到第8個的所有事件,僅支援微信iPhone5.4.1以上版本,和Android5.4以上版本的微信使用者,舊版本微信使用者點選後將沒有迴應,開發者也不能正常接收到事件推送。
1、解析微信推送的xml資料包
-
/**
-
* 解析微信發來的請求(XML)
-
* xml示例
-
* <xml>
-
<ToUserName><![CDATA[toUser]]></ToUserName>
-
<FromUserName><![CDATA[FromUser]]></FromUserName>
-
<CreateTime>123456789</CreateTime>
-
<MsgType><![CDATA[event]]></MsgType>
-
<Event><![CDATA[CLICK]]></Event>
-
<EventKey><![CDATA[EVENTKEY]]></EventKey>
-
</xml>
-
* @param request
-
* @return
-
* @throws Exception
-
*/
-
public static Map<String, String> parseXml(HttpServletRequest request) throws Exception {
-
// 將解析結果儲存在HashMap中
-
Map<String, String> map = new HashMap<String, String>();
-
// 從request中取得輸入流
-
InputStream inputStream = request.getInputStream();
-
// 讀取輸入流
-
SAXReader reader = new SAXReader();
-
Document document = reader.read(inputStream);
-
// 得到xml根元素
-
Element root = document.getRootElement();
-
// 得到根元素的所有子節點
-
List<Element> elementList = root.elements();
-
// 遍歷所有子節點
-
for (Element e : elementList)
-
map.put(e.getName(), e.getText());
-
// 釋放資源
-
inputStream.close();
-
inputStream = null;
-
return map;
-
}
2、利用map的get(key)獲取value
MsgType 訊息型別,event Event 事件型別,CLICK EventKey 事件KEY值,與自定義選單介面中KEY值對應
注:key是引數名
附:WechatConfig.java
-
// 建立選單
-
public static final String MENU_CREATE_URL = "https://api.weixin.qq.com/cgi-bin/menu/create";
-
// 查詢自定義選單
-
public static final String MENU_GET_URL = "https://api.weixin.qq.com/cgi-bin/menu/get";
-
// 刪除自定義選單
-
public static final String MENU_DELTE_URL = "https://api.weixin.qq.com/cgi-bin/menu/delete";