2017-04-13 08:40:07 xiaojin0816 阅读数 1472
  • 微信公众号开发7-用户管理-微信开发php

    微信公众平台开发之微信用户开发管理是子恒老师《微信公众平台开发》视频教程的第7部。详细讲解了用php开发微信,对微信公众平台中的粉丝用户管理开发。内容包含微信公众平台用户分组,获取微信用户列表,查询用户详情等等。欢迎反馈,微信/QQ:68183131

    14052 人正在学习 去看看 秦子恒


一、什么是AccessToken
AccessToken是微信企业号和企业微信的全局唯一票据,所有接口在通信时都需要携带此信息用于验证接口的访问权限。
AccessToken需要用CorpID和Secret来换取,不同的Secret会返回不同的AccessToken。正常情况下AccessToken有效期为7200秒,有效期内重复获取返回相同结果。access_token至少保留512字节的存储空间。


二、调用接口、参数和返回结果
1、调用接口:微信企业号和企业微信相同。
Https请求方式: GET
https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=id&corpsecret=secrect


2、调用参数:
(1)corpid:微信企业号和企业微信都有唯一的ID号。
(2)secret:微信企业号指的是管理组的凭证密钥,每个管理组都有一个secret;企业微信指的是应用(通讯录也是一个应用)的凭证密钥,每个应用都有一个secret。这点两者是不同的。


3、返回结果:在两者是有差异的,具体如下:
(1)微信企业号:
成功时返回
{
  "access_token": "accesstoken000001",
  "expires_in": 7200
}
错误时返回
{
  "errcode": 43003,
  "errmsg": "require https"
}
(2)企业微信
成功时返回
{
 "errcode":0,
 "errmsg":"",
 "access_token": "accesstoken000001",
  "expires_in": 7200
}
失败时返回
{
"errcode":40091,
 "errmsg":"provider_secret is invalid"
}


三、C#示例,获取AccessToken
为了统一微信企业号和企业微信的调用方法,我们首先要定义一个类,如下:


 

这个类已经涵盖了微信企业号/企业微信获取AccessToken成功和失败的全部情况,是否成功我们只需要读取access_token的内容,只要不为空则可以认定成功。当然也可以判断errcode的值。但微信企业号成功时没有errcode 这个字段,这时需要特殊处理一下。

下面这个函数调用这个API,将corpid和corpsecret作为参数,返回结果为AccessTokenJsonResult类


 

下面的代码调用这个函数,自己根据返回结果读取AccessToken的值,如果调用失败就需要自行处理异常。


 

2016-04-04 16:36:34 wgyscsf 阅读数 4950
  • 微信公众号开发7-用户管理-微信开发php

    微信公众平台开发之微信用户开发管理是子恒老师《微信公众平台开发》视频教程的第7部。详细讲解了用php开发微信,对微信公众平台中的粉丝用户管理开发。内容包含微信公众平台用户分组,获取微信用户列表,查询用户详情等等。欢迎反馈,微信/QQ:68183131

    14052 人正在学习 去看看 秦子恒

java微信开发API解析(二)-获取消息和回复消息

说明

* 本示例根据微信开发文档:http://mp.weixin.qq.com/wiki/home/index.html最新版(4/3/2016 5:34:36 PM )进行开发演示。
* 编辑平台:myeclipse10.7+win32+jdk1.7+tomcat7.0  
* 服务器:阿里云 windows server 2008 64bits
* 平台要求:servlet使用注解方式,平台要求:j2ee6.0+、jdk6.0+、tomcat7.0+
* 演示更加注重于api解析。
* 为了便于测试说明,每个测试用例为独立,不依赖于其它方法。对于封装,不多加考虑。
* 演示尽可能按照API要求进行,目的:了解文档使用方式,达到举一反三的效果。
* 知识要求:牢固的java基础、了解http网络通信知识、对于javaweb有足够了解、json解析
* 在每篇文章结束会给出该部分演示源码。在分析完API之后,会以源码包的形式给出所有演示源码。
* 当前时间:4/3/2016 5:32:57 PM ,以该时间为准。

文档原文-消息管理(摘要)

  • 文档地址:http://mp.weixin.qq.com/wiki/17/f298879f8fb29ab98b2f2971d42552fd.html
  • 消息管理
    • 接收消息-接收普通消息
    • 接收消息-接收事件推送
    • 发送消息-被动回复消息
    • 发送消息-被动回复时的加解密
    • 发送消息-客服消息
    • 发送消息-群发接口
    • 发送消息-模板消息接口
    • 发送消息-模板消息运营规范
    • 获取公众号自动回复配置

