2019-08-27 07:57:45 qq_35029061 阅读数 64
  • 微信支付开发-微信公众号开发12-微信开发php

    微信公众平台开发之微信支付开发是子恒老师《微信公众平台开发》视频教程的第12部。详细讲解了用php进行微信支付的开发。内容包含获取支付密钥,微信公众号支付开发,扫码支付,微信刷卡支付,异步处理支付结果等等。欢迎反馈,微信/QQ:68183131

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

微信开发与代码的编写(一)

微信开发环境的搭建

目前移动开发处于比较火的的趋势,很多的开发者都跃跃欲试,目前移动App开发领域主要分为以下几种类型

  

  我在平时的工作中接触得比较多的就是基于Android的Native App开发和基于微信公众号的Light App开发,今天就来带领大家快速进入微信公众号的开发领域.

微信开发环境搭建

  工欲善其事,必先利其器。要做微信公众号开发,那么要先准备好两样必不可少的东西:

  1、要有一个用来测试的公众号。

  2、用来调式代码的开发环境。

 

注册测试公众号

  微信公众号分为服务号、订阅号、企业号,订阅号可以个人申请,服务号和企业号要有企业资质才可以。

  我们所说的微信公众号开发指的是订阅号和服务号。

  关于订阅号和服务器的区别,官方是这样解释的

  服务号:主要偏向于服务交互(功能类似12315,114,银行,提供绑定信息,服务交互),每月可群发4条消息;服务号适用人群:媒体、企业、政府或其他组织。

  订阅号:主要偏向于为用户传达资讯,(功能类似报纸杂志,为用户提供新闻信息或娱乐趣事),每天可群发1条消息;订阅号适用人群:个人、媒体、企业、政府或其他组织。

  个人订阅号有一些接口是没有权限的,也就是说个人订阅号无法调用一些高级的权限接口,下图就是一个我的个人订阅号所具备权限列表,如下图所示:

  

  而一些高级接口,如生成二维码、网页授权、自定义菜单、微信支付这样的接口权限个人订阅号是没有调用权限的,如上图红色框起来的那些接口,个人订阅号都无法调用。

  幸运的是,微信公众平台提供了测试公众账号,测试公众号的注册地址为:http://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login,只需要到这个页面,点击登录,并用自己的微信客户端扫码,并授权登录,就可以获得属于自己的测试公众号。测试公众号具备几乎所有的接口,所以平时学习微信公众号开发时,就可以去注册一个测试公众号,然后使用这个测试公众号做开发就可以了。不废话了,还是先注册一个测试公众号吧

  访问http://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login,注册一个测试公众号。

  注册测试公众号的步骤如下图所示:

  

    

   用微信扫描上述的二维码进行登录,登录成功后,就可以看到腾讯分配给我们的测试公众号的信息了,如下图所示:

  

  测试公众号的所拥有的接口权限如下:

  

  可以看到,测试公众号拥有大部分的接口调用权限,因此用测试公众号来学习微信开发是完全可以的。

 

搭建微信本地调试环境

  开发基于微信公众号的应用最大的痛苦之处就是调试问题,每次实现一个功能后都需要部署到一个公网服务器进行测试,因为微信用户每次向公众号发起请求时,微信服务器会先接收到用户的请求,然后再转发到我们的服务器上,也就是说,微信服务器是要和我们的服务器进行网络交互,所以我们必须保证我们的服务器外网可以访问到,这种部署到公网服务器进行测试的做法对于我们开发者来说简直是噩梦。所以我们要想一个办法可以做到本地部署,本地调试代码,而要做到这一点,那么我们要解决的问题就是将内网的部署服务器映射到外网,让微信服务器可以正常访问到,幸运的是,借助于第三方软件Ngrok,我们就可以做得到。Ngrok是一个免费的软件Ngrok,使用Ngrok后,我们就可以实现内网穿透,也就是说我们可以将内网的服务器映射到外网给别人访问,这对于我们在本地开发环境中调试微信代码是以及给用户演示一些东西非常快速和有帮助的,因为可以直接使用我们自己的内网的电脑作为服务器。

  国内提供Ngrok服务比较好的网站是:http://natapp.cn/,如下图所示:

  

  从http://natapp.cn/网站上下载ngrok客户端,如下图所示:

  

   下载完成后,得到一个压缩包,解压压缩包后,得到一个文件夹,里面有如下图所示的几个文件:

  

  打开CMD命令行窗口,进入到ngrok_windows目录下,然后输入如下命令:

  ngrok -config ngrok.cfg -subdomain xdp 8080,如下图所示:

  

  xdp是我自己自定义的一个域名, 8080为本地服务器的运行端口,执行完上述命令后,我们就可以本地的127.0.0.1:8080服务器映射到外网了,如下图所示:

  

  此时外网的用户可以直接使用http://xdp.ngrok.natapp.cn这个域名访问到我内网的127.0.0.1:8080服务器了,如下图所示:

  

  

  使用了ngrok之后,我们就可以把内网的服务器当成公网服务器来使用了.访问的速度也还在可以接受的范围内吧,截止到目前为止ngrok是可用的,微信公众号服务器是可以访问的,这样一来也就不妨碍我们做本地调式了。到此,我们的微信本地调试开发环境就算是搭建好了。

 

微信公众平台的基本原理

  在开始做之前,先简单介绍了微信公众平台的基本原理。

  微信服务器就相当于一个转发服务器,终端(手机、Pad等)发起请求至微信服务器,微信服务器然后将请求转发给我们的应用服务器。应用服务器处理完毕后,将响应数据回发给微信服务器,微信服务器再将具体响应信息回复到微信App终端。

  通信协议为:HTTP

  数据传输格式为:XML

  具体的流程如下图所示:

  

  来一张更加直观的图吧:

  

  我们需要做的事情,就是对微信服务器转发的HTTP请求做出响应。具体的请求内容,我们按照特定的XML格式去解析,处理完毕后,也要按照特定的XML格式返回。

 

微信公众号接入

  在微信公众平台开发者文档上,关于公众号接入这一节内容在接入指南上写的比较详细的,文档中说接入公众号需要3个步骤,分别是:

  1、填写服务器配置
  2、验证服务器地址的有效性
  3、依据接口文档实现业务逻辑

  其实,第3步已经不能算做公众号接入的步骤,而是接入之后,开发人员可以根据微信公众号提供的接口所能做的一些开发。

  第1步中服务器配置包含服务器地址(URL)、Token和EncodingAESKey。

  服务器地址即公众号后台提供业务逻辑的入口地址,目前只支持80端口,之后包括接入验证以及任何其它的操作的请求(例如消息的发送、菜单管理、素材管理等)都要从这个地址进入。接入验证和其它请求的区别就是,接入验证时是get请求,其它时候是post请求;

  Token可由开发者可以任意填写,用作生成签名(该Token会和接口URL中包含的Token进行比对,从而验证安全性);

  EncodingAESKey由开发者手动填写或随机生成,将用作消息体加解密密钥。本例中全部以未加密的明文消息方式,不涉及此配置项。

  第2步,验证服务器地址的有效性,当点击“提交”按钮后,微信服务器将发送一个http的get请求到刚刚填写的服务器地址,并且携带四个参数:

  

  接到请求后,我们需要做如下三步,若确认此次GET请求来自微信服务器,原样返回echostr参数内容,则接入生效,否则接入失败。

  1. 将token、timestamp、nonce三个参数进行字典序排序
  2. 将三个参数字符串拼接成一个字符串进行sha1加密
  3. 开发者获得加密后的字符串可与signature对比,标识该请求来源于微信

  下面我们用Java代码来演示一下这个验证过程

  使用IDE(Eclipse或者IntelliJ IDEA)创建一个JavaWeb项目,这里我使用的是IntelliJ IDEA,项目目录结构如下图所示:

  

  编写一个servlevt,在其中的doGet方法中定义校验方法,具体代码如下:

