借助API,可快速实现在安卓手机上的与智能机器人聊天的功能。大体的步骤有:1、异步请求数据;2、解析json数据;3、封装数据;4、实现布局;
源代码下载地址:http://download.csdn.net/detail/leyezhiqiu/9471571
实现效果如下图:
下面一一介绍。
1、在浏览器上获取与机器人对话的数据。
1)打开图灵机器人官网,注册账号www.tuling123.com/openapi 。
2)登录账号,记录下官方分配给账号的key。
3)在账号中查看获得数据的格式,如get的请求方式格式:http://www.tuling123.com/openapi/api?key=XXX&info=XXXXX&userid=XXXXXXX
4)在浏览器上测试能否通过get方式获得对应数据。
2、在Android工程中获取数据。
1)编写异步通信类HttpData.java:发送请求,获取数据,读取数据。
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import android.os.AsyncTask;
/**
* 异步通信类
*
* @author mingyue
*
*/
public class HttpData extends AsyncTask<String, Void, String> {
private HttpClient httpClient;
private HttpGet httpGet;
private HttpResponse httpResponse; // 获取请求返回的数据
private HttpEntity httpEntity;// 创建http实体
private InputStream in; // 将获取到的数据转化为流文件
private HttpGetDataListener listener;// 实现自定义的HttpGetDataListener接口,并且构造化传递参数
private String url;
public HttpData(String url, HttpGetDataListener listener) {
this.url = url;
this.listener = listener;
}
@Override
protected String doInBackground(String... params) {// 实现接口后重写此方法,此方法的作用是:发送get请求后,获取数据
try {
httpClient = new DefaultHttpClient();// 实例化客户端
httpGet = new HttpGet(url);// 使用get方式,通过发送URL来请求
httpResponse = httpClient.execute(httpGet); // 通过客户端发送请求
httpEntity = httpResponse.getEntity();// 通过httpResponse对象获取数据
in = httpEntity.getContent();// 获取实体的具体内容
BufferedReader br = new BufferedReader(new InputStreamReader(in));// 获取到具体内容后,通过缓冲区进行读取
String line = null; // 读取数据
StringBuffer sb = new StringBuffer();// 储存所有数据
while ((line = br.readLine()) != null) { // 读取缓冲区的数据
sb.append(line); // 存储数据到StringBuffer中
}
return sb.toString();// 转换为String类型
} catch (Exception e) {
}
return null;
}
/**
* 重写此方法,通过这方法获取数据
*/
@Override
protected void onPostExecute(String result) {
listener.getDataUrl(result);// 返回数据
super.onPostExecute(result);
}
}
2)由于要将获取到的数据传递给其它页面,所有这里新建一个接口,采用回调的方式。
//HttpGetDataListener.java
public interface HttpGetDataListener {
void getDataUrl(String data);
}
3)在MainActivity中实现HttpGetDataListener接口,测试能否获得数据。
具体代码如下:
public class MainActivity extends Activity implements HttpGetDataListener{
private HttpData httpData;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
httpData = (HttpData) new HttpData(
"http://www.tuling123.com/openapi/api?key=XXXXX&info=北京",
this).execute();//execute()启动异步通信,注意:XXXXX应该是在步骤1获取的key值
}
/**
*实现抽象方法
*/
@Override
public void getDataUrl(String data) {
System.out.println(data);
}
}
4)运行代码,在logcat中能看到相关的json数据,则说明数据获取成功。
3、解析json数据。
在MainActivity.java中编写parseText()方法,解析数据,在getDataUrl()方法中调用parseText()方法。
代码如下:
@Override
public void getDataUrl(String data) {
System.out.println(data);
parseText(data);
}
public void parseText(String str){
try {
JSONObject jb = new JSONObject(str);
System.out.println(jb.getString("code"));
System.out.println(jb.getString("text"));
} catch (JSONException e) {
e.printStackTrace();
}
}
再次运行代码,可看到运行结果如下:
4、封装数据
1)创建ListData.java,用于封装数据,如机器人的对话、时间等,现只是初步封装时间。代码如下:
public class ListData {
private String content;
public ListData(String content) {
this.content = content;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
2)修改MainActivity.java,让解析出的json数据封装好(具体步骤:新建实例化方法initiView() 方法,实例化List lists;在parseText()方法中实例化ListData listData,将解析出的json数据封装到listData,再将listData添加到lists)。完成后,MainActivity.java的代码如下所示。
public class MainActivity extends Activity implements HttpGetDataListener {
private HttpData httpData;
private List<ListData> lists;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
httpData = (HttpData) new HttpData(
"http://www.tuling123.com/openapi/api?key=XXXXX&info=北京",
this).execute();//execute()启动异步通信,注意:XXXXX应该是在步骤1获取的key值
}
/**
* 实例化方法
*/
public void initView() {
lists = new ArrayList<ListData>();
}
/**
* 实现HttpGetDataListener接口的抽象方法,获取到HttpData得到的数据
*/
@Override
public void getDataUrl(String data) {
System.out.println(data);
parseText(data);
}
/**
* 解析json、封装数据
*
* @param data
*/
public void parseText(String data) {
System.out.println("data======" + data);
try {
JSONObject jb = new JSONObject(data);
ListData listData;
listData = new ListData(jb.getString("text"));// 封装数据
lists.add(listData);
} catch (JSONException e) {
e.printStackTrace();
}
}
}
5、实现布局。
1)在与MainActivity对应的layout中添加控件:一个ListView(作用:显示对话)、一个EditText(作用:对话输入框)、一个Button(作用:发送按钮)。
完整具体代码如下。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/LinearLayout1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<!-- transcriptMode 自动向下滚动 alwaysScroll一直向下滚动状态; divider设置间隔线效果 ; listSelector设置没有滑动效果 -->
<ListView
android:id="@+id/lv"
android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:divider="@null"
android:listSelector="@android:color/transparent"
android:transcriptMode="alwaysScroll" >
</ListView>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="#229dec"
android:orientation="horizontal" >
<EditText
android:id="@+id/et_sendText"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginLeft="7dp"
android:layout_weight="1"
android:background="@drawable/bg_edittext_selector"
android:paddingBottom="7dp"
android:paddingTop="7dp" />
<Button
android:id="@+id/btn_send"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="7dp"
android:background="@drawable/bg_button"
android:text="@string/send" />
</LinearLayout>
</LinearLayout>
2)创建与ListView想对应的适配器
(1)要先在ListData中封装一标识flag,用于表示适配器加载哪一个view。因为在聊天对话ListView中,每一个item的中的控件摆放位置是不一样的。同时在ListData中添加String time,表示对话时间数据。ListData完整代码如下所示。
/**
* 把获取到的数据封装起来
*
* @author mingyue
*
*/
public class ListData {
public static final int SEND = 1;
public static final int RECEIVE = 2;
private String content;
private int flag;// 标识
private String time;
public ListData(String content, int flag, String time) {
setContent(content);
setFlag(flag);
setTime(time);
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public int getFlag() {
return flag;
}
public void setFlag(int flag) {
this.flag = flag;
}
public String getTime() {
return time;
}
public void setTime(String time) {
this.time = time;
}
}
(2)创建名为leftitem.xml的layout,在ListView中对应机器人item:一个TextView(作用:显示时间)、ImageView(作用:显示头像)和TextView(作用:显示对话内容)。代码如下:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<TextView
android:id="@+id/tv_time"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:padding="3dp" />
<ImageView
android:id="@+id/iv"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_alignParentLeft="true"
android:layout_below="@id/tv_time"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:src="@drawable/person2" />
<TextView
android:id="@+id/tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/tv_time"
android:layout_marginRight="10dp"
android:layout_marginTop="5dp"
android:layout_toRightOf="@id/iv"
android:background="@drawable/left"
android:gravity="center"
android:padding="10dp" />
</RelativeLayout>
(3)创建名为rightitem.xml的layout,在ListView中对应用户item:一个TextView(作用:显示时间)、ImageView(作用:显示头像)和TextView(作用:显示对话内容)。
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<TextView
android:id="@+id/tv_time"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:padding="3dp" />
<ImageView
android:id="@+id/iv"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_alignParentRight="true"
android:layout_below="@id/tv_time"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:src="@drawable/person3" />
<TextView
android:id="@+id/tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/tv_time"
android:layout_marginLeft="10dp"
android:layout_marginTop="5dp"
android:layout_toLeftOf="@id/iv"
android:background="@drawable/right"
android:gravity="center"
android:padding="12dp" />
</RelativeLayout>
(4)适配器完整代码。
//TextAdapter.java
public class TextAdapter extends BaseAdapter {// /继承BaseAdapter,实现方法
private List<ListData> lists;// 集合的数据内容
private Context mContext;// 承接上下文的Context
private RelativeLayout layout;
public TextAdapter(List<ListData> lists, Context mContext) {
this.lists = lists;
this.mContext = mContext;
}
@Override
public int getCount() {// 返回lists所承载的条数
return lists.size();
}
@Override
public Object getItem(int position) {
return lists.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {// convertView表示要加载的view,在本例子中有两个view要加载的,一是机器人说话,二是用户说话,分别对应leftitem和rightitem
LayoutInflater inflater = LayoutInflater.from(mContext);
if (lists.get(position).getFlag() == ListData.RECEIVE) {
layout = (RelativeLayout) inflater.inflate(R.layout.leftitem, null);
}
if (lists.get(position).getFlag() == ListData.SEND) {
layout = (RelativeLayout) inflater
.inflate(R.layout.rightitem, null);
}
TextView tv = (TextView) layout.findViewById(R.id.tv);// 对话textView
TextView tv_time = (TextView) layout.findViewById(R.id.tv_time);// 时间textView
tv.setText(lists.get(position).getContent()); // 将数据内容放进TextView中
tv_time.setText(lists.get(position).getTime());// 将事件写进TextView
return layout;
}
}
3)修改MainActivity.java:设置发送Button的监听事件、将ListView与adapter绑定。代码入下。
public class MainActivity extends Activity implements HttpGetDataListener,
OnClickListener {
private HttpData httpData;
private List<ListData> lists;
private ListView lv;
private EditText et_sendText;
private Button btn_send;
private TextAdapter adapter;
private double currentTime, oldTime = 0;// 对话时间
private String content_str; // 存储在EditText获取到的数据
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
/**
* 实例化方法
*/
public void initView() {
lists = new ArrayList<ListData>();
lv = (ListView) findViewById(R.id.lv);
et_sendText = (EditText) findViewById(R.id.et_sendText);
btn_send = (Button) findViewById(R.id.btn_send);
btn_send.setOnClickListener(this);
adapter = new TextAdapter(lists, this);
lv.setAdapter(adapter);// 绑定adapter
}
/**
* 设置时间
*
* @return
*/
private String getTime() {
currentTime = System.currentTimeMillis();
SimpleDateFormat format = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
Date curDate = new Date();
String str = format.format(curDate);
if (currentTime - oldTime >= 5 * 60 * 1000) {// 如果超过5分钟,显示时间
oldTime = currentTime;
return str;
} else {
return "";
}
}
/**
* 实现HttpGetDataListener接口的抽象方法,获取到HttpData得到的数据
*/
@Override
public void getDataUrl(String data) {
System.out.println(data);
parseText(data);
}
/**
* 解析json、封装数据
*
* @param data
*/
public void parseText(String data) {
System.out.println("data======" + data);
try {
JSONObject jb = new JSONObject(data);
ListData listData;
listData = new ListData(jb.getString("text"), ListData.RECEIVE,
getTime());// 封装数据
lists.add(listData);
adapter.notifyDataSetChanged();// 重新適配
} catch (JSONException e) {
e.printStackTrace();
}
}
@Override
public void onClick(View v) {
content_str = et_sendText.getText().toString();// 获取EditText內容
et_sendText.setText("");
String dropk = content_str.replace(" ", "");// 去掉空格
String droph = dropk.replace("\n", "");// 去掉回车
ListData listData;
listData = new ListData(content_str, ListData.SEND, getTime());
lists.add(listData);
adapter.notifyDataSetChanged();// 刷新
httpData = (HttpData) new HttpData(
"http://www.tuling123.com/openapi/api?key=XXXXX&info=北京",
this).execute();// 发送已经去掉空格和回车content_str的数据droph
// ;execute()启动异步通信
}
}
4)点9图片处理,作用是处理聊天的对话框,让对话框背景图随对话内容适当拉升,使得界面更美观大方,本例子用模仿的是QQ气泡。点9图片的处理工具:打开sdk/tools/draw9patch.bat,就可以选择图片来处理了。
5)添加欢迎语。
(1)在string.xml中添加string-array。代码如下。
<!-- 添加欢迎语 -->
<string-array name="welcome_tips">
<item>主人,您好!O(∩_∩)O</item>
<item>亲,你回来啦~</item>
<item>Welcome!!</item>
<item>呵呵,你来啦~</item>
<item>Hi, long time no see !</item>
</string-array>
(2)在MainActivity.java中添加欢迎语方法。
public String getRandomWelcomeTips() {
String welcome_tip = null;
welcome_arry = this.getResources().getStringArray(R.array.welcome_tips);// 从string.xml中获取名为welcome_tips的字符串数组
int index = (int) (Math.random() * (welcome_arry.length - 1));// 获取一个随机数
welcome_tip = welcome_arry[index];
return welcome_tip;
}
6)移除界面过多的信息,比如对话信息超过30条,就只显示最近的30条对话。在MainActivity.java中添加相关代码,如下。
if (lists.size() > 30) {// 如果屏幕上的对话数据多于30,则移除前面的数据
for (int i = 0; i < lists.size(); i++) {
lists.remove(i);
}
}