Android--智能機器人聊天


借助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數據,則說明數據獲取成功。
圖1

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();
}

}

再次運行代碼,可看到運行結果如下:
圖2

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);
}
}

6、運行、調試、完善、重構、總結。


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com