package me.gacl.wx.web.servlet;
  
  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 java.io.IOException;
  import java.security.MessageDigest;
  import java.security.NoSuchAlgorithmException;
  import java.util.Arrays;
  
  /**
   * Created by xdp on 2016/1/25.
   * 使用@WebServlet注解配置WxServlet,urlPatterns属性指明了WxServlet的访问路径
   */
  @WebServlet(urlPatterns="/WxServlet")
  public class WxServlet extends HttpServlet {
  
      /**
       * Token可由开发者可以任意填写,用作生成签名(该Token会和接口URL中包含的Token进行比对,从而验证安全性)
       * 比如这里我将Token设置为gacl
       */
     private final String TOKEN = "gacl";
  
      protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  
      }
  
      protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
          System.out.println("开始校验签名");
          /**
           * 接收微信服务器发送请求时传递过来的4个参数
           */
          String signature = request.getParameter("signature");//微信加密签名signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数。
          String timestamp = request.getParameter("timestamp");//时间戳
          String nonce = request.getParameter("nonce");//随机数
          String echostr = request.getParameter("echostr");//随机字符串
          //排序
          String sortString = sort(TOKEN, timestamp, nonce);
          //加密
          String mySignature = sha1(sortString);
          //校验签名
         if (mySignature != null && mySignature != "" && mySignature.equals(signature)) {
              System.out.println("签名校验通过。");
              //如果检验成功输出echostr,微信服务器接收到此输出,才会确认检验完成。
              //response.getWriter().println(echostr);
              response.getWriter().write(echostr);
          } else {
              System.out.println("签名校验失败.");
          }
  
      }
  
      /**
       * 排序方法
       *
       * @param token
       * @param timestamp
       * @param nonce
       * @return
       */
      public String sort(String token, String timestamp, String nonce) {
          String[] strArray = {token, timestamp, nonce};
         Arrays.sort(strArray);
          StringBuilder sb = new StringBuilder();
          for (String str : strArray) {
              sb.append(str);
          }
  
          return sb.toString();
      }
  
      /**
       * 将字符串进行sha1加密
       *
       * @param str 需要加密的字符串
       * @return 加密后的内容
       */
     public String sha1(String str) {
          try {
              MessageDigest digest = MessageDigest.getInstance("SHA-1");
              digest.update(str.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);
              }
              return hexString.toString();
  
          } catch (NoSuchAlgorithmException e) {
              e.printStackTrace();
          }
         return "";
     }
 }

  我这里用的Servlet3.0,使用Servlet3.0的好处就是可以直接使用@WebServlet注解映射Servlet的访问路径,不再需要在web.xml文件中进行配置.

  将WxStudy项目部署到Tomcat服务器中运行,直接启动项目,然后用ngrok将本地8080端口映射到外网(如何使用ngrok请参考博客《微信开发学习总结(一)——微信开发环境搭建》)。如下图所示:

  

  测试是否可以通过http://xdp.ngrok.natapp.cn地址正常访问,测试结果如下:

  

  可以看到,我们的项目已经可以被外网正常访问到了。

  进入微信测试公众号管理界面,在接口配置信息中填入映射的外网地址和token,如下图所示:

点击提交按钮,页面会提示配置成功,

  

  IDE的控制台中输出了校验通过的信息,如下图所示:

  

  到此,我们的公众号应用已经能够和微信服务器正常通信了,也就是说我们的公众号已经接入到微信公众平台了。

 

access_token管理

access_token介绍

  我们的公众号和微信服务器对接成功之后,接下来要做的就是根据我们的业务需求调用微信公众号提供的接口来实现相应的逻辑了。在使用微信公众号接口中都需要一个access_token。

  关于access_token,在微信公众平台开发者文档上的获取接口调用凭据有比较详细的介绍:access_token是公众号的全局唯一票据,公众号调用各接口时都需使用access_token,开发者需要妥善保存access_token的存储至少要保留512个字符空间。access_token的有效期目前为2个小时,需定时刷新,重复获取将导致上次获取的access_token失效。并且每天调用获取access_token接口的上限是2000次。

  总结以上说明,access_token需要做到以下两点:

  1.因为access_token有2个小时的时效性,要有一个机制保证最长2个小时重新获取一次。

  2.因为接口调用上限每天2000次,所以不能调用太频繁。

微信公众平台提供的获取access_token的接口

  关于access_token的获取方式,在微信公众平台开发者文档上有说明,公众号可以调用一个叫"获取access token"的接口来获取access_token。

  获取access token接口调用请求说明

    http请求方式: GET

    请求的URL地址:https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
       

  我们可以看到,调用过程中需要传递appID和AppSecret,appID和AppSecret是在申请公众号的时候自动分配给公众号的,相当于公众号的身份标示,使用微信公众号的注册帐号登录到腾讯提供的微信公众号管理后台就可以看到自己申请的公众号的AppID和AppSecret,如下图所示:

             

  这是我申请公众号测试帐号时分配到的AppID和AppSecret。