文档理解

  • 接收消息

    • 文档这样解释:​当普通微信用户向公众账号发消息时,微信服务器将POST消息的XML数据包到开发者填写的URL上。
    • 理解:微信服务器将用户发送的消息通过Post流的形式返回给req。当我们想要获取用户发送的消息时,可以通过req.getInputStream()获取。当然,我们可以根据文档上关于消息的返回的xml格式,进行必要的解析。
    • 实现:

      /*
       * 该部分我们获取用户发送的信息,并且解析成<K,V>的形式进行显示
       */
      // 解析用户发送过来的信息
      InputStream is = req.getInputStream();// 拿取请求流
      // 将解析结果存储在HashMap中
      Map<String, String> map = new HashMap<String, String>();
      // 解析xml,将获取到的返回结果xml进行解析成我们习惯的文字信息
      SAXReader reader = new SAXReader();// 第三方jar:dom4j【百度:saxreader解析xml】
      Document document = null;
      try {
          document = reader.read(is);
      } catch (DocumentException e1) {
          // TODO Auto-generated catch block
          e1.printStackTrace();
      }
      // 得到xml根元素
      Element root = document.getRootElement();
      // 得到根元素的所有子节点
      List<Element> elementList = root.elements();
      
      // 遍历所有子节点
      for (Element e : elementList)
          map.put(e.getName(), e.getText());
      
      // 测试输出
      Set<String> keySet = map.keySet();
      // 测试输出解析后用户发过来的信息
      System.out.println(TAG + ":解析用户发送过来的信息开始");
      for (String key : keySet) {
          System.out.println(key + ":" + map.get(key));
      }
      System.out.println(TAG + ":解析用户发送过来的信息结束");     
      
  • 发送消息

    • 文档这样解释:​当用户发送消息给公众号时(或某些特定的用户操作引发的事件推送时),会产生一个POST请求,开发者可以在响应包(Get)中返回特定XML结构,来对该消息进行响应(现支持回复文本、图片、图文、语音、视频、音乐)。严格来说,发送被动响应消息其实并不是一种接口,而是对微信服务器发过来消息的一次回复。
    • 理解:用户发送请求,会产生一个POST请求,我们可以通过Respone进行回复消息。但是,回复的内容有严格的格式要求,只有满足格式要求,微信服务器才会进行处理返回给用户。通过查看文档“消息管理”模块,我们可以看到微信中有各种各样的消息,每类消息都有自己特定的格式要求,我们必须按照要求才可以正常的给用户返回特定的信息。我们尝试按照文档的要求格式给用户回复文本信息、图文消息。重点:按照文档要求构造需要的参数。特别注意:参数区分大小写。
    • 实现1-回复普通文本消息:

      //实例1:发送普通文本消息,请查看文档关于“回复文本消息”的xml格式
      
      // 第一步:按照回复文本信息构造需要的参数
      TextMsg textMsg = new TextMsg();
      textMsg.setToUserName(map.get("FromUserName"));// 发送和接收信息“User”刚好相反
      textMsg.setFromUserName(map.get("ToUserName"));
      textMsg.setCreateTime(new Date().getTime());// 消息创建时间 (整型)
      textMsg.setMsgType("text");// 文本类型消息
      textMsg.setContent("我是服务器回复给用户的信息");
      
      // // 第二步,将构造的信息转化为微信识别的xml格式【百度:xstream bean转xml】
      XStream xStream = new XStream();
      xStream.alias("xml", textMsg.getClass());
      String textMsg2Xml = xStream.toXML(textMsg);
      System.out.println(textMsg2Xml);
      
      // // 第三步,发送xml的格式信息给微信服务器,服务器转发给用户
      PrintWriter printWriter = resp.getWriter();
      printWriter.print(textMsg2Xml);
      
    • 实现2-回复图文消息:

      //实例2,发送图文消息。请查看文档关于“回复图文消息”的xml格式
      
      // 第一步:按照回复图文信息构造需要的参数
      List<Article> articles = new ArrayList<Article>();
      Article a = new Article();
      a.setTitle("我是图片标题");
      a.setUrl("www.baidu.com");// 该地址是点击图片跳转后
      a.setPicUrl("http://b.hiphotos.baidu.com/image/pic/item/08f790529822720ea5d058ba7ccb0a46f21fab50.jpg");// 该地址是一个有效的图片地址
      a.setDescription("我是图片的描述");
      articles.add(a);
      PicAndTextMsg picAndTextMsg = new PicAndTextMsg();
      picAndTextMsg.setToUserName(map.get("FromUserName"));// 发送和接收信息“User”刚好相反
      picAndTextMsg.setFromUserName(map.get("ToUserName"));
      picAndTextMsg.setCreateTime(new Date().getTime());// 消息创建时间 (整型)
      picAndTextMsg.setMsgType("news");// 图文类型消息
      picAndTextMsg.setArticleCount(1);
      picAndTextMsg.setArticles(articles);
      // 第二步,将构造的信息转化为微信识别的xml格式【百度:xstream bean转xml】
      XStream xStream = new XStream();
      xStream.alias("xml", picAndTextMsg.getClass());
      xStream.alias("item", a.getClass());
      String picAndTextMsg2Xml = xStream.toXML(picAndTextMsg);
      System.out.println(picAndTextMsg2Xml);
      // 第三步,发送xml的格式信息给微信服务器,服务器转发给用户
      PrintWriter printWriter = resp.getWriter();
      printWriter.print(picAndTextMsg2Xml);
      

