原创代码慕课网,讲师讲的太快,不方便理解,做个代码分析,方便以后阅读!如图页面
1.项目要求
要求接入图灵官网的API,做一个能机器 现类似微信的聊天页面
2.项目核心
访问网络, JSON解析,枚举实现双视图适配器的原理
3.项目结> 基本概括:5个类, 两个getset类,一个核心类访问网络,一个Activity处理页面 还有一个测试类如图:
"MyFahui.java" 是存放访问网址后返回值的getset类 编码+Json
"Myjiexi.java" 是对Jons解析后的值后的getset类, 消息+时间+枚举
"HttpUtil.Java" 是 发送接受消息的核心类,传入发送消息,得到Json数据,被解析到MyFahui.java中,然后最后交给了Myjiexi,
(作为一个一体成型的工具类,只要传入String,就能返回出一个完整getset类)
”MyActivity.Java“ 是一个Activity,负责显示页面,双套布局的适配器,线程更新适配器等
HttpText.java"是一个测试类,主要负责测试工作,在主页面没有绘制前,对"HttpUtil.java"进行测试工作
1.一旦消息传工具类,自动拼接成一个网址,并访问网址得到Json格式的数据,被解析成到一个getset类中封装好==================================================================================
import java.io.ByteArrayOutputStream; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URLEncoder; import java.util.Date; <img src="http://img.blog.csdn.net/20151018231250086?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="" /> import com.google.gson.Gson; import com.info.MyFanhui; import com.info.Myjiexi; import com.info.Myjiexi.Type; public class HttpUtil { private static final String URL="http://www.tuling123.com/openapi/api"; private static final String KEY="742330b59b0cdea8a537db941bfa087f"; /*【核心】: * 发送一个消息,被解析成一个getset类----------------------------------------- * */ public static Myjiexi jiexi(String stt){ Myjiexi jiexi=new Myjiexi(); MyFanhui fh=null; String xiaoxi=doGet(stt); //传入String,得到Json返回 Gson gson=new Gson(); try { fh=gson.fromJson(xiaoxi,MyFanhui.class); jiexi.setMsg(fh.getText()); //设置返回的内容 } catch (Exception e) { jiexi.setMsg("服务器繁忙"); } jiexi.setDate(new Date()); //设置当前时间 jiexi.setType(Type.WO); //设置枚举类型 return jiexi ; } /*核心方法,------------------------------------------------- 传入String字符串,自动拼接好地址,Url 传入访问网络地址,自动返回字符串,Json */ public static String doGet(String str){ String str2=Pingjie(str); //【此方法拼接出完整地址】 String str3= Fangwen(str2); //【此方法连接网址】 return str3; } //【访问连接网址】 private static String Fangwen(String str2) { InputStream is = null; ByteArrayOutputStream bs = null; String str3=""; try { java.net.URL url= new java.net.URL(str2); HttpURLConnection conn=(HttpURLConnection) url.openConnection(); conn.setReadTimeout(5*1000); conn.setConnectTimeout(5*1000); conn.setRequestMethod("GET"); is=conn.getInputStream(); //输入流 int len=-1; byte[] by=new byte[128]; bs=new ByteArrayOutputStream();//输出流 while((len=is.read(by))!=-1){ bs.write(by, 0, len); } bs.flush(); //清除缓冲区 str3=new String(bs.toByteArray()); //*输出流String } catch (Exception e) { e.printStackTrace(); }finally{ //释放资源 try { if(is!=null){ is.close(); if(bs!=null){ bs.close(); } } } catch (Exception e2) { e2.printStackTrace();} } return str3; } /*【拼接完整地址】*/ public static String Pingjie(String str){ String str2=""; try { str2=URL+"?key="+KEY+"&info="+URLEncoder.encode(str, "UTF-8"); } catch (Exception e) {e.printStackTrace();} return str2; } }
2.下面简要看看两个getset类的基本数据类型有什么===========================================================================================================
注意:解析类中包含两个构造方法,一个有参,一个无参,方便我们以后调用
1)MyFanhui (返回类)
int类型的编码,
String类型的字符串
package com.info; public class MyFanhui { private int code; private String text; public int getCode() { return code; } public void setCode(int code) { this.code = code; } public String getText() { return text; } public void setText(String text) { this.text = text; } }
2)Myjiexi(解析类)------------------------------------------------------------------
字符串消息String
枚举类型Type
时间数据Data
package com.info; import java.util.Date; public class Myjiexi { private String msg; private Type type; private Date date; public Myjiexi() { } public Myjiexi( String msg, Type type, Date date) { this.msg = msg; this.type = type; this.date = date; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } public Type getType() { return type; }
图灵的规定是,按要求拼接好你的地址,连接网址,得到返回数据
我们写的工具类就完全具备了这个功能了,只是没有页面,下面要进行一个代码的测试工作,
测试配置清单一览
三个重要的部分,联网权限,测试的应用库,然后就是单元测试,配置好了,就可以进入测试环境了
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.xiaomu" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="17" /> <!-- 【网络权限】 --> <uses-permission android:name="android.permission.INTERNET"/> <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <!-- 【应用库】 --> <uses-library android:name="android.test.runner"/> <activity android:name="com.example.xiaomu.MyActvity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> <!-- 【单元测试】 --> <instrumentation android:targetPackage="com.example.xiaomu" android:label="This is test" android:name="android.test.InstrumentationTestRunner"></instrumentation> </manifest>
代码也是极其简单,继承就AndroidTestCase就可以了
package com.text; import com.util.HttpUtil; import android.test.AndroidTestCase; /*单元测通过*/ public class Httptext extends AndroidTestCase{ public void testSendInfo(){ String str= HttpUtil.doGet("你好"); System.out.println("---"+str); } }测试方法比较特殊,代码中点击这个类名,直接运行,
如果看到了日志中的返回中文消息就说明测试成功,我们才能继续下一步的页面编写工作了
这是返回的数据应该还是json数据,注意几个问题,测试需要开模拟器,并关闭工具类的jiexi方法,
6.主页面,如果测试完毕了那么久可以做页面了,
首先了解下页面结构,后期贴上xml代码,
三段式,头部,中间,底部,
头部标题,中间listview,底部输入框
java代码的工作是,
主要是适配器的工作,默认要启动适配器,主人发送消息要激活一次适配,机器回复一次开一个适配器(线程)
至于双套视图,可以参考这个适配器中,除了默认的四个方法外,还有两个关于枚举的方法,枚举的返回值 决定了,我们要用那套视图,而且并用了ViewHoder的一个类,
实际是作为缓存类,目的是让适配器效率更加的快速,
判断是否为空,是为了保存缓存类数据,方便后面的加载,等等
其次可以看到代码集中在点击事件上,点击事件中适配器要动两次,主人发送,机器返回,
都需要适配器更新,而机器人返回数据是返回到getset中,传输整个getset类用线程启动再更新实现的效果
package com.example.xiaomu; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; import com.info.Myjiexi; import com.info.Myjiexi.Type; import com.util.HttpUtil; import android.app.Activity; import android.content.Context; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.text.TextUtils; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.view.Window; import android.widget.BaseAdapter; import android.widget.Button; import android.widget.EditText; import android.widget.ListView; import android.widget.TextView; import android.widget.Toast; public class MyActvity extends Activity implements OnClickListener{ private EditText w_edit; private Button w_but; private ListView w_listview; //列表UI private List<Myjiexi> list; //数据集合 private MyAdapter adapter; //适配器 //【3】子线程更新主线程,拿到消息,重启适配器 private Handler handler=new Handler(){ public void handleMessage(android.os.Message msg) { Myjiexi myjiexi=(Myjiexi) msg.obj; list.add(myjiexi); adapter.notifyDataSetChanged(); }; }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.activity_main); initView(); initData(); } /*【机器人第一句话 方法】 (测试) * */ private void initData() { list=new ArrayList<Myjiexi>(); list.add(new Myjiexi("你好2", Type.Ni, new Date())); //list.add(new Myjiexi("你好1", Type.WO, new Date())); adapter=new MyAdapter(this, list); w_listview.setAdapter(adapter); } /*【布局的初始化 方法】 * */ private void initView() { w_edit=(EditText) findViewById(R.id.edit1); w_but=(Button) findViewById(R.id.but1); w_listview=(ListView) findViewById(R.id.listview1); w_but.setOnClickListener(this); } /*【双套布局适配器】---------------------- * 目的:双套布局的使用 * 适配器中加入了枚举的两个方法, * 一个是枚举数量,一个是枚举的返回值, * 根据返回值,设置双套布局,并设置了缓存类方便加速 */ class MyAdapter extends BaseAdapter{ private Context con; private List<Myjiexi> list; public MyAdapter(Context con,List<Myjiexi> list) { this.con=con; this.list=list; } public int getItemViewType(int h) { if(list.get(h).getType()==Type.Ni){ return 0; }return 1; } public int getViewTypeCount() { return 2; } public int getCount() { return list.size(); } public Object getItem(int h) { return list.get(h); } public long getItemId(int h) { return h; } public View getView(int h, View v, ViewGroup v2) { Myjiexi myjiexi=list.get(h); ViewHolder viewHolder=null; if(v==null){ if(getItemViewType(h)==0){ v=View.inflate(con, R.layout.litem_2, null); viewHolder=new ViewHolder(); viewHolder.w_msg=(TextView) v.findViewById(R.id.Wtext); viewHolder.w_time=(TextView) v.findViewById(R.id.Wtime); }else{ v=View.inflate(con, R.layout.litem_1, null); viewHolder=new ViewHolder(); viewHolder.w_msg=(TextView) v.findViewById(R.id.Ntext); viewHolder.w_time=(TextView) v.findViewById(R.id.Ntime); } v.setTag(viewHolder); }else{ viewHolder=(ViewHolder) v.getTag(); } SimpleDateFormat sf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); viewHolder.w_time.setText(sf.format(myjiexi.getDate())); viewHolder.w_msg.setText(myjiexi.getMsg()); return v; } } /*缓存类*/ private final class ViewHolder{ TextView w_time; TextView w_msg; } /*【点击事件】--------------------------------------------------------------------------------- * 目的: 发送消息,更新页面 * 消息判断,清空输入框,线程访问网络,返回值交给主线程更新页面 * */ public void onClick(View v) { switch (v.getId()) { case R.id.but1: final String str=w_edit.getText().toString(); if(TextUtils.isEmpty(str)){ Toast.makeText(this, "消息不能为空", 1).show(); } //【1】更新发送 Myjiexi myjiexi=new Myjiexi(); myjiexi.setDate(new Date()); myjiexi.setMsg(str); myjiexi.setType(Type.Ni); list.add(myjiexi); adapter.notifyDataSetChanged(); w_edit.setText(""); // 【2】处理回收 new Thread(){ public void run() { Myjiexi myjiexi=HttpUtil.jiexi(str); Message m=Message.obtain(); m.obj=myjiexi; handler.sendMessage(m); }}.start(); break; } } }
后面就是页面了
1)主页面
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" > <!-- 【头部布局】 --> <RelativeLayout android:id="@+id/toubu" android:layout_width="fill_parent" android:layout_height="45dp" android:layout_alignParentTop="true" android:background="@drawable/title_bar" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:text="小欣" android:textColor="#ffffff" android:textSize="22sp" /> </RelativeLayout> <!-- 【底部布局】 --> <RelativeLayout android:id="@+id/dibu" android:layout_width="fill_parent" android:layout_height="55dp" android:layout_alignParentBottom="true" android:background="@drawable/bottom_bar" > <Button android:id="@+id/but1" android:layout_width="60dp" android:layout_height="40dp" android:layout_alignParentRight="true" android:layout_centerVertical="true" android:background="@drawable/but_by" android:text="发送" /> <EditText android:id="@+id/edit1" android:layout_width="fill_parent" android:layout_height="40dp" android:layout_centerVertical="true" android:layout_marginLeft="10dp" android:layout_marginRight="10dp" android:layout_toLeftOf="@id/but1" android:background="@drawable/login_edit_normal" android:textSize="18sp" /> </RelativeLayout> <!-- 聊天列表 --> <ListView android:id="@+id/listview1" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_above="@id/dibu" android:layout_below="@id/toubu" android:divider="@null" android:dividerHeight="5dp" > </ListView> </RelativeLayout>
2)发送
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <TextView android:id="@+id/Wtime" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="显示时间" android:textSize="12sp" android:background="#bebebe" android:textColor="#f5f5f5" android:layout_gravity="center"/> <!-- 线性布局(横向) 包含: 消息+ 线性布局(头像+昵称) --> <LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:gravity="right"> <TextView android:id="@+id/Wtext" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="你好" android:layout_gravity="center_vertical" android:background="@drawable/chatto_bg_focused" /> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="vertical"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text=" 主人"/> </LinearLayout> </LinearLayout> </LinearLayout>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <TextView android:id="@+id/Wtime" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="显示时间" android:textSize="12sp" android:background="#bebebe" android:textColor="#f5f5f5" android:layout_gravity="center"/> <!-- 线性布局(横向) 包含: 消息+ 线性布局(头像+昵称) --> <LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:gravity="right"> <TextView android:id="@+id/Wtext" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="你好" android:layout_gravity="center_vertical" android:background="@drawable/chatto_bg_focused" /> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="vertical"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text=" 主人"/> </LinearLayout> </LinearLayout> </LinearLayout>