获取access_token方案以及具体实现

  这里采用的方案是这样的,定义一个默认启动的servlet,在init方法中启动一个Thread,这个进程中定义一个无限循环的方法,用来获取access_token,当获取成功后,此进程休眠7000秒(7000秒=1.944444444444444小时),否则休眠3秒钟继续获取。流程图如下:

  

  下面正式开始在工程中实现以上思路,因为返回的数据都是json格式,这里会用到阿里的fastjson库,为构造请求和处理请求后的数据序列化和反序列化提供支持。

  1.定义一个AccessToken实体类

 package me.gacl.wx.entry;
 
 /**
   * AccessToken的数据模型
  * Created by xdp on 2016/1/25.
  */
 public class AccessToken {
 
     //获取到的凭证
     private String accessToken;
    //凭证有效时间,单位:秒
     private int expiresin;
 
     public String getAccessToken() {
         return accessToken;
     }
 
     public void setAccessToken(String accessToken) {
        this.accessToken = accessToken;
     }
 
     public int getExpiresin() {
         return expiresin;
     }
 
     public void setExpiresin(int expiresin) {
         this.expiresin = expiresin;
     }
 }

     2.定义一个AccessTokenInfo类,用于存放获取到的AccessToken,代码如下:

 package me.gacl.wx.Common;
 
  import me.gacl.wx.entry.AccessToken;
 
 /**
  * Created by xdp on 2016/1/25.
  */
 public class AccessTokenInfo {
 
     //注意是静态的
     public static AccessToken accessToken = null;
 }

  3.编写一个用于发起https请求的工具类NetWorkHelper,代码如下:

 package me.gacl.wx.util;
 
 import javax.net.ssl.*;
 import java.io.BufferedReader;
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.net.URL;
 import java.security.cert.CertificateException;
 import java.security.cert.X509Certificate;
 
 /**
 * 访问网络用到的工具类
  */
 public class NetWorkHelper {
 
     /**
      * 发起Https请求
      * @param reqUrl 请求的URL地址
      * @param requestMethod
      * @return 响应后的字符串
      */
     public String getHttpsResponse(String reqUrl, String requestMethod) {
         URL url;
         InputStream is;
         String resultData = "";
         try {
             url = new URL(reqUrl);
             HttpsURLConnection con = (HttpsURLConnection) url.openConnection();
             TrustManager[] tm = {xtm};
 
             SSLContext ctx = SSLContext.getInstance("TLS");
             ctx.init(null, tm, null);
 
             con.setSSLSocketFactory(ctx.getSocketFactory());
             con.setHostnameVerifier(new HostnameVerifier() {
                 @Override
                 public boolean verify(String arg0, SSLSession arg1) {
                     return true;
                 }
             });
 
 
             con.setDoInput(true); //允许输入流,即允许下载
 
             //在android中必须将此项设置为false
             con.setDoOutput(false); //允许输出流,即允许上传
             con.setUseCaches(false); //不使用缓冲
             if (null != requestMethod && !requestMethod.equals("")) {
                 con.setRequestMethod(requestMethod); //使用指定的方式
             } else {
                 con.setRequestMethod("GET"); //使用get请求
             }
             is = con.getInputStream();   //获取输入流,此时才真正建立链接
             InputStreamReader isr = new InputStreamReader(is);
             BufferedReader bufferReader = new BufferedReader(isr);
             String inputLine;
             while ((inputLine = bufferReader.readLine()) != null) {
                 resultData += inputLine + "\n";
             }
             System.out.println(resultData);
 
         } catch (Exception e) {
             e.printStackTrace();
         }
         return resultData;
     }
 
     X509TrustManager xtm = new X509TrustManager() {
         @Override
         public X509Certificate[] getAcceptedIssuers() {
             return null;
         }
 
         @Override
         public void checkServerTrusted(X509Certificate[] arg0, String arg1)
                 throws CertificateException {
 
         }
 
         @Override
         public void checkClientTrusted(X509Certificate[] arg0, String arg1)
                 throws CertificateException {
 
         }
     };
 }

  getHttpsResponse方法是请求一个https地址,参数requestMethod为字符串“GET”或者“POST”,传null或者“”默认为get方式。

  4.定义一个默认启动的servlet,在init方法中启动一个新的线程去获取accessToken

 package me.gacl.wx.web.servlet;
 
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONObject;
 import me.gacl.wx.Common.AccessTokenInfo;
 import me.gacl.wx.entry.AccessToken;
 import me.gacl.wx.util.NetWorkHelper;
 
 import javax.servlet.ServletException;
 import javax.servlet.annotation.WebInitParam;
 import javax.servlet.annotation.WebServlet;
 import javax.servlet.http.HttpServlet;
 
 /**
  * 用于获取accessToken的Servlet
  * Created by xdp on 2016/1/25.
  */
 @WebServlet(
         name = "AccessTokenServlet",
         urlPatterns = {"/AccessTokenServlet"},
         loadOnStartup = 1,
         initParams = {
                 @WebInitParam(name = "appId", value = "wxbe4d433e857e8bb1"),
                 @WebInitParam(name = "appSecret", value = "ccbc82d560876711027b3d43a6f2ebda")
         })
 public class AccessTokenServlet extends HttpServlet {
 
     @Override
     public void init() throws ServletException {
         System.out.println("启动WebServlet");
         super.init();
 
         final String appId = getInitParameter("appId");
         final String appSecret = getInitParameter("appSecret");
 
         //开启一个新的线程
         new Thread(new Runnable() {
             @Override
             public void run() {
                 while (true) {
                     try {
                         //获取accessToken
                         AccessTokenInfo.accessToken = getAccessToken(appId, appSecret);
                         //获取成功
                         if (AccessTokenInfo.accessToken != null) {
                             //获取到access_token 休眠7000秒,大约2个小时左右
                             Thread.sleep(7000 * 1000);
                             //Thread.sleep(10 * 1000);//10秒钟获取一次
                         } else {
                             //获取失败
                             Thread.sleep(1000 * 3); //获取的access_token为空 休眠3秒
                         }                     } catch (Exception e) {
                         System.out.println("发生异常:" + e.getMessage());
                         e.printStackTrace();
                         try {
                             Thread.sleep(1000 * 10); //发生异常休眠1秒
                         } catch (Exception e1) {
 
                         }
                     }
                 }
 
             }
         }).start();
     }
 
     /**
      * 获取access_token
      *
      * @return AccessToken
      */
     private AccessToken getAccessToken(String appId, String appSecret) {
         NetWorkHelper netHelper = new NetWorkHelper();
         /**
          * 接口地址为https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET,其中grant_type固定写为client_credential即可。
          */
         String Url = String.format("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s", appId, appSecret);
         //此请求为https的get请求,返回的数据格式为{"access_token":"ACCESS_TOKEN","expires_in":7200}
         String result = netHelper.getHttpsResponse(Url, "");
         System.out.println("获取到的access_token="+result);
         //使用FastJson将Json字符串解析成Json对象
         JSONObject json = JSON.parseObject(result);
         AccessToken token = new AccessToken();
         token.setAccessToken(json.getString("access_token"));
         token.setExpiresin(json.getInteger("expires_in"));
         return token;
     }
 }

        AccessTokenServlet采用注解的方式进行配置
  至此代码实现完毕,将项目部署,看到控制台输出如下:

  

  为了方便看效果,可以把休眠时间设置短一点,比如10秒获取一次,然后将access_token输出。

  下面做一个测试jsp页面,并把休眠时间设置为10秒,这样过10秒刷新页面,就可以看到变化

 <%-- Created by IntelliJ IDEA. --%>
  <%@ page contentType="text/html;charset=UTF-8" language="java" %>
  <%@ page import="me.gacl.wx.Common.AccessTokenInfo"%>
  <html>
   <head>
     <title></title>
   </head>
   <body>
     微信学习
     <hr/>
     access_token为:<%=AccessTokenInfo.accessToken.getAccessToken()%>
   </body>
 </html>

  

  10秒钟后刷新页面,access_token变了,如下图所示:

  

 

接收微信服务器发送的消息并做出响应

  经过上述的三步,我们开发前的准备工作已经完成了,接下来要做的就是接收微信服务器发送的消息并做出响应

  从微信公众平台接口消息指南中可以了解到,当用户向公众帐号发消息时,微信服务器会将消息通过POST方式提交给我们在接口配置信息中填写的URL,而我们就需要在URL所指向的请求处理类WxServlet的doPost方法中接收消息、处理消息和响应消息。