该部分所有操作源码,可以直接使用

  • CoreServlet.java(包括服务器接入、接收用户发送消息、回复普通文字消息、回复图文消息。需要第三方jar:dom4j、xstream)

    package com.gist.servlet;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.PrintWriter;
    import java.security.MessageDigest;
    import java.security.NoSuchAlgorithmException;
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.Date;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    import java.util.Set;
    
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import org.dom4j.Document;
    import org.dom4j.DocumentException;
    import org.dom4j.Element;
    import org.dom4j.io.SAXReader;
    
    import com.gist.bean.Article;
    import com.gist.bean.PicAndTextMsg;
    import com.thoughtworks.xstream.XStream;
    
    /**
     * @author 高远</n> 邮箱:wgyscsf@163.com</n> 博客 http://blog.csdn.net/wgyscsf</n>
     *         编写时期 2016-4-3 下午4:34:05
     */
    @WebServlet("/CoreServlet")
    public class CoreServlet extends HttpServlet {
        private static final long serialVersionUID = 1L;
        String TAG = "CoreServlet";
    
        /*
         * 第二步:验证服务器地址的有效性 开发者提交信息后,微信服务器将发送GET请求到填写的服务器地址URL上,
         * GET请求携带四个参数:signature、timestamp、nonce、echostr
         * 开发者通过检验signature对请求进行校验(下面有校验方式)。 若确认此次GET请求来自微信服务器,请原样返回echostr参数内容,
         * 则接入生效, 成为开发者成功,否则接入失败。
         * 
         * 加密/校验流程如下: 1. 将token、timestamp、nonce三个参数进行字典序排序 2.
         * 将三个参数字符串拼接成一个字符串进行sha1加密 3. 开发者获得加密后的字符串可与signature对比,标识该请求来源于微信
         */
        /*
         * 字典排序(lexicographical
         * order)是一种对于随机变量形成序列的排序方法。其方法是,按照字母顺序,或者数字小大顺序,由小到大的形成序列。
         */
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp)
                throws ServletException, IOException {
    
            // 设置编码
            req.setCharacterEncoding("utf-8");
            resp.setContentType("html/text;charset=utf-8");
            resp.setCharacterEncoding("utf-8");
            // 获取输出流
            PrintWriter printWriter = resp.getWriter();
    
            // 设置一个全局的token,开发者自己设置。api这样解释:Token可由开发者可以任意填写,
            // 用作生成签名(该Token会和接口URL中包含的Token进行比对,从而验证安全性)
            String token = "wgyscsf";
            // 根据api说明,获取上述四个参数
            String signature = req.getParameter("signature");
            String timestamp = req.getParameter("timestamp");
            String nonce = req.getParameter("nonce");
            String echostr = req.getParameter("echostr");
            // // temp:临时打印,观看返回参数情况
            // System.out.println(TAG + ":signature:" + signature + ",timestamp:"
            // + timestamp + ",nonce:" + nonce + ",echostr:" + echostr);
            // 根据api所说的“加密/校验流程”进行接入。共计三步
    
            // 第一步:将token、timestamp、nonce三个参数进行字典序排序
            String[] parms = new String[] { token, timestamp, nonce };// 将需要字典序排列的字符串放到数组中
            Arrays.sort(parms);// 按照api要求进行字典序排序
            // 第二步:将三个参数字符串拼接成一个字符串进行sha1加密
            // 拼接字符串
            String parmsString = "";// 注意,此处不能=null。
            for (int i = 0; i < parms.length; i++) {
                parmsString += parms[i];
            }
            // sha1加密
            String mParms = null;// 加密后的结果
            MessageDigest digest = null;
            try {
                digest = java.security.MessageDigest.getInstance("SHA");
            } catch (NoSuchAlgorithmException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            digest.update(parmsString.getBytes());
            byte messageDigest[] = digest.digest();
            // Create Hex String
            StringBuffer hexString = new StringBuffer();
            // 字节数组转换为 十六进制 数
            for (int i = 0; i < messageDigest.length; i++) {
                String shaHex = Integer.toHexString(messageDigest[i] & 0xFF);
                if (shaHex.length() < 2) {
                    hexString.append(0);
                }
                hexString.append(shaHex);
            }
            mParms = hexString.toString();// 加密结果
    
            /*
             * api要求: 若确认此次GET请求来自微信服务器,请原样返回echostr参数内容, 则接入生效, 成为开发者成功,否则接入失败。
             */
            // 第三步: 开发者获得加密后的字符串可与signature对比,标识该请求来源于微信接入成功。
            // System.out.println(TAG + ":" + mParms + "---->" + signature);
            if (mParms.equals(signature)) {
                // System.out.println(TAG + ":" + mParms + "---->" + signature);
                printWriter.write(echostr);
            } else {
                // 接入失败,不用回写
                // System.out.println(TAG + "接入失败");
            }
        }
    
        /*
         * 查看api文档关于收发消息推送的消息格式基本一致。 如以下格式: <xml>
         * <ToUserName><![CDATA[toUser]]></ToUserName>
         * <FromUserName><![CDATA[fromUser]]></FromUserName>
         * <CreateTime>1348831860</CreateTime> <MsgType><![CDATA[text]]></MsgType>
         * <Content><![CDATA[this is a test]]></Content>
         * <MsgId>1234567890123456</MsgId> </xml> 那么,我们就可以进行统一处理。
         */
        /*
         * 我们先获取输入流,看输入流里面的信息。通过测试打印输出流,我们可以看到每次用户请求,都会收到req请求,请求格式是xml格式,该信息在文档中有说明。
         */
        /*
         * 特别注意,req.getInputStream()只能获取一次,并且只能读取一次。如果想要多次读取,需要另外想办法。为了简单起见,
         * 我们只获取一次req.getInputStream(),不再打印输出流信息。直接打印解析后的信息。
         */
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp)
                throws ServletException, IOException {
            // 设置编码
            req.setCharacterEncoding("utf-8");
            resp.setContentType("html/text;charset=utf-8");
            resp.setCharacterEncoding("utf-8");
    
            /*
             * 该部分我们获取用户发送的信息,并且解析成<K,V>的形式进行显示
             */
            // 解析用户发送过来的信息
            InputStream is = req.getInputStream();// 拿取请求流
            // 将解析结果存储在HashMap中
            Map<String, String> map = new HashMap<String, String>();
            // 解析xml,将获取到的返回结果xml进行解析成我们习惯的文字信息
            SAXReader reader = new SAXReader();// 第三方jar:dom4j【百度:saxreader解析xml】
            Document document = null;
            try {
                document = reader.read(is);
            } catch (DocumentException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            }
            // 得到xml根元素
            Element root = document.getRootElement();
            // 得到根元素的所有子节点
            List<Element> elementList = root.elements();
    
            // 遍历所有子节点
            for (Element e : elementList)
                map.put(e.getName(), e.getText());
    
            // 测试输出
            Set<String> keySet = map.keySet();
            // 测试输出解析后用户发过来的信息
            System.out.println(TAG + ":解析用户发送过来的信息开始");
            for (String key : keySet) {
                System.out.println(key + ":" + map.get(key));
            }
            System.out.println(TAG + ":解析用户发送过来的信息结束");
    
            /*
             * 该部分我们尝试按照文档的要求格式给用户回复文本信息、图文消息。重点:按照文档要求构造需要的参数。特别注意:参数区分大小写。
             */
    
            // //实例1:发送普通文本消息,请查看文档关于“回复文本消息”的xml格式
            //
            // // 第一步:按照回复文本信息构造需要的参数
            // TextMsg textMsg = new TextMsg();
            // textMsg.setToUserName(map.get("FromUserName"));// 发送和接收信息“User”刚好相反
            // textMsg.setFromUserName(map.get("ToUserName"));
            // textMsg.setCreateTime(new Date().getTime());// 消息创建时间 (整型)
            // textMsg.setMsgType("text");// 文本类型消息
            // textMsg.setContent("我是服务器回复给用户的信息");
            //
            // // // 第二步,将构造的信息转化为微信识别的xml格式【百度:xstream bean转xml】
            // XStream xStream = new XStream();
            // xStream.alias("xml", textMsg.getClass());
            // String textMsg2Xml = xStream.toXML(textMsg);
            // System.out.println(textMsg2Xml);
            //
            // // // 第三步,发送xml的格式信息给微信服务器,服务器转发给用户
            // PrintWriter printWriter = resp.getWriter();
            // printWriter.print(textMsg2Xml);
    
            // //实例2,发送图文消息。请查看文档关于“回复图文消息”的xml格式
    
            // 第一步:按照回复图文信息构造需要的参数
            List<Article> articles = new ArrayList<Article>();
            Article a = new Article();
            a.setTitle("我是图片标题");
            a.setUrl("www.baidu.com");// 该地址是点击图片跳转后
            a.setPicUrl("http://b.hiphotos.baidu.com/image/pic/item/08f790529822720ea5d058ba7ccb0a46f21fab50.jpg");// 该地址是一个有效的图片地址
            a.setDescription("我是图片的描述");
            articles.add(a);
            PicAndTextMsg picAndTextMsg = new PicAndTextMsg();
            picAndTextMsg.setToUserName(map.get("FromUserName"));// 发送和接收信息“User”刚好相反
            picAndTextMsg.setFromUserName(map.get("ToUserName"));
            picAndTextMsg.setCreateTime(new Date().getTime());// 消息创建时间 (整型)
            picAndTextMsg.setMsgType("news");// 图文类型消息
            picAndTextMsg.setArticleCount(1);
            picAndTextMsg.setArticles(articles);
            // 第二步,将构造的信息转化为微信识别的xml格式【百度:xstream bean转xml】
            XStream xStream = new XStream();
            xStream.alias("xml", picAndTextMsg.getClass());
            xStream.alias("item", a.getClass());
            String picAndTextMsg2Xml = xStream.toXML(picAndTextMsg);
            System.out.println(picAndTextMsg2Xml);
            // 第三步,发送xml的格式信息给微信服务器,服务器转发给用户
            PrintWriter printWriter = resp.getWriter();
            printWriter.print(picAndTextMsg2Xml);
        }
    }
    
  • TestMsg.java(普通文字消息bean)

    package com.gist.bean;
    
    /**
     * @author 高远</n> 邮箱:wgyscsf@163.com</n> 博客 http://blog.csdn.net/wgyscsf</n>
     *         编写时期 2016-4-4 下午2:09:27
     */
    public class TextMsg {
        private String ToUserName;
        private String FromUserName;
        private long CreateTime;
        private String MsgType;
    
        @Override
        public String toString() {
            return "TextMsg [ToUserName=" + ToUserName + ", FromUserName="
                    + FromUserName + ", CreateTime=" + CreateTime + ", MsgType="
                    + MsgType + ", Content=" + Content + "]";
        }
    
        private String Content;
    
        public TextMsg(String toUserName, String fromUserName, long createTime,
                String msgType, String content) {
            super();
            ToUserName = toUserName;
            FromUserName = fromUserName;
            CreateTime = createTime;
            MsgType = msgType;
            Content = content;
        }
    
        public TextMsg() {
            super();
        }
    
        public String getToUserName() {
            return ToUserName;
        }
    
        public void setToUserName(String toUserName) {
            ToUserName = toUserName;
        }
    
        public String getFromUserName() {
            return FromUserName;
        }
    
        public void setFromUserName(String fromUserName) {
            FromUserName = fromUserName;
        }
    
        public long getCreateTime() {
            return CreateTime;
        }
    
        public void setCreateTime(long createTime) {
            CreateTime = createTime;
        }
    
        public String getMsgType() {
            return MsgType;
        }
    
        public void setMsgType(String msgType) {
            MsgType = msgType;
        }
    
        public String getContent() {
            return Content;
        }
    
        public void setContent(String content) {
            Content = content;
        }
    }
    
  • Article.java(图文消息内部Article bean)

    package com.gist.bean;
    
    /**
     * @author 高远</n> 邮箱:wgyscsf@163.com</n> 博客 http://blog.csdn.net/wgyscsf</n>
     *         编写时期 2016-4-4 下午2:47:08
     */
    public class Article {
        private String Title;
    
        @Override
        public String toString() {
            return "item [Title=" + Title + ", Description=" + Description
                    + ", PicUrl=" + PicUrl + ", Url=" + Url + "]";
        }
    
        public String getTitle() {
            return Title;
        }
    
        public void setTitle(String title) {
            Title = title;
        }
    
        public String getDescription() {
            return Description;
        }
    
        public void setDescription(String description) {
            Description = description;
        }
    
        public String getPicUrl() {
            return PicUrl;
        }
    
        public void setPicUrl(String picUrl) {
            PicUrl = picUrl;
        }
    
        public String getUrl() {
            return Url;
        }
    
        public void setUrl(String url) {
            Url = url;
        }
    
        private String Description;
        private String PicUrl;
        private String Url;
    
    }
    
  • PicAndTextMsg.java(图文消息 bean)

    package com.gist.bean;
    
    import java.util.List;
    
    /**
     * @author 高远</n> 邮箱:wgyscsf@163.com</n> 博客 http://blog.csdn.net/wgyscsf</n>
     *         编写时期 2016-4-4 下午2:47:08
     */
    public class PicAndTextMsg {
        private String ToUserName;
        private String FromUserName;
        private long CreateTime;
        private String MsgType;
        private int ArticleCount;
        private List<Article> Articles;
    
        @Override
        public String toString() {
            return "PicAndTextMsg [ToUserName=" + ToUserName + ", FromUserName="
                    + FromUserName + ", CreateTime=" + CreateTime + ", MsgType="
                    + MsgType + ", ArticleCount=" + ArticleCount + ", Articles="
                    + Articles + "]";
        }
    
        public String getToUserName() {
            return ToUserName;
        }
    
        public void setToUserName(String toUserName) {
            ToUserName = toUserName;
        }
    
        public String getFromUserName() {
            return FromUserName;
        }
    
        public void setFromUserName(String fromUserName) {
            FromUserName = fromUserName;
        }
    
        public long getCreateTime() {
            return CreateTime;
        }
    
        public void setCreateTime(long createTime) {
            CreateTime = createTime;
        }
    
        public String getMsgType() {
            return MsgType;
        }
    
        public void setMsgType(String msgType) {
            MsgType = msgType;
        }
    
        public int getArticleCount() {
            return ArticleCount;
        }
    
        public void setArticleCount(int articleCount) {
            ArticleCount = articleCount;
        }
    
        public List<Article> getArticles() {
            return Articles;
        }
    
        public void setArticles(List<Article> articles) {
            Articles = articles;
        }
    
    }
    
