首页 > 代码库 > Android第十八期 - 智能机器人
Android第十八期 - 智能机器人
这回比较复杂,所以就一步步的教学弟们怎么写出来一个可以和你聊天的智能机器人Android版。
首先我们找一个第三方的api接口,我选择的是图灵机器人,地址是http://www.tuling123.com/openapi/, 如图:
然后是找到api文档接入口:
然后是json的解析部分:
然后我们挑一个做一个demo - 一问一答的例子:
那么调用的方式url = http://www.tuling123.com/openapi/api?key=ad5f0729523118c422f47e4dba0cf4c6&info=从西直门到东直门怎么走
在地址栏查看返回的json如图:
这样我们就完成了准备工作,接下来敲代码:
布局文件分别是main.xml,left.xml,right.xml,首先要说明的是main.xml:
<ListView android:id="@+id/listview" android:layout_width="fill_parent" android:layout_height="0dip" 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:orientation="horizontal" > <EditText android:id="@+id/ed" android:layout_width="0dip" android:layout_height="wrap_content" android:layout_weight="1" /> <Button android:id="@+id/seach" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/send1" /> </LinearLayout>
接下来是给listview的item的xml,首先是left.xml,用户显示在左边的对话部分:
<?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="match_parent" android:orientation="vertical" > <TextView android:id="@+id/time" android:layout_width="fill_parent" android:layout_height="wrap_content" android:gravity="center_horizontal" android:paddingTop="5dip" /> <RelativeLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_below="@+id/time" android:padding="5dip" > <ImageView android:id="@+id/iv" android:layout_width="60dp" android:layout_height="60dp" android:src="http://www.mamicode.com/@drawable/right_icon" /> <TextView android:id="@+id/tv" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginRight="50dip" android:layout_marginTop="10dip" android:layout_toRightOf="@+id/iv" android:background="@drawable/left" android:gravity="center" android:textColor="#000000" android:text="测试" /> </RelativeLayout> </RelativeLayout>
效果图,如下:
然后是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:layout_height="match_parent" android:orientation="vertical" > <TextView android:id="@+id/time" android:layout_width="fill_parent" android:layout_height="wrap_content" android:gravity="center_horizontal" android:paddingTop="5dip" /> <RelativeLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_below="@+id/time" android:padding="5dip" > <ImageView android:id="@+id/iv" android:layout_width="60dp" android:layout_height="60dp" android:layout_alignParentRight="true" android:src="http://www.mamicode.com/@drawable/left_icon" /> <TextView android:id="@+id/tv" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="50dip" android:layout_marginTop="10dip" android:layout_toLeftOf="@+id/iv" android:background="@drawable/right" android:gravity="center" android:text="测试" android:textColor="#000000" /> </RelativeLayout> </RelativeLayout>
效果图如下:
然后是主窗体的效果图,如下显示:
接着就是代码部分:我给大家解析一下:
思路是这样的,你要实例化一个用于调用listviewData的方法类,用来对listview中的item进行操作和修改:
ListData.java:其中flag做为用于显示左右区别的标识,SEND为右边,RECEIVE为左边,time做为显示当前消息的时间记录
package com.xiaosan; public class ListData { private String content; public static final int SEND = 1; public static final int RECEIVE = 2; private int flag; private String time; public int getFlag() { return flag; } public void setFlag(int flag) { this.flag = flag; } 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 String getTime() { return time; } public void setTime(String time) { this.time = time; } }
接下来是给listview写一个适配器TextAdapter.java来适配listview中的item,代码如下:
package com.xiaosan; import java.util.List; import net.sourceforge.simcpux.R; import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.RelativeLayout; import android.widget.TextView; public class TextAdapter extends BaseAdapter { private List<ListData> lists; private Context context; private RelativeLayout Rlayout; public TextAdapter(List<ListData> lists, Context context) { this.lists = lists; this.context = context; } @Override public int getCount() { // TODO Auto-generated method stub return lists.size(); } @Override public Object getItem(int position) { // TODO Auto-generated method stub return lists.get(position); } @Override public long getItemId(int position) { // TODO Auto-generated method stub return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { LayoutInflater inflater = LayoutInflater.from(context); if (lists.get(position).getFlag() == ListData.RECEIVE) { Rlayout = (RelativeLayout) inflater .inflate(R.layout.leftitem, null); } if (lists.get(position).getFlag() == ListData.SEND) { Rlayout = (RelativeLayout) inflater.inflate(R.layout.rightitem, null); } TextView tv = (TextView) Rlayout.findViewById(R.id.tv); TextView time = (TextView) Rlayout.findViewById(R.id.time); time.setText(lists.get(position).getTime()); tv.setText(lists.get(position).getContent()); return Rlayout; } }
然后接着写一个监听事件的接口类,声明方法用来调用异步加载部分的传值,HttpGetDataListener.java,代码如下:
package com.xiaosan; public interface HttpGetDataListener { void getDataUrl(String data); }
接着是定义一个异步加载的方法用来加载listview中的数据,HttpData.java,代码如下:
package com.xiaosan; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.DefaultHttpClient; import android.os.AsyncTask; public class HttpData extends AsyncTask<String, Void, String> { private HttpClient nhttHttpClient; private HttpGet nHttpGet; private HttpResponse nHttpResponse; private HttpEntity nhttHttpEntity; private InputStream in; private String url; private HttpGetDataListener listener; public HttpData(String url, HttpGetDataListener listener) { this.url = url; this.listener = listener; } @Override protected String doInBackground(String... arg0) { try { nhttHttpClient = new DefaultHttpClient(); nHttpGet = new HttpGet(url); nHttpResponse = nhttHttpClient.execute(nHttpGet); nhttHttpEntity = nHttpResponse.getEntity(); in = nhttHttpEntity.getContent(); BufferedReader br = new BufferedReader(new InputStreamReader(in)); String line = null; StringBuffer sb = new StringBuffer(); while ((line = br.readLine()) != null) { sb.append(line); } return sb.toString(); } catch (ClientProtocolException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } return null; } @Override protected void onPostExecute(String result) { listener.getDataUrl(result); super.onPostExecute(result); } }
最后获得result就是json返回的String类型的内容部分。
接着我们继续看MainActivity.java是怎么写的,代码如下:
package com.xiaosan; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; import net.sourceforge.simcpux.Constants; import net.sourceforge.simcpux.R; import net.sourceforge.simcpux.uikit.MMAlert; import org.json.JSONException; import org.json.JSONObject; import android.app.Activity; import android.content.DialogInterface; import android.os.Bundle; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.CheckBox; import android.widget.EditText; import android.widget.LinearLayout; import android.widget.ListView; import android.widget.TextView; import android.widget.Toast; import com.tencent.mm.sdk.openapi.IWXAPI; import com.tencent.mm.sdk.openapi.SendMessageToWX; import com.tencent.mm.sdk.openapi.WXAPIFactory; import com.tencent.mm.sdk.openapi.WXMediaMessage; import com.tencent.mm.sdk.openapi.WXTextObject; public class MainActivity extends Activity implements HttpGetDataListener, OnClickListener { private HttpData httpData; private TextView code; private TextView text; private EditText et; private Button btn; private List<ListData> lists; private ListView lv; private String str; private TextAdapter adapter; private String[] welcome_array; private double currentTime, oldTime = 0; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); initview(); } public void initview() { et = (EditText) findViewById(R.id.ed); btn = (Button) findViewById(R.id.seach); btn.setOnClickListener(this); lv = (ListView) findViewById(R.id.listview); lists = new ArrayList<ListData>(); adapter = new TextAdapter(lists, this); lv.setAdapter(adapter); ListData listData; listData = new ListData(getRandomWelcomeTips(), ListData.RECEIVE, getTime()); lists.add(listData); } @Override public void onClick(View v) { // getTime(); if (v == btn) { str = et.getText().toString(); // 去掉空格和回车部分 String a = str.replace(" ", ""); String b = a.replace("\n", ""); et.setText(""); ListData listData; listData = new ListData(str, ListData.SEND, getTime()); lists.add(listData); // 移除多余的字部分 if (lists.size() > 30) { for (int i = 0; i < lists.size(); i++) { lists.remove(i); } } adapter.notifyDataSetChanged(); httpData = (HttpData) new HttpData( "http://www.tuling123.com/openapi/api?key=ad5f0729523118c422f47e4dba0cf4c6&info=" + b, this).execute(); } } @Override public void getDataUrl(String data) { // Toast.makeText(MainActivity.this, data, Toast.LENGTH_LONG).show(); // System.err.println(data); text(data); } public void text(String data) { try { JSONObject jb = new JSONObject(data); // code.setText(jb.getString("code")); // text.setText(jb.getString("text")); ListData listData; listData = new ListData(jb.getString("text"), ListData.RECEIVE, getTime()); lists.add(listData); adapter.notifyDataSetChanged(); } catch (JSONException e) { // TODO Auto-generated catch block e.printStackTrace(); } } // 欢迎语部分 private String getRandomWelcomeTips() { String welcome_tip = null; welcome_array = this.getResources() .getStringArray(R.array.welcome_tips); int index = (int) (Math.random() * (welcome_array.length - 1)); welcome_tip = welcome_array[index]; return welcome_tip; } // 时间部分 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 >= 500) { oldTime = currentTime; return str; } else { return ""; } } }
我们来分析一下这段代码,要先implement HttpGetDataListener, onClickListener,然后声明所需的对象:
private HttpData httpData; private TextView code; private TextView text; private EditText et; private Button btn; private List<ListData> lists; private ListView lv; private String str; private TextAdapter adapter; private String[] welcome_array; private double currentTime, oldTime = 0;
注明的是welcome_array是保存进入程序的欢迎语数组集合,double的currentTime和oldYime是记录时间变化的用以判断间隔多久将对话的item归为一个时间段内的变量。
接着看简单的两个public方法:getRandomWelcomeTips()//欢迎语部分,getTime()//时间部分返回的都是String类型。
// 欢迎语部分 private String getRandomWelcomeTips() { String welcome_tip = null; welcome_array = this.getResources() .getStringArray(R.array.welcome_tips); int index = (int) (Math.random() * (welcome_array.length - 1)); welcome_tip = welcome_array[index]; return welcome_tip; } // 时间部分 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 >= 500) { oldTime = currentTime; return str; } else { return ""; } }
需要注意的是欢迎语里面我用的是random随机给index来寻找value目录下的strings.xml里面的string-array中的item,如下代码:
<string-array name="welcome_tips"> <item>主人,奴婢梁小三在此等候您多时了(*^__^*)</item> <item>主人,奴婢梁小三为你服务( # ▽ # )</item> <item>主人,最近可好(┬_┬)</item> <item>主人,奴婢梁小三想你(ˇ?ˇ) </item> <item>欢迎回来,我亲爱的主人</item> <item>主人,你有时候很凶的〒_〒</item> <item>主人,我想抱抱你么~</item> <item>主人,中午吃什么了?</item> <item>主人,晚上要好好睡觉,别玩我了--!</item> <item>主人,梁小三特别喜欢你,听到ta的呼喊了嘛。。</item> </string-array>
这里我们也可以getResources()一个url去读取服务器上的xml,就可以实现实时的控制欢迎语的内容显示了,大家可以自己试试,我在这里就不说了。
接下来的就是请求url来异步加载listview中的数据了:
@Override public void getDataUrl(String data) { // Toast.makeText(MainActivity.this, data, Toast.LENGTH_LONG).show(); // System.err.println(data); text(data); } public void text(String data) { try { JSONObject jb = new JSONObject(data); // code.setText(jb.getString("code")); // text.setText(jb.getString("text")); ListData listData; listData = new ListData(jb.getString("text"), ListData.RECEIVE, getTime()); lists.add(listData); adapter.notifyDataSetChanged(); } catch (JSONException e) { // TODO Auto-generated catch block e.printStackTrace(); } }
注意的是这地方是触发到button事件才会有数据回传值得,也就是说在欢迎语之前是没有进行异步加载的操作的。
欢迎语的操作如下代码:
public void initview() { et = (EditText) findViewById(R.id.ed); btn = (Button) findViewById(R.id.seach); btn.setOnClickListener(this); lv = (ListView) findViewById(R.id.listview); lists = new ArrayList<ListData>(); adapter = new TextAdapter(lists, this); lv.setAdapter(adapter); ListData listData; listData = new ListData(getRandomWelcomeTips(), ListData.RECEIVE, getTime()); lists.add(listData); }
注意的是里面的ListData的实例化把三个参数都传值进去后,加载到lists这个ArrayList的数据组存放,再通过真正的listview去设置适配器然后显示到listview中。
最后是button事件的解析:
str = et.getText().toString(); // 去掉空格和回车部分 String a = str.replace(" ", ""); String b = a.replace("\n", ""); et.setText(""); ListData listData; listData = new ListData(str, ListData.SEND, getTime()); lists.add(listData); // 移除多余的字部分 if (lists.size() > 30) { for (int i = 0; i < lists.size(); i++) { lists.remove(i); } } adapter.notifyDataSetChanged(); httpData = (HttpData) new HttpData( "http://www.tuling123.com/openapi/api?key=ad5f0729523118c422f47e4dba0cf4c6&info="+ b, this).execute();
注意的是这里面有一个edittext你输入后把String传入到str中药做一个过滤,因为api中是不认字符夹杂""和"\n"换行的,过滤后的String的b就是想要的,我们把它传入ListData中的方法中,并加入到arraylist中,显示出来,并且设置超过多少条数据后自动去掉的方法,然后notifyDataSetChanged这个TextAdapter,最后异步加载请求HttpData,得到数据,显示出来,至此完成整个操作。
不会的可以问我,应该能看的懂吧--!最后的效果图和源码如下:
你也可以去360手机助手去下载 - 梁小三 ,谢谢大家!国庆节快乐!
喜欢写代码,写一辈子都幸福,不喜欢写代码,写一行都悲哀。
本文出自 “梁肖技术中心” 博客,请务必保留此出处http://liangxiao.blog.51cto.com/3626612/1559778
Android第十八期 - 智能机器人