编写一个用于处理消息的工具类

  编写处理消息的工具栏,工具类代码如下:

 package me.gacl.wx.util;
  
  import org.dom4j.Document;
  import org.dom4j.Element;
  import org.dom4j.io.SAXReader;
  
  import javax.servlet.http.HttpServletRequest;
  import java.io.InputStream;
  import java.text.DateFormat;
  import java.text.SimpleDateFormat;
  import java.util.Date;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  
  /**
   * 消息处理工具类
   * Created by xdp on 2016/1/26.
   */
  public class MessageHandlerUtil {
  
      /**
       * 解析微信发来的请求(XML)
       * @param request
       * @return map
       * @throws Exception
       */
      public static Map<String,String> parseXml(HttpServletRequest request) throws Exception {
          // 将解析结果存储在HashMap中
          Map<String,String> map = new HashMap();
         // 从request中取得输入流
          InputStream inputStream = request.getInputStream();
          System.out.println("获取输入流");
          // 读取输入流
         SAXReader reader = new SAXReader();
          Document document = reader.read(inputStream);
          // 得到xml根元素
          Element root = document.getRootElement();
          // 得到根元素的所有子节点
          List<Element> elementList = root.elements();
  
          // 遍历所有子节点
         for (Element e : elementList) {
              System.out.println(e.getName() + "|" + e.getText());
              map.put(e.getName(), e.getText());
          }
  
          // 释放资源
          inputStream.close();
          inputStream = null;
          return map;
      }
  
      // 根据消息类型 构造返回消息
      public static String buildXml(Map<String,String> map) {
          String result;
          String msgType = map.get("MsgType").toString();
          System.out.println("MsgType:" + msgType);
          if(msgType.toUpperCase().equals("TEXT")){
              result = buildTextMessage(map, "孤傲苍狼在学习和总结微信开发了,构建一条文本消息:Hello World!");
          }else{
              String fromUserName = map.get("FromUserName");
              // 开发者微信号
              String toUserName = map.get("ToUserName");
              result = String
                      .format(
                              "<xml>" +
                                      "<ToUserName><![CDATA[%s]]></ToUserName>" +
                                      "<FromUserName><![CDATA[%s]]></FromUserName>" +
                                      "<CreateTime>%s</CreateTime>" +
                                      "<MsgType><![CDATA[text]]></MsgType>" +
                                     "<Content><![CDATA[%s]]></Content>" +
                                      "</xml>",
                              fromUserName, toUserName, getUtcTime(),
                              "请回复如下关键词:\n文本\n图片\n语音\n视频\n音乐\n图文");
          }
  
          return result;
      }
  
      /**
       * 构造文本消息
       *
       * @param map
       * @param content
       * @return
       */
      private static String buildTextMessage(Map<String,String> map, String content) {
          //发送方帐号
          String fromUserName = map.get("FromUserName");
          // 开发者微信号
          String toUserName = map.get("ToUserName");
          /**
           * 文本消息XML数据格式
           * <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>
          */
         return String.format(
                 "<xml>" +
                         "<ToUserName><![CDATA[%s]]></ToUserName>" +
                         "<FromUserName><![CDATA[%s]]></FromUserName>" +
                         "<CreateTime>%s</CreateTime>" +
                         "<MsgType><![CDATA[text]]></MsgType>" +
                         "<Content><![CDATA[%s]]></Content>" + "</xml>",
                 fromUserName, toUserName, getUtcTime(), content);
     }
 
    private static String getUtcTime() {
         Date dt = new Date();// 如果不需要格式,可直接用dt,dt就是当前系统时间
         DateFormat df = new SimpleDateFormat("yyyyMMddhhmm");// 设置显示格式
         String nowTime = df.format(dt);
         long dd = (long) 0;
         try {
             dd = df.parse(nowTime).getTime();
         } catch (Exception e) {
 
         }
        return String.valueOf(dd);
     }
 }

 为了方便解析微信服务器发送给我们的xml格式的数据,这里我们借助于开源框架dom4j去解析xml(这里使用的是dom4j-2.0.0-RC1.jar)

  

 

在WxServlet的doPost方法中处理请求

  WxServlet的doPost方法的代码如下:

  /**
      * 处理微信服务器发来的消息
      */
     protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
         // TODO 接收、处理、响应由微信服务器转发的用户发送给公众帐号的消息
         // 将请求、响应的编码均设置为UTF-8(防止中文乱码)
         request.setCharacterEncoding("UTF-8");
         response.setCharacterEncoding("UTF-8");
         System.out.println("请求进入");
         String result = "";
         try {
             Map<String,String> map = MessageHandlerUtil.parseXml(request);
             System.out.println("开始构造消息");
             result = MessageHandlerUtil.buildXml(map);
             System.out.println(result);
             if(result.equals("")){
                 result = "未正确响应";
             }
         } catch (Exception e) {
             e.printStackTrace();
             System.out.println("发生异常:"+ e.getMessage());
         }
         response.getWriter().println(result);
     }

  到此,我们的WxServlet已经可以正常处理用户的请求并做出响应了.接下来我们测试一下我们开发好的公众号应用是否可以正常和微信用户交互

  将WxStudy部署到Tomcat服务器,启动服务器,记得使用ngrok将本地Tomcat服务器的8080端口映射到外网,保证接口配置信息的URL地址:http://xdp.ngrok.natapp.cn/WxServlet可以正常与微信服务器通信

  登录到我们的测试公众号的管理后台,然后用微信扫描一下测试号的二维码,如下图所示:

  关注成功后,我们开发好的公众号应用会先给用户发一条提示用户操作的文本消息,微信用户根据提示操作输入"文本",我们的公众号应用接收到用户请求后就给用户回复了一条我们自己构建好的文本消息,如下图所示:

 

  我们的公众号应用响应给微信用户的文本消息的XML数据如下:

 <xml>
   <ToUserName><![CDATA[ojADgs0eDaqh7XkTM9GvDmdYPoDw]]></ToUserName>
   <FromUserName><![CDATA[gh_43df3882c452]]></FromUserName>
   <CreateTime>1453755900000</CreateTime>
   <MsgType><![CDATA[text]]></MsgType>
   <Content><![CDATA[孤傲苍狼在学习和总结微信开发了,构建一条文本消息:Hello World!]]></Content>
</xml>

  测试公众号的管理后台也可以看到关注测试号的用户列表,如下图所示:

  通过这个简单的入门程序,我们揭开了微信开发的神秘面纱了.

 

 

 

 

 

 

 

 

 

 

 

 

2017-09-19 07:23:09 towtotow 阅读数 899
  • 微信支付开发-微信公众号开发12-微信开发php

    微信公众平台开发之微信支付开发是子恒老师《微信公众平台开发》视频教程的第12部。详细讲解了用php进行微信支付的开发。内容包含获取支付密钥,微信公众号支付开发,扫码支付,微信刷卡支付,异步处理支付结果等等。欢迎反馈,微信/QQ:68183131

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