2016-08-06 00:39:48 linxi7 阅读数 1534
  • 微信公众号开发7-用户管理-微信开发php

    微信公众平台开发之微信用户开发管理是子恒老师《微信公众平台开发》视频教程的第7部。详细讲解了用php开发微信,对微信公众平台中的粉丝用户管理开发。内容包含微信公众平台用户分组,获取微信用户列表,查询用户详情等等。欢迎反馈,微信/QQ:68183131

    14052 人正在学习 去看看 秦子恒

最近在练习自定义view,找了一些资料,模仿了一下微信通讯录的实现效果,首先看一下效果图:
这里写图片描述

记录下来当做笔记以备后用。
第一步:是绘制26个字母的view:
在每个构造方法中调用下面的init()方法实现画笔的初始化

private void init() {
        //去掉字母锯齿的参数
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setColor(Color.WHITE);
        // mPaint.setTypeface(Typeface.DEFAULT_BOLD);
        mPaint.setTextSize(40);//设置字母大小
    }

然后是onDraw()方法进行绘制:

@Override
    protected void onDraw(Canvas canvas) {
        //这里用来绘制背景,当字母被触摸的时候会变成灰色
        if (mTouched) {
            canvas.drawColor(0x30000000);
        }

        for (int i = 0; i < letterLength; i++) {
            String text = LETTERS[i];
            // 计算坐标
            int x = (int) (cellWidth / 2.0f - mPaint.measureText(text) / 2.0f);
            // 获取文本的高度
            Rect bounds = new Rect();// 矩形
            mPaint.getTextBounds(text, 0, text.length(), bounds);
            int textHeight = bounds.height();
            int y = (int) (cellHeight / 2.0f + textHeight / 2.0f + i* cellHeight);

            // 设置文本颜色
            mPaint.setColor(Color.BLACK);


            // 绘制文本A-Z
            canvas.drawText(text, x, y, mPaint);
        }
    }

根据注释可以清楚的知道绘制的原理,就是计算26个字母的x和y坐标,x左边不变的,y坐标规律的增加。

接下来重写onTouchEvent()方法

boolean mTouched = false;
int touchIndex = -1;

    @Override
public boolean onTouchEvent(MotionEvent event) {
        int index = -1;
        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
        case MotionEvent.ACTION_MOVE:
            // 获取当前触摸到的字母索引
            index = (int) (event.getY() / cellHeight);
            if (index >= 0 && index < LETTERS.length) {
                // 判断是否跟上一次触摸到的一样
            if (index != touchIndex) {

                if (listener != null) {
                                    listener.onLetterUpdate(LETTERS[index]);
                    }

                    touchIndex = index;
                }
            }
            mTouched = true;
            break;
        case MotionEvent.ACTION_UP:
            if (listener != null) {
                listener.onFinished();
            }
            touchIndex = -1;
            mTouched = false;
            break;

        default:
            break;
        }
        invalidate();

        return true;
    }

原理就是根据两个索引值,即当前按下字母的索引是否和上次的一样,如果不一样才会调用接口把字母暴露给调用方,否则如果不进行判断的话,就会导致当手指在快速索引条上移动的时候总是会弹出相同的字母。还有就是记录了一个按下的mTouched值,如果按下或者移动设置为true,否则为false。这样就可以根据是否按下来绘制背景改变颜色了。再看一下ACTION_UP事件中暴露onFinished()方法给调用方,主要是为了将弹出的字母隐藏掉。这样当手指按下的时候会弹出当前的字母,当手指抬起就会立即消失。

当view的大小发生变化时获取单元格的宽和高

@Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        // 获取单元格的宽和高

        cellWidth = getMeasuredWidth();

        int mHeight = getMeasuredHeight();
        cellHeight = mHeight * 1.0f / LETTERS.length;

    }

接下来就是回调接口:

    /**
     * 暴露一个字母的监听
     */
    public interface OnLetterUpdateListener {
        void onLetterUpdate(String letter);

        void onFinished();
    }

    private OnLetterUpdateListener listener;

    /**
     * 设置字母更新监听
     * 
     * @param listener
     */
    public void setListener(OnLetterUpdateListener listener) {
        this.listener = listener;
    }

以上就是快速索引条的绘制过程。

接下来看一下MainActivity.java的主要代码:

// 设置监听
bar.setListener(new OnLetterUpdateListener() {
            @Override
    public void onLetterUpdate(String letter) {
        showLetter(letter);
        // 根据字母定位ListView, 找到集合中第一个以letter为拼音首字母的对象,得到索引
        for (int i = 0; i < persons.size(); i++) {
            Person person = persons.get(i);
            String l = person.getPinyin().charAt(0) + "";
            if (TextUtils.equals(letter, l)) {
                // 匹配成功
                mMainList.setSelection(i);
                break;
            }
        }
    }

    @Override
    public void onFinished() {
        tv_center.setVisibility(View.GONE);
    }
});

这就是实现回调方法,弹出显示字母框和隐藏字母的功能。
还有一个就是对ListView中联系人拼音的第一个字母和手指此刻按下的字母进行比较,如果相等的话,就会将ListView位置定位到这个字母,也就是把该字母显示在第一个位置。

这个是显示字母的代码:

/**
* 显示字母
*/
protected void showLetter(String letter) {
    tv_center.setVisibility(View.VISIBLE);
    tv_center.setText(letter);
}

这个是对通讯录进行排序的方法:

private void fillAndSortData(ArrayList<Person> persons) {
    List<String> nameList = PhoneContactsUtils.getContactsName(this);
    // 填充数据
    for (int i = 0; i < nameList.size(); i++) {
        String name = nameList.get(i);
        persons.add(new Person(name));
    }

    // 进行排序
    Collections.sort(persons);
    }

以上就是主要的代码,还有工具类、实体类和适配器的代码没有贴出来,感觉太多了贴出来不方便阅读。实体类就是一个名字和拼音两个成员变量,并且要实现Comparable接口以便进行联系人根据字母排序。还有读取手机通讯录联系人等没什么好讲的,直接放到源码包中就好了。

源码下载地址:Android自定义view实现微信通讯录一模一样效果

2018-11-28 17:59:21 gt15886435708 阅读数 731
  • 微信公众号开发7-用户管理-微信开发php

    微信公众平台开发之微信用户开发管理是子恒老师《微信公众平台开发》视频教程的第7部。详细讲解了用php开发微信,对微信公众平台中的粉丝用户管理开发。内容包含微信公众平台用户分组,获取微信用户列表,查询用户详情等等。欢迎反馈,微信/QQ:68183131

    14052 人正在学习 去看看 秦子恒

/**
     * 根据url获取json数据
     * @param url
     * @return
     * @throws IOException 
     */
    public static String getUserInfo(String token,String code) throws IOException {
        StringBuilder json = new StringBuilder();
        BufferedReader in = null;
        HttpURLConnection conn = null;
        try {
            URL url = new URL("https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo?access_token="+token+"&code="+code);
            conn = (HttpURLConnection) url.openConnection();
            in = new BufferedReader(new InputStreamReader(conn.getInputStream(),"UTF-8"));
            String inputLine = null;
            while ((inputLine = in.readLine()) != null) {
                json.append(inputLine);
            }
        }catch (IOException e) {
            e.printStackTrace();
        }finally{
            in.close();
            conn.disconnect();
        }
        return json.toString();
    }

运行结果:

{
    "UserId": "zhagnsan",
    "DeviceId": "864253035285998",
    "errcode": 0,
    "errmsg": "ok",
    "user_ticket": "4TmADZ0hfsAw4QWIayl6IepHr6f9rQTwsSoowj4VuqXLQf2yWOoAXW8zKOnvPOZjb4hJI-MHsisDfkDKQbITqxo0a1P5XnYw6ZtHlsTjMTI",
    "expires_in": 1800
}

2016-02-20 23:25:02 qq_25526067 阅读数 2662
  • 微信公众号开发7-用户管理-微信开发php

    微信公众平台开发之微信用户开发管理是子恒老师《微信公众平台开发》视频教程的第7部。详细讲解了用php开发微信,对微信公众平台中的粉丝用户管理开发。内容包含微信公众平台用户分组,获取微信用户列表,查询用户详情等等。欢迎反馈,微信/QQ:68183131

    14052 人正在学习 去看看 秦子恒

最近刚好有要做一个聊天通讯录,有点类似微信,所以自己也把探索的总结一下,可能还要进一步优化;
不多说,先上效果图看看:
模拟器有点卡
这里先说一些,要使用拼音字母转换,我们引用了第三方拼音库(pinyin4j-2.5.0.jar),下面我也会提供出来,这里面有源代码
(字母排序搜索)类似微信通讯录效果
用到的图片:
头像弹框
还有在drawable目录下的一些样式布局:
在sidebar_background.xml中:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle" >
    <gradient
        android:angle="90.0"
        android:endColor="#11000000"
        android:startColor="#11000000" />
    <corners
        android:bottomLeftRadius="3dip"
        android:bottomRightRadius="3dip"
        android:topLeftRadius="3dip"
        android:topRightRadius="3dip" />