微信公众号开发,

涉及到的内容非常多,

所以微信官方团队只给我们提供了一个最基础的开发示例脚本。


5guanfang_jiaoben.jpg


虽然说这个脚本很简单,

但是也具备了公众号开发的基本框架,

这节课程跟你分析下官方的脚本,

掌握好后,

可以加速你开发的速度…

微信开发源代码详细分析

http://edu.csdn.net/course/detail/2586/40529

2018-02-03 09:43:54 yji534123343 阅读数 945
  • 微信支付开发-微信公众号开发12-微信开发php

    微信公众平台开发之微信支付开发是子恒老师《微信公众平台开发》视频教程的第12部。详细讲解了用php进行微信支付的开发。内容包含获取支付密钥,微信公众号支付开发,扫码支付,微信刷卡支付,异步处理支付结果等等。欢迎反馈,微信/QQ:68183131

    28204 人正在学习 去看看 秦子恒
一、微信开发的整体介绍
开放平台:https://open.weixin.qq.com/
公众平台:https://mp.weixin.qq.com/
1、微信开发的种类
①移动应用开发
支持微信的分享、收藏、支付三大功能,原生开发的人员去使用的开发类别
②网站应用开发
网站支持使用微信账号进行注册和登录,从而来实现:提高用户的留存率,方便不同账号的统一(结合unionId)
③公众账号开发


④公众账号第三方平台
优势在于免繁琐设置,方便小白用户去使用。


2、公众账号开发
①服务号
主要偏向于给用户提供查询服务(招商银行、麦当劳。。)
显示在好友列表,每个月只能群发4条消息


②订阅号
主要偏向于给用户提供资讯(朝闻天下、Web前端。。)
显示在订阅号文件夹内,每天都可以群发1条消息


③小程序
2017年1月9号 (2007年1月9号 推出iphone)
基于微信的平台,ui提供了很多组件,js提供了很多接口


(微信官方的小程序:小程序示例,包含了微信官方封装好的组件、以及封装好的接口)


目标:万物互联 (IOT),  二维码
开放范围:(不包含个人)
http://www.wxapp-union.com/forum.php?mod=viewthread&tid=495


https://mp.weixin.qq.com/debug/wxadoc/dev/index.html


数据存储和服务器:
两种解决方案 ①腾讯云(不到100) ②godaddy


④企业号
主要是建立起 企业与人的关系,提供信息、和企业相关的定制功能


温馨提示:
1)如果想简单的发送消息,达到宣传效果,建议可选择订阅号;
2)如果想进行商品销售,进行商品售卖,建议可申请服务号;
3)如果想用来管理内部企业员工、团队,对内使用,可申请企业号。
4)订阅号可通过微信认证资质审核通过后有一次升级为服务号的入口,升级成功后类型不可再变。
5)服务号不可变更成订阅号。


3、九大类高级接口


语音识别接口
客服接口
OAuth2.0 网页授权接口
生成带参数的二维码接口
获取用户地理位置接口
获取用户基本信息接口
获取关注者列表接口
用户分组接口
上传下载多媒体文件接口


+ 微信支付
(十大类接口的使用,必须是认证过的服务号和订阅号才能使用)




4、正式环境的搭建
(公众账号-》订阅号)


购买自己的域名(万网、dnsPod、godaddy)
购买申请主机空间(新浪云、阿里云、腾讯云、大米云、godaddy、aws)
注册订阅号
登录到订阅号管理接口、服务器的配置、调用10大类接口


5、测试环境的搭建
(公众账号-》订阅号)


主机空间(新浪云)
注册订阅号
登录到订阅号管理接口、服务器的配置、调用10大类接口


6、注册订阅号
①填写邮箱、密码
②到邮箱中激活账号
③选择类型
④登记信息(个人、组织、媒体、企业。。)
⑤填写公众号的信息(名称、介绍、头像)






二、个人订阅号(jssdk)


1、实现功能:统计页面被访问的次数


分析:
①存储需要持久保存的数据
数据库、文件系统、内存中:fileSystem
②读写过程
读:file_get_contents($fileUrl);
写:file_put_contents($fileUrl,$content);


需要apache启动


2、新浪云storage


通过bucket管理我们的文件,如何访问?
saestor:// testbucket/count.txt




3、jsSDK的使用步骤
①绑定域名
②引入js文件
③注入接口(将使用接口的名称,写在config方法中jsAPIList这个数组中)
④在ready中调用接口
⑤在error中处理失败的异常情况






sample.zip有4个文件,
前两个文件都是用来存储数据的,jssdk.php进行网络请求以及数据的读写操作,sample.php文件主要完成接口的注入和调用


jssdk.php封装了2个方法get_php_file、set_php_file,如果部署在服务端,写文件会出现权限拒绝的问题。
①将存储文件 放到新浪云的bucket中
②修改读写文件的路径为 saestor://bucketName/**.php




require subscribe 需要订阅测试账号。




三、版本控制工具(git)


1、版本控制
记录一个或者多个文件内容变化,以便于未来查询指定的版本信息。


svn 集中式/git  分布式
①防止代码的丢失
②团队协作
③版本还原
④更好的管理代码


2、git介绍
用于代码的版本控制,使用方式:命令行/图形化


git(分布式版本控制工具)与github(托管开源项目的网站,托管项目的方式采用的是git)


3、自己使用git将项目上传到github


①申请github的账号 
https://github.com/
注册账号,在选择计划的时候选择continue,在编辑经验时选择skip跳过。
②去邮件激活账号
③start project 只需要指定repository的名称,点击create去创建。


④安装git
一路next,最后点击install安装 取消所有的勾选就可以了。
⑤启动git
到所有程序中,找到git,找到gitbash,点击启动


⑥基础命令
ls (list) 查看当前目录下的文件
clear 清除当前的屏幕信息
pwd (print the work directory)显示当前目录
mkdir web1609 在当前的路径中创建一个叫做web1609的目录
cd web1609 (change directory)
touch a.txt 创建一个叫做a.txt的一个文件


通过电脑中的文件系统找到文件,写上了hello git.


git的用法


git init 初始化仓库
git status 查看仓库的状态
git add a.txt 将a.txt添加到代码仓库
git commit -m '第一次提交'     添加到缓存区


配置上传到github上的用户信息:
git config --global user.name "web1609best"
git config --global user.email "web1609@vip.163.com"


设置完账号之后,重新提交到缓存区:
git commit -m '第一次提交'     添加到缓存区


将本地的代码设置它推送到github上的地址:
git remote add origin https://github.com/web1609best/web1609.git


将当前目录的git仓库推送到github对应的origin地址的master分支
git push origin master

















2017-02-05 11:58:03 gccll 阅读数 3323
  • 微信支付开发-微信公众号开发12-微信开发php

    微信公众平台开发之微信支付开发是子恒老师《微信公众平台开发》视频教程的第12部。详细讲解了用php进行微信支付的开发。内容包含获取支付密钥,微信公众号支付开发,扫码支付,微信刷卡支付,异步处理支付结果等等。欢迎反馈,微信/QQ:68183131

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