</shape>

在colleague_list_item_selector.xml中:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_pressed="true" ><color android:color="#d9d9d9" />       
    </item>
    <item android:state_pressed="false"><color android:color="#ffffff" />
    </item>
    <item android:state_selected="false"><color android:color="#ffffff" />
    </item>
    <item ><color android:color="#ffffff"/></item>
</selector>

在layout布局中,一个Activity布局,一个子条目item布局和列表底部显示总共多少人的尾布局;
在activity_main.xml中;

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="#e1e2e6"
    android:orientation="vertical" >
    <FrameLayout
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" >       
            <ListView
                android:id="@+id/colleague_lv_Contact"
                android:layout_width="fill_parent"
                android:layout_height="fill_parent"
                android:layout_gravity="center"
                android:divider="@null" />
        <TextView
            android:id="@+id/colleague_tv_dialog"
            android:layout_width="80.0dip"
            android:layout_height="80.0dip"
            android:layout_gravity="center"
           android:background="@drawable/colleague_dialog"
            android:gravity="center"
            android:textColor="#ffffffff"
            android:textSize="30.0dip"
            android:visibility="invisible" />
        <com.demo.address.SideBarView
            android:id="@+id/colleague_sideBar"
            android:layout_width="20dp"
            android:layout_height="wrap_content"
            android:layout_gravity="right|center_vertical" />
    </FrameLayout>
</LinearLayout>

在fragment_colleague_item.xml中;

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:gravity="center_vertical"
    android:orientation="vertical" >

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:background="#ffffff"
        android:orientation="vertical" >
        <TextView
            android:id="@+id/colleague_tv_catalog"
            android:layout_width="fill_parent"
            android:layout_height="22dp"
            android:background="#ecedf1"
            android:gravity="center_vertical"
            android:paddingLeft="5dip"
            android:text="A"
            android:textColor="#666666"
            android:textSize="16sp" />
        <LinearLayout
            android:id="@+id/colleague_layout"
            android:layout_width="fill_parent"
            android:layout_height="60dp"
            android:layout_weight="1"        android:background="@drawable/colleague_list_item_selector"
            android:gravity="center_vertical" >
            <ImageView
                android:id="@+id/colleague_iv_avatar"
                android:layout_width="45dp"
                android:layout_height="45dp"
                android:layout_marginLeft="10dp"
                android:src="@drawable/chat_im_touxiang" />
            <LinearLayout
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:orientation="vertical" >
                <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content" 
                    android:layout_marginLeft="13dip">
                    <TextView 
                    android:layout_width="wrap_content"                      android:layout_height="wrap_content"
                        android:textSize="16sp"
                        android:id="@+id/process_item"/>
                    <TextView
                      android:id="@+id/colleague_nickname"
                       android:layout_width="wrap_content"
                      android:layout_height="wrap_content"                       
                        android:ellipsize="end"
                        android:gravity="center_vertical"
                        android:lines="1"
                        android:maxLength="30"
                        android:text="aaaaa"
                        android:textColor="#666666"
                        android:textSize="16sp" />
                </LinearLayout>
            </LinearLayout>
        </LinearLayout>
        <View
            android:id="@+id/line"
            android:layout_width="fill_parent"
            android:layout_height="0.8dp"
            android:layout_marginLeft="10dp"
            android:background="#ecedf1" />
    </LinearLayout>
</LinearLayout>

在fragment_colleague_item_footer.xml中;

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"   
    android:background="#e1e2e6"
    android:orientation="vertical" >  
    <TextView
        android:id="@+id/colleague_tv_total"
        android:layout_width="match_parent"  
        android:layout_height="56dp"
        android:gravity="center"  
        android:text="0位联系人"
        android:textColor="#cccccc"
        android:textSize="16sp" />
</LinearLayout>

布局就这样;代码中主要有这几个类,
主要有这几个类
SideBarView类主要是画出通讯录侧边栏A-Z的排列和搜索

public class SideBarView extends View {
    // 触摸事件
    private OnTouchingLetterChangedListener onTouchingLetterChangedListener;
    private char[] l;
    // private String[] l;
    // 选中单选框
    private int choose = -1;
    private Activity activity;
    // 排序
    private SectionIndexer sectionIndexter = null;
    // 侧边栏列表
    private ListView list;
    //
    private TextView mDialogText;
    private final int m_nItemHeight = 12;
    private boolean isFirst = true;
    private int width, height;
    private int singleHeight;
    private static final int textSize = 27;
    public SideBarView(Activity context) {
        super(context);
        this.activity = context;
        init();
    }
    public SideBarView(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.activity = (Activity) context;
        init();
    }
    private void init() { 
        l = new char[] { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
                'U', 'V', 'W', 'X', 'Y', 'Z','#' };
        /*
         * l= new String[] { "A", "B", "C", "D", "E", "F", "G", "H", "I", "J",
         * "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X",
         * "Y", "Z" };
         */
    }

    public SideBarView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init();
    }
    public void setListView(ListView _list) {
        list = _list;   
        sectionIndexter = (SectionIndexer) _list.getAdapter();
    }
    public void setTextView(TextView mDialogText) {
        this.mDialogText = mDialogText;
    }
    public boolean onTouchEvent(MotionEvent event) {
        super.onTouchEvent(event);  
        int i = (int) event.getY();
        //int idx = (i - getTop()) / singleHeight -1;
        int idx = (i - getTop()) / singleHeight;
        // int idx = i / m_nItemHeight;
        if (idx >= l.length) {
            idx = l.length - 1;
        } else if (idx < 0) {
            idx = 0;
        }
        if (event.getAction() == MotionEvent.ACTION_DOWN || event.getAction() == MotionEvent.ACTION_MOVE) {
            mDialogText.setVisibility(View.VISIBLE);
            this.setBackgroundResource(R.drawable.sidebar_background);
            mDialogText.setText("" + l[idx]);
            if (sectionIndexter == null) {
                //由于传递进来的是一个addHeadView的适配,所以它的getAdapter()方法获取到的其实是包装过后的HeaderViewListAdapter,所以必须拿到没包装之前的适配器
                HeaderViewListAdapter ha = (HeaderViewListAdapter) list.getAdapter();
                sectionIndexter = (SectionIndexer) ha.getWrappedAdapter();
                //              sectionIndexter = (SectionIndexer) list.getAdapter();
            }
            int position = -1;
            if (sectionIndexter != null)
                position = sectionIndexter.getPositionForSection(l[idx]);
            if (position == -1) {
                return true;
            }
            list.setSelection(position);        
        } else{
            mDialogText.setVisibility(View.INVISIBLE);
        }
        if(event.getAction()==MotionEvent.ACTION_UP){
            this.setBackgroundColor(android.graphics.Color.parseColor("#00000000")); 
        }
        return true;
    }

    // 用画笔设置A-Z的距离
    protected void onDraw(Canvas canvas) {
        Paint paint = new Paint();
        super.onDraw(canvas);
        // 获取焦点改变背景颜色.
        if (isFirst) {
            // 获取对应高度
            height = getHeight();
            // 获取对应宽度
            width = getWidth();
            isFirst = false;
        }
        // 获取每一个字母的高度
        singleHeight = height / l.length;
        for (int i = 0; i < l.length; i++) {
            //paint.setColor(0xff595c61);
    paint.setColor(android.graphics.Color.parseColor("#999999"));
            paint.setTypeface(Typeface.DEFAULT_BOLD);
            paint.setAntiAlias(true);
            // TODO 适配
            paint.setTextSize(textSize);
            // 选中的状态
            if (i == choose) {
            paint.setColor(Color.parseColor("#3399ff"));
                paint.setFakeBoldText(true);
            }
            // x坐标等于中间-字符串宽度的一半.
            float xPos = width / 2 - paint.measureText(l, i, 1) / 2;
            // float xPos = width / 2 - paint.measureText(l[i]) / 2;
            float yPos = singleHeight * i + singleHeight;

            // public void drawText (char[] text, int index, int count, float x,
            // float y, Paint paint)
            /*
             * text:字符串内容,可以采用String格式,也可以采用char字符数组形式。 x:显示位置的x坐标。 y:显示位置的y坐标。
             * index
             * :显示的起始字符位置。count:显示字符的个数。start:显示的起始字符位置。end:显示的终止字符位置。paint:
             * 绘制时所使用的画笔。
             */
            // canvas.drawText(l[i], xPos, yPos, paint);
            canvas.drawText(l, i, 1, xPos, yPos, paint);
            // 重置画笔
            paint.reset();
        }
    }
    /**
     * 向外公开的方法 设置sideBar的右侧触摸监听
     * 
     * @param onTouchingLetterChangedListener
     */
    public void setOnTouchingLetterChangedListener(OnTouchingLetterChangedListener onTouchingLetterChangedListener) {
        this.onTouchingLetterChangedListener = onTouchingLetterChangedListener;
    }
    /*
     * 接口
     */
    public interface OnTouchingLetterChangedListener {
        public void onTouchingLetterChanged(String s);
    }
}

PingYinUtil类中,主要是有两个方法汉字转换位汉语拼音和汉字转换位汉语拼音首字母;

public class PingYinUtil {
       /**   
        * 汉字转换位汉语拼音,英文字符不变   
        * @param chines 汉字   
        * @return 拼音   
        */    
    public static String getPingYin(String inputString) {
        HanyuPinyinOutputFormat format = new HanyuPinyinOutputFormat();//格式转换器
        format.setCaseType(HanyuPinyinCaseType.LOWERCASE);
        format.setToneType(HanyuPinyinToneType.WITHOUT_TONE);//音标
        format.setVCharType(HanyuPinyinVCharType.WITH_V);
        char[] input = inputString.trim().toCharArray();
        String output = "";
        try {
            for (int i = 0; i < input.length; i++) {//遍历用户名字符数组长度               
                 if (java.lang.Character.toString(input[i]).matches("[\\u4E00-\\u9FA5]+")) {
                    String[] temp = PinyinHelper.toHanyuPinyinStringArray(input[i], format);
                    output += temp[0];
                }else{
//                  Logcat.printInfo("这个字符非汉字:"+java.lang.Character.toString(input[i]));
                    output += java.lang.Character.toString(input[i]);
                }                
            }
        } catch (BadHanyuPinyinOutputFormatCombination e) {
            e.printStackTrace();
        }
        return output;
    }

    /**
     * 汉字转换位汉语拼音首字母,英文字符不变
     * 
     * @param chines
     *            汉字
     * @return 拼音  
     */ 
    public static String converterToFirstSpell(String chines) {
        String pinyinName = "";
        //将传过来的用户名转换为字符数组  
        char[] nameChar = chines.toCharArray();
        HanyuPinyinOutputFormat defaultFormat = new HanyuPinyinOutputFormat();//格式转换器
        defaultFormat.setCaseType(HanyuPinyinCaseType.UPPERCASE);//忽略大小写
        defaultFormat.setToneType(HanyuPinyinToneType.WITHOUT_TONE);//音标
        for (int i = 0; i < nameChar.length; i++) {//遍历用户名字符数组长度
            if (nameChar != null && nameChar.length != 0) {
                //处理特殊符号,例如:¥哈哈
                if(i==0){
                    if (java.lang.Character.toString(nameChar[i]).matches("[\\u4E00-\\u9FA5]+")) {                    
                    } else{
                        if(Character.isUpperCase(nameChar[i])|Character.isLowerCase(nameChar[i])){}else{
                            return "#";
                        }               
                    }                   
                }  
                if (nameChar[i] > 128) {
                    // 是汉字
                    try { 
                        String[] pinyinNameStart = PinyinHelper.toHanyuPinyinStringArray(nameChar[i], defaultFormat);
                        if (pinyinNameStart != null&&pinyinNameStart.length != 0)
                            pinyinName = pinyinName + pinyinNameStart[0].charAt(0);

                    } catch (BadHanyuPinyinOutputFormatCombination e) {
                        e.printStackTrace();
                    }
                } else if (Character.isUpperCase(nameChar[i])) {  
                    pinyinName += nameChar[i];
                } else if (Character.isLowerCase(nameChar[i])) {
                    pinyinName += (char) (nameChar[i] - 32) + "";
                }               
            }
        }
        if (pinyinName == ""){
                pinyinName = "#";
        }
        return pinyinName;
    }
}

PinyinComparator类接口用来对ListView中的数据根据A-Z进行排序,前面两个if判断主要是将不是以汉字开头的数据放在后面

public class PinyinComparator implements Comparator{

    @Override
    public int compare(Object o1, Object o2) {
        String str1 = PingYinUtil.getPingYin((String) o1);
        String str2 = PingYinUtil.getPingYin((String) o2);
        if(str1.contains("#")&&!str2.contains("#")){
            return 1;
        }else if (str2.contains("#")&&!str1.contains("#")){
            return -1;
        }else{
            return str1.compareTo(str2);
        }
    }
}

ContactAdapter设配器,一般使用在项目设配器都大同小异;改改就用在另一项目了;适配器写得粗糙,需要在优化,让用户体验更好。