新年快乐,开工咯~~!!

2017年,新年上班第一天,大家都忙着串领导办公室领红包,我这人比较懒,就工位旁边领导领了个,闲着无聊,来写这新年第一遍博客,程序其实是去年回家前搞定的,今天算是给补上这篇记录(表情:害羞~~)!

祝大家新年好,新年快乐,新年升职加薪~ ~ !

微信小计算器

前言:这个计算器承接该文章中的计算器的继续和延伸,同时将其做了稍微调整移植到了微信上,经测试微信运行正常。

主要修改点:
  1. 去掉原先的 DOM 相关的操作部分,修改成,直接修改数据去刷新视图;
  2. 由于该计算器是通过对象形式,构造器方式封装和创建的,但是没有DOM情况下需要去改变视图,则需要将该对象与小程序功能相关联起来,采取方式是:通过构造器参数,将 Page 对象传给 Calculator 来实现数据绑定和修改;
  3. 模块化处理:这个简单,直接将获取计算器对象实例的函数导出即可;
    module.exports = { getInstance: getInstance };

    getInstance 属于单例实现方式,实现原理这里采用的是直接使用全局变量方式,这种方式算是最简捷的(偷了下懒 ~~)

  4. 字体的处理,因为原先用的是外部字体 DigifaceWide 原来情况直接使用 @font-face 简单就能搞定,这里折腾了下,经过 baidu + google 也简单完成了,主要实现原理也简单:

    • 将目标字体文件拷贝出来,放到 https://transfonter.org/ 这个链接去处理下就行;
    • 然后把生成的文件 stylesheet.css 里的代码拷贝出来放到 index.wxss 中的 @font-face 体中,就可以在样式中直接使用该字体了,后面在使用 awesome 图标的时候也通过这种方式实现了,还不错!!
  5. 最后需要处理的就是布局的转换了,由于开始对 view 的特性还不能完全熟悉,导致纠结了挺久,总是每个按钮成了单独一行,通过浮动,绝对定位都没能解决,因为一开始按键直接用 view 去处理了,最后换成 <button> 然后通过浮动按键元素解决,最后行的浮动,直接使用 overflow: auto; 解决,并没采用之前方式:clearfix,发现用溢出处理更快更好用,深层原理还没深入去了解(不能做拿来主义,不可以,不能,要加紧获取知识步伐,↖(^ω^)↗)。

主要部分实现代码:
  1. 效果图

    工具上:

    计算器

    微信上:

    微信计算器

  2. 视图部分

    一开始计划用模版去实现,后面发现用模版可能更麻烦点,中间有些地方还是要单独处理,并且涉及到数据更新问题,因此还是决定直接这种排版方式来写控件部分。

    // index.wxml
    
    <view class="container">
     <view id="main-board">
        <view id="board-title">CALCULATOR</view>
        <view id="board-result">
            <view id="result-date">{{dateContent}}</view>
            <view id="result-up"><span>{{resultUp}}</span></view>
            <view id="result-down">{{resultDown}}</view>
        </view>
        <view id="board-keys" bindtap="bindKeyTap">
            <view id="board-line-0" class="board-row">
                <button data-text="AC" class="bg-red">AC</button>
                <button data-text="CE" class="bg-red">CE</button>
                <button data-text="÷" id="divide">÷</button>
                <button data-text="×">×</button>
            </view>
            <view class="board-row">
                <button data-text="7">7</button>
                <button data-text="8">8</button>
                <button data-text="9">9</button>
                <button data-text="-">-</button>
            </view>
            <view class="board-row">
                <button data-text="4">4</button>
                <button data-text="5">5</button>
                <button data-text="6">6</button>
                <button data-text="+">+</button>
            </view>
            <view class="board-row">
                <view class="equal-left">
                    <view>
                        <button data-text="1">1</button>
                        <button data-text="2">2</button>
                        <button data-text="3">3</button>
                    </view>
                    <view id="btn-0">
                        <button data-text="0" class="btn-0">0</button>
                        <button data-text=".">.</button>
                    </view>
                </view>
                <view class="equal-right">
                    <button data-text="=" class="btn-equal">=</button>
                </view>
            </view>
        </view>
    </view>
  3. 控制部分代码

    控制部分主要就 onLoadbindKeyTap 里面的处理,前者里面创建计算器实例,后者绑定事件处理,这里能让人感受到模块化和对象化的好处,和优雅((^__^)),只需要简单的两行代码就搞定,当然文件最开头的 require 模块的引入不能忽略,这都有赖于前面辛苦的结果,面向对象编程的益处。

    
    var calculator = require('./Calculator.js');
    
    //获取应用实例
    var app = getApp()
    Page({
      data: {
        motto: 'Hello World',
        userInfo: {},
        digits: [ ['AC', 'CE', '÷'], ['7', '8', '9'], ['4', '5', '6'], ['1', '2', '3'] ],
        calculator: null,
        resultUp: '',
        resultDown: '',
        dateContent: ''
      },
    
      calculator: null,
    
      //事件处理函数
      bindViewTap: function() {
        wx.navigateTo({
          url: '../logs/logs'
        })
      },
      onLoad: function () {
        var that = this;
    
        that.calculator = calculator.getInstance(that);
    
        console.log( that.calculator );
      },
    
      bindKeyTap: function(event) {
    
        var that = this;
    
        that.calculator.calculatorClickEvent(event)
      }
    })
    
  4. 最后就是 Calculator.js 里的核心控制逻辑代码了,这个在这就不赘述了,前面的文章有更详细的设计和说明;

总结:

总的来说,微信小程序算是有了个大概的了解,至少稍微有点了解是个什么东西了,怎么开始着手去开发,针对这个计算器也算是个练手,加前期的改进和功能添加,至于小程序其他的东西,学习起来也就没那么困难了(最近小程序有点冷淡了啊,自己也试用了一些,感觉一开始进入方式就不是很方便,比较针对心中有特定目标,或者有特定关键字的使用群体,毕竟需要通过搜索才能找到自己心仪的应用,希望以后能改进 ~~ 学习步伐还是不能停止~~~)

最后祝所有努力,所有奋斗在人生旅途的人新年快乐,恭喜发财,步步高升!!

2017-09-14 16:47:56 qq_36964677 阅读数 4120
  • 微信支付开发-微信公众号开发12-微信开发php

    微信公众平台开发之微信支付开发是子恒老师《微信公众平台开发》视频教程的第12部。详细讲解了用php进行微信支付的开发。内容包含获取支付密钥,微信公众号支付开发,扫码支付,微信刷卡支付,异步处理支付结果等等。欢迎反馈,微信/QQ:68183131

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

运行界面

详细信息
聊天界面