package com.demo.address;
public class ContactAdapter extends BaseAdapter implements SectionIndexer {
    private Context mContext;
    private List<String> users;
    private int j = 0;// 用来记录当前置顶的位置,防止没有字幕索引时跳转到第一个字母

    public ContactAdapter(Context mContext, List<String> users) {
        this.mContext = mContext;
        this.users = sort(users);
    }

    static class ViewHolder {
        // 显示A目录
        TextView tvCatalog;
        // 头像
        ImageView ivAvatar;
        // 昵称
        TextView tvNick;
        // 分割线
        View line;
    }

    // 排序的方法
    private List<String> sort(List<String> users) {
        List<String> userList = new ArrayList<String>();
        Map<String, Object> map = new HashMap<String, Object>();
        for (int i = 0; i < users.size(); i++) {        
            map.put(PingYinUtil.converterToFirstSpell(users.get(i)) + i, users.get(i));
        }

        Set<String> keySets = map.keySet();
        for (int i = 0; i < keySets.size(); i++) {
            // Logcat.printInfo("keySets:"+keySets);
        }
        String[] keys = new String[keySets.size()];

        keySets.toArray(keys);
        for (int i = 0; i < keys.length; i++) {
            // System.out.println("排序前:"+keys[i]+":"+((User)map.get(keys[i])).getUsername());
        }
        Arrays.sort(keys, new PinyinComparator());
        for (int i = 0; i < keys.length; i++) {
            userList.add((String) map.get(keys[i]));
            // System.out.println("排序后:"+keys[i]+":"+((User)map.get(keys[i])).getUsername());
        }
        return userList;
    }

    @Override
    public int getCount() {
        return users.size();
    }

    @Override
    public String getItem(int position) {
        return users.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    public String getUser(int position) {
        return users.get(position);
    }

    @Override
    public View getView(final int position, View convertView, ViewGroup parent) {
        final String user = users.get(position);
        final String nickName = user;


        ViewHolder viewHolder = null;
//       if (convertView == null) {
        convertView = LayoutInflater.from(mContext).inflate(
                R.layout.fragment_colleague_item, null);
        viewHolder = new ViewHolder();
        // 初始化A目录
        viewHolder.tvCatalog = (TextView) convertView
                .findViewById(R.id.colleague_tv_catalog);
        // 初始化头像
        viewHolder.ivAvatar = (ImageView) convertView
                .findViewById(R.id.colleague_iv_avatar);
        // 昵称
        viewHolder.tvNick = (TextView) convertView
                .findViewById(R.id.colleague_nickname);
        // 分割线
        viewHolder.line = (View) convertView.findViewById(R.id.line);
        convertView.setTag(viewHolder);
//       } else {
//       viewHolder = (ViewHolder) convertView.getTag();
//       }
        // 判断A目录显示状态
        String spellName = PingYinUtil.converterToFirstSpell(nickName);
        String catalog = spellName.substring(0, 1);
        String nextSpellName = "";//下一个item的名字
        String nextCatalog = "";//下一个item的名字的首字母

        if (position == 0) {
            viewHolder.tvCatalog.setVisibility(View.VISIBLE);
            viewHolder.tvCatalog.setText(catalog);
        } else {
            String lastSpellName = PingYinUtil.converterToFirstSpell(users.get(position - 1));
            String lastCatalog = lastSpellName.substring(0, 1);
            // 判断是否显示组名
            if (catalog.equals(lastCatalog)) {
                viewHolder.tvCatalog.setVisibility(View.GONE);
            } else {
                viewHolder.tvCatalog.setVisibility(View.VISIBLE);
                viewHolder.tvCatalog.setText(catalog);
            }
        }
        // 判断避免数组索引越界的异常
        if ((position + 1) < users.size()) {
            nextSpellName = PingYinUtil.converterToFirstSpell(users.get(
                    position + 1));
            nextCatalog = nextSpellName.substring(0, 1);
        }
        // 判断是否显示item下面的分割线
        if (!catalog.equals(nextCatalog)) {
            viewHolder.line.setVisibility(View.GONE);
        }
        // 设置昵称
        viewHolder.tvNick.setText(nickName);
        return convertView;
    }

    // 根据分类列的索引号获得该序列的首个位置
    @Override
    public int getPositionForSection(int section) {
        for (int i = 0; i < users.size(); i++) {
            String spellName = PingYinUtil.getPingYin(users.get(i));
            String l = spellName.substring(0, 1);
            char firstChar = l.toUpperCase().charAt(0);
            if (firstChar == section) {
                j = i;
                return i;
            }
        }
        return j;
    }

    @Override
    public int getSectionForPosition(int position) {
        return 0;
    }

    @Override
    public Object[] getSections() {
        return null;
    }

}

在Activity中;

package com.demo.address;

//实现通讯录字母索引
public class MainActivity extends Activity {
    // 显示同事的列表
        public ListView lvContact;
        // A-Z侧边栏
        private SideBarView sideBar;
        // 点击A-Z的弹出框
        private TextView tvdialog;
        // 排序适配器
        private ContactAdapter adapter;
        private TextView  tvTotal;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        lvContact = (ListView) findViewById(R.id.colleague_lv_Contact);

        View footerView = View.inflate(this, R.layout.fragment_colleague_item_footer,null);
        lvContact.addFooterView(footerView);
        tvTotal = (TextView) footerView.findViewById(R.id.colleague_tv_total);
        sideBar = (SideBarView) findViewById(R.id.colleague_sideBar);
        tvdialog = (TextView) findViewById(R.id.colleague_tv_dialog);
        sideBar.setTextView(tvdialog);  
        sideBar.setListView(lvContact);
        tvTotal.setOnClickListener(new OnClickListener() {                      
            @Override
            public void onClick(View arg0) {
                System.out.println("点击没处理");                    
            }
        }); 
        adapter = new ContactAdapter(this, initLeft());
        lvContact.setAdapter(adapter);
        tvTotal.setText(initLeft().size()+"位联系人"); 
    }
    private List<String> initLeft(){
        List<String> list=new ArrayList<String>();
            list.add("王石");     
            list.add("狄云");
            list.add("曹操");  
            list.add("刘备");
            list.add("Mark");
            list.add("关羽");
            list.add("爱神");
            list.add("孙大圣");        
            list.add("神灯");
            list.add("菜鸡");
            list.add("周亮"); 
            list.add("彤仔");
            list.add("A-lin");
            list.add("玛德");
            list.add("曹彦");
            list.add("曹尼玛");
            list.add("段yu");
            list.add("¥哈哈");
            list.add("cc");
            list.add("B人cc");
            return list;
    }
}

这样就能运行效果了,其中可能需要继续优化,具体有不足之处,欢迎大家吐槽。

java获取微信JS SDK

阅读数 388

没有更多推荐了,返回首页