1建立布局ui_top.xml显示界面上标题栏

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:background="#2D2E2E"
    android:orientation="horizontal"
    android:layout_height="wrap_content">
    <LinearLayout
        android:layout_weight="1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">
        <ImageView
            android:id="@+id/iv_exit"
            android:src="@mipmap/rl"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
        <TextView
            android:id="@+id/user_name"
            android:text="微信"
            android:textSize="15sp"
            android:paddingLeft="15dp"
            android:layout_gravity="center"
            android:textColor="@android:color/white"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"  />
    </LinearLayout>
    <ImageView
        android:id="@+id/iv_Setting"
        android:src="@mipmap/dux"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
</LinearLayout>

2建立用户详细详细布局ui_message.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:orientation="vertical"
    android:background="#DCE0E0"
    android:layout_height="match_parent">

    <include layout="@layout/ui_top"/>
    <LinearLayout
        android:background="@android:color/white"
        android:layout_marginTop="20dp"
        android:padding="10dp"
        android:layout_gravity="center_vertical"
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <ImageView
            android:id="@+id/iv_message_user_icon"
            android:src="@mipmap/default_useravatar"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
        <TextView
            android:id="@+id/tv_name"
            android:padding="20dp"
            android:text="昵称"
            android:textSize="15sp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />

    </LinearLayout>
    <EditText
        android:layout_gravity="center_vertical"
        android:gravity="center_vertical"
        android:background="@android:color/white"
        android:layout_marginTop="20dp"
        android:padding="10dp"
        android:hint="设置备注和标签"
        android:textSize="15sp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
    <LinearLayout
        android:background="@android:color/white"
        android:layout_marginTop="20dp"
        android:padding="10dp"
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <TextView
            android:id="@+id/tv_location"
            android:text="地区"
            android:textSize="15sp"
            android:layout_margin="10dp"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
            <TextView
                android:layout_margin="10dp"
                android:text="个人相册"
                android:textSize="15sp"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content" />
            <ImageView
                android:layout_margin="5dp"
                android:src="@mipmap/icon"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content" />
            <ImageView
                android:src="@mipmap/icon"
                android:layout_margin="5dp"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content" />
            <ImageView
                android:src="@mipmap/icon"
                android:layout_margin="5dp"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content" />
        </LinearLayout>
        <TextView
            android:layout_margin="10dp"
            android:text="更多"
            android:textSize="15sp"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />
    </LinearLayout>
    <Button
        android:id="@+id/message_btn_send"
        android:layout_margin="20dp"
        android:padding="15dp"
        android:text="发信息"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
    <Button
        android:id="@+id/message_btn_video"
        android:layout_marginLeft="20dp"
        android:layout_marginRight="20dp"
        android:padding="15dp"
        android:text="视频聊天"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
</LinearLayout>

3建立聊天布局中发来信息显示布局ui_chat_left.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="match_parent">

    <ImageView
        android:id="@+id/left_iv_icon"
        android:layout_gravity="top"
        android:src="@mipmap/app_lvjian_message_background"
        android:layout_width="45dp"
        android:layout_height="45dp" />
    <TextView
        android:id="@+id/left_tv_content"
        android:layout_marginLeft="8dp"
        android:layout_marginTop="8dp"
        android:padding="8dp"
        android:text="ni hao !"
        android:background="@mipmap/app_lvjian_other_chat_background"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</LinearLayout>

4建立聊天布局中发送信息显示布局ui_chat_right.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:orientation="horizontal"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/right_tv_content"
        android:layout_marginRight="8dp"
        android:layout_marginTop="8dp"
        android:padding="8dp"
        android:text="ni hao !"
        android:layout_toLeftOf="@+id/right_iv_icon"
        android:background="@mipmap/app_lvjian_chat_right"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
    <ImageView
        android:layout_alignParentRight="true"
        android:id="@+id/right_iv_icon"
        android:layout_gravity="top"
        android:src="@mipmap/app_lvjian_message_background"
        android:layout_width="45dp"
        android:layout_height="45dp" />
</RelativeLayout>

5建立聊天布局ui_chat.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:orientation="vertical"
    android:layout_height="match_parent">
    <include layout="@layout/ui_top"/>
    <ListView
        android:id="@+id/lv_show"
        android:layout_weight="1"
        android:layout_width="match_parent"
        android:layout_height="match_parent"></ListView>
    <LinearLayout
        android:layout_width="match_parent"
        android:orientation="horizontal"
        android:layout_height="wrap_content">
        <EditText
            android:id="@+id/et_input"
            android:layout_weight="1"
            android:paddingLeft="10dp"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />
        <Button
            android:id="@+id/btn_send"
            android:paddingLeft="10dp"
            android:paddingRight="10dp"
            android:onClick="btnSend"
            android:text="发送"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
    </LinearLayout>
</LinearLayout>

6在bean文件夹下建立PersonBean.java.java


public class PersonBean {
    private int id;

    private int imageId;

    private String name;

    private String chatMessage;

    private boolean isMeSend;

    public int getImageId() {
        return imageId;
    }

    public void setImageId(int imageId) {
        this.imageId = imageId;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getChatMessage() {
        return chatMessage;
    }

    public void setChatMessage(String chatMessage) {
        this.chatMessage = chatMessage;
    }

    public boolean isMeSend() {
        return isMeSend;
    }

    public void setMeSend(boolean meSend) {
        isMeSend = meSend;
    }

    public PersonBean(int id , String name, String chatMessage , boolean isMeSend){
        super();
        this.id = id;
        this.name = name;
        this.chatMessage = chatMessage;
        this.isMeSend = isMeSend;
    }
    public PersonBean(){
        super();
    }
}

7在custom文件夹下建立聊天适配器ChatAdapter.java

import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;

import java.util.List;

/**
 * Created by Administrator on 2017/9/13.
 */

public class ChatAdapter extends BaseAdapter {

    private Context context;

    private List<PersonBean> list;

    public ChatAdapter(Context context, List<PersonBean> list){
        super();
        this.context = context;
        this.list = list;
    }

    public  interface IMsgViewType{
        int IMVT_COM_MSG = 0;// 收到对方的消息
        int IMVT_TO_MSG = 1;// 自己发送出去的消息
    }

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

    @Override
    public Object getItem(int i) {
        return list.get(i);
    }

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

    public int getItemViewType(int i){
        PersonBean personBean = list.get(i);
        if(personBean.isMeSend()){
            return IMsgViewType.IMVT_COM_MSG;
        }else
            return IMsgViewType.IMVT_TO_MSG;
    }

    @Override
    public View getView(int i, View view, ViewGroup viewGroup) {
        HolderView holderView = null;
        PersonBean personBean = list.get(i);
        ImageView right_iv_icon;
        ImageView left_iv_icon;
        boolean isMeSend = personBean.isMeSend();
        if(holderView == null){
            holderView = new HolderView();
            if(isMeSend){
                view = View.inflate(context, R.layout.ui_chat_right,null);
                right_iv_icon = (ImageView)view.findViewById(R.id.right_iv_icon);
                right_iv_icon.setImageResource(personBean.getImageId());
                holderView.tv_chat_me_message = (TextView)view.findViewById(R.id.right_tv_content);
                holderView.tv_chat_me_message.setText(personBean.getChatMessage());
            }else {
                view = View.inflate(context,R.layout.ui_chat_left,null);
                left_iv_icon = (ImageView)view.findViewById(R.id.left_iv_icon);
                left_iv_icon.setImageResource(personBean.getImageId());
                holderView.tv_chat_me_message = (TextView)view.findViewById(R.id.left_tv_content);
                holderView.tv_chat_me_message.setText(personBean.getChatMessage());
            }
            view.setTag(holderView);
        }else {
            holderView = (HolderView)view.getTag();
        }
        return view;
    }
    class HolderView {
        TextView tv_chat_me_message;
    }
}

8在activity中建立 UserActivity.java用来显示用户信息详情

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

/**
 * Created by Administrator on 2017/9/13.
 */

public class UserActivity extends Activity {

    Intent intent;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.ui_message);
        intent = getIntent();
        ImageView exit = (ImageView)findViewById(R.id.iv_exit);
        ImageView iv_add = (ImageView)findViewById(R.id.iv_Setting);
        iv_add.setImageResource(R.mipmap.rr);
        TextView name = (TextView)findViewById(R.id.tv_name);
        TextView location = (TextView)findViewById(R.id.tv_location);
        TextView title_name = (TextView)findViewById(R.id.user_name);
        Button send = (Button) findViewById(R.id.message_btn_send);
        ImageView user_icon = (ImageView)findViewById(R.id.iv_message_user_icon);
        title_name.setText("详细资料");
        if(intent.getStringExtra("name") != null)
            name.setText(intent.getStringExtra("name"));
        if(intent.getStringExtra("location") != null)
            location.setText("地区  " +intent.getStringExtra("location"));
        if(intent.getStringExtra("imageId") != null)
            user_icon.setImageResource(new Integer(intent.getStringExtra("imageId")));
        exit.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                finish();
            }
        });

        send.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent1 = new Intent(getApplicationContext(),ChatActivity.class);
                intent1.putExtra("name",intent.getStringExtra("name"));
                startActivity(intent1);
                finish();
            }
        });

        iv_add.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Toast.makeText(getApplicationContext(),"点我也没用",Toast.LENGTH_SHORT).show();
            }
        });

    }
}

9在activity文件夹下建立ChatActivity.java显示聊天界面

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.text.TextUtils;
import android.view.View;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

import com.example.imitatewechat.R;
import com.example.imitatewechat.bean.PersonBean;
import com.example.imitatewechat.custom.ChatAdapter;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by Administrator on 2017/9/13.
 */

public class ChatActivity extends Activity {

    private TextView name;

    PersonBean personBean ;

    private ChatAdapter chatAdapter;

    private List<PersonBean> personBeanList = new ArrayList<>();

    private ListView lv_chat_dialog;

    private EditText et_chat_message;

    private Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            int what = msg.what;
            switch (what){
                case 1:
                    lv_chat_dialog.setSelection(personBeanList.size());
                    break;
                default:
                    break;
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.ui_chat);
        Intent intent = getIntent();
        name = (TextView) findViewById(R.id.user_name);
        ImageView exit = (ImageView)findViewById(R.id.iv_exit);
        ImageView msg = (ImageView)findViewById(R.id.iv_Setting);

            personBean = new PersonBean();
            personBean.setMeSend(false);
            personBean.setImageId(R.mipmap.default_useravatar);
            personBean.setChatMessage("你好!");
            personBeanList.add(personBean);

            personBean = new PersonBean();
            personBean.setMeSend(false);
            personBean.setImageId(R.mipmap.default_useravatar);
            personBean.setChatMessage("你好吗!");
            personBeanList.add(personBean);

            personBean = new PersonBean();
            personBean.setMeSend(true);
            personBean.setImageId(R.mipmap.myicon);
            personBean.setChatMessage("不好!");
            personBeanList.add(personBean);

        lv_chat_dialog = (ListView)findViewById(R.id.lv_show);

        et_chat_message = (EditText) findViewById(R.id.et_input);

        chatAdapter = new ChatAdapter(this, personBeanList);
        lv_chat_dialog.setAdapter(chatAdapter);


        if(intent.getStringExtra("name") != null)
            name.setText(intent.getStringExtra("name"));
        msg.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Toast.makeText(getApplicationContext(),"点我也没用",Toast.LENGTH_SHORT).show();
            }
        });
        exit.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                finish();
            }
        });
    }
    public void btnSend(View view){
        if (TextUtils.isEmpty(et_chat_message.getText().toString())) {
            Toast.makeText(ChatActivity.this, "发送内容不能为空", Toast.LENGTH_SHORT).show();
            return;
        }
        PersonBean personBean = new PersonBean();
        personBean.setMeSend(true);
        personBean.setImageId(R.mipmap.myicon);
        personBean.setChatMessage(et_chat_message.getText().toString());
        personBeanList.add(personBean);
        et_chat_message.setText("");
        chatAdapter.notifyDataSetChanged();
        handler.sendEmptyMessage(1);
    }
}

10在Fragment1中调用聊天界面ChatActivity

public class Fragment1 extends Fragment {

    ...

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        view = inflater.inflate(R.layout.fragment_1,container,false);
        ListView wechat = (ListView)view.findViewById(R.id.myListView);
        wechat.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
                Intent intent = new Intent(getContext(), ChatActivity.class);
                intent.putExtra("name",objects.get(i).getTitle());
                startActivity(intent);
               // Toast.makeText(getContext(),"第" + i + "条" ,Toast.LENGTH_SHORT).show();
            }
        });

        return view;
...
}

11在Fragment2中调用用户信息界面UserActivity

public class Fragment2 extends Fragment {

    ...

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = getLayoutInflater(savedInstanceState).inflate(R.layout.fragment_2,container,false);
        titleLayout = (LinearLayout)view.findViewById(R.id.title_layout);
        titleLayout.bringToFront();
        title = (TextView)view.findViewById(R.id.title);
        mListView = (ListView)view.findViewById(R.id.contectListView);
        alphabetButton = (ImageButton)view.findViewById(R.id.az);
        sectionToastLayout = (RelativeLayout)view.findViewById(R.id.az_toast_layout);
        sectionToastText = (TextView)view.findViewById(R.id.az_toast);
        ListView connect = (ListView)view.findViewById(R.id.contectListView);
        connect.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
                if(i<=3){
                    Toast.makeText(getContext(),"我是: " + listUserBeans.get(i).getUser() , Toast.LENGTH_SHORT).show();
                }else {
                    Intent intent = new Intent(getContext(), UserActivity.class);
                    intent.putExtra("name", listUserBeans.get(i).getUser());
                    intent.putExtra("location", listUserBeans.get(i).getLocatino());
                    intent.putExtra("imageId",listUserBeans.get(i).getImageId()+"");
                    startActivity(intent);
                    //Toast.makeText(getContext(),"第" + i + "条" ,Toast.LENGTH_SHORT).show();
                }
            }
        });
        return view;
    }
    ...
    }

12更改配置AndroidManifest.xml

    ...
    <activity android:name=".activity.ChatActivity"/>
    <activity android:name=".activity.UserActivity"/>

源码下载

微信开发之JSSDK调用

阅读数 20093

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