[置顶] 图灵机器人————在线一对一人机聊天


1.难点

  ListView中两个Item的切换
2.实现要点
  1.联网获取Json数据:HttpUrlConnection和HttpClient
  2.解析Json数据(本项目采用Gson解析)
  3.开启异步任务用于处理以上两个耗时操作
  4.自定义适配器(工作中常用的适配器) 实现BaseAdapter接口
  5.在接口中依据枚举标志处理机器人和我的会话操作
3.遇到的问题及解决措施
  1.聊天布局在调出输入法时不会改变,则在清单文件Androidmanifest中对应的Activity标签下标注:
    android:windowsSoftInputMode = "adjustPan";
  2.ListView显示数据,实现每次更新后始终定位到最后一条Item,则:
    在ListView布局的xml文件中添加属性:android:stackFromBottom = "true;
  3.编码问题:API要求编码必须为UTF-8,所以在从EditText控件中获取数据的要进行
    URLEncoder.encode(String数据,"UTF-8");
    否则便会因为编码不一致问题导致聊天数据加载不正确。
  4.注意数据更新,通知适配器更新数据 adapter.notifyDataSetChanged()方法通知适配器数据已更新
4.具体实现
  1.设计布局
    共需三个布局:主页面(包括标题,ListView显示框,文本输入框和发送按钮)
                 机器人会话页面(包括时间,头像,名字和聊天文本数据。下同)
用户绘画页面
  2.设计获取jSON数据的HttpUrlConnection类和HttpClient类
    1.HttpUrlConnection类实现步骤
      1.新建Url对象,参数是调用处创来的服务器资源路径url
      2.建立一个网络连接,使用url.openConnection()方法实现
      3.设置连接属性,常用的是:
        connecton.setConnectTimeout(500);//设置连接超时
connection.setDoInput(true);//设置Url可输入
connection.setDoOutput(true);//设置Url可输出
connection.setRequestMethod("get");//设置请求方式
connection.setDafaultUseCaches(false);//忽略缓存
conn.setInstanceFollowRedirects(false);// 设置HttpURLConnection自动重定向
        不设置取默认即可
      4.获取状态响应码:int status = conn.getResponseCode(); 若为200或HttpUrlConnection.HTTP_OK即为响应成功
        状态响应码:
  1XX:临时响应,请求已接受继续处理
  2XX: 响应成功
  3XX: 重定向,要完成请求必须进行更进一步的操作
  4XX: 请求有语法错误或者请求资源不存在
  5XX: 服务器错误
       200:访问服务器成功并返回网页
       404: 请求的网页不存在
503: 服务不可用
  重定向:通过各种方法将网络请求重新定个方向转到其他位置(网页重定向,域名重定向。。。)
      5.依据状态响应码操作流
        connection的getInputStream()方法

      6.断开连接  


      HttpClient实现


    3.解析json数据,采用Gson解析
      新建一个数据类,成员变量时json数据的属性名
      新建一个数据封转类,成员变量是要在Item显示的数据项
    4.自定义一个适配器,继承BaseAdapter接口,重写其中的四个方法:
      getItemId()  getCount() getItem()  getView()
      主要在getView中写逻辑代码
      
      注意:此处涉及到一个ListView优化问题
      1.ListView布局时高度要匹配父类
      2.创建视图时,首先判断converView是否为空,为空则要创建新的视图
        convertView = LayoutInflater.from(context).inflate(页面布局文件,权限);
        不为空则复用convertView
      3.与控件Id的绑定
        写一个ViewHolder视图保持类,成员是布局控件的变量,在布局中与布局id绑定
第一次就绑定,之后则不用绑定直接复用。
    5.在主Activity中,操作联网获取Json,解析Json,适配器与控件绑定等操作
      因为联网获取json,解析json以及调用适配器等都是耗时操作,要将其放在子线程中运行。
      故写一个异步任务来操作:
      AsyncTask(参数1,参数2,参数3)
      参数1:执行异步操作时要传递的参数
      参数2: 后台任务执行时返回进度值的类型
      参数3:任务完成后返回结果的数据类型


      四个方法:
        onPerExecute:        在后台操作前被调用,主要是用于一些ui组件的初始化。
        doInBackground:      必须重写,异步任务要执行的操作都在这个方法中执行。(执行耗时操作)
onPostExecute:       在doInBackground方法执行完后,自动调用此方法。同时doInBackground方法返回的数据会作为此方法的参数。

onProgressUpdate:    需要在 doInBackg



主要逻辑代码

MyAdapter

package com.example.myadapter;


import java.util.List;


import android.R.integer;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
import com.example.chatutils.ChatMessage;
import com.example.chatutils.ChatMessage.type;
import com.example.mylistviewdemo2.R;


public class MyAdapter extends BaseAdapter {
private Context context;
private List<ChatMessage> list;


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


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


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


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


@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder vh;
if (getItemViewType(position) == 0) {// 我发消息
if(convertView == null){
vh = new ViewHolder();
convertView = LayoutInflater.from(context).inflate(R.layout.activity_item_right, null);
vh.tv_time = (TextView) convertView.findViewById(R.id.tv_time2);
vh.tv_message = (TextView) convertView.findViewById(R.id.message2);
convertView.setTag(vh);
}else{
vh = (ViewHolder) convertView.getTag();
}
vh.tv_time.setText(list.get(position).getTime());
vh.tv_message.setText(list.get(position).getMessage());
}
if (getItemViewType(position) == 1) {// 我收消息
if(convertView == null){
vh = new ViewHolder();
convertView = LayoutInflater.from(context).inflate(R.layout.activity_item_left, null);
vh.tv_time = (TextView) convertView.findViewById(R.id.tv_time1);
vh.tv_message = (TextView) convertView.findViewById(R.id.message1);
}else{
vh = (ViewHolder) convertView.getTag();
}
vh.tv_time.setText(list.get(position).getTime());
vh.tv_message.setText(list.get(position).getMessage());
}
return convertView;
}

class ViewHolder{
TextView tv_time;
TextView tv_message; 
}
@Override
public int getViewTypeCount() {
return 2;
}


@Override
public int getItemViewType(int position) {
if (list.get(position).getType().equals(type.OUTCOMING.toString())) {
return 0;
} else {
return 1;
}
}
}

MainActivity

package com.example.mylistviewdemo2;


import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import com.example.chatutils.Chat;
import com.example.chatutils.ChatMessage;
import com.example.chatutils.ChatMessage.type;
import com.example.myadapter.MyAdapter;
import com.example.utils.Utrls;
import com.google.gson.Gson;
import android.os.AsyncTask;
import android.os.Bundle;
import android.app.Activity;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;


public class MainActivity extends Activity {


private ListView listView;
private Button sendButton;
private EditText editText;
private String url = "http://www.tuling123.com/openapi/api?key=5c0cb5d8206e4e328f833ebe56d9aa82&info=";
private List<ChatMessage> list = new ArrayList<ChatMessage>();// 获取得到的数据源
private com.example.myadapter.MyAdapter adapter;
private SimpleDateFormat form = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");


@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);


listView = (ListView) findViewById(R.id.listview);
sendButton = (Button) findViewById(R.id.send);
editText = (EditText) findViewById(R.id.edit);
sendButton.setOnClickListener(new OnClickListener() {


@Override
public void onClick(View v) {
ChatMessage chat = new ChatMessage();
String message1 = editText.getText().toString();
chat.setMessage(message1);
chat.setName("我");
chat.setTime(form.format(new Date()));
chat.setType(type.OUTCOMING.toString());
list.add(chat);


adapter = new com.example.myadapter.MyAdapter(
MainActivity.this, list);// 发送消息
listView.setAdapter(adapter);
adapter.notifyDataSetChanged();// 提醒数据更新


try {
new MyTask().execute(url
+ URLEncoder.encode(message1, "UTF-8"));
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}


editText.setText("");
}
});


initData();
}


private void initData() {// 刚进入时显示问候语
ChatMessage chat = new ChatMessage();
chat.setMessage("您好,我是蛋蛋,很高兴为您服务!");
chat.setTime(form.format(new Date()));
chat.setType(type.INCOMING.toString());
list.add(chat);


adapter = new MyAdapter(MainActivity.this, list);
listView.setAdapter(adapter);
}


class MyTask extends AsyncTask<String, Void, List<ChatMessage>> {// 接收消息


@Override
protected List<ChatMessage> doInBackground(String... params) {
// 联网获取json
String json = Utrls.getJsonContent(params[0]);


// 解析json
ChatMessage chat = new ChatMessage();
Chat chat1 = parseGson(json);
chat.setMessage(chat1.getText());
chat.setName("蛋蛋");
chat.setTime(form.format(new Date()));
chat.setType(type.INCOMING.toString());
list.add(chat);


return list;
}


@Override
protected void onPostExecute(List<ChatMessage> result) {
super.onPostExecute(result);
// 在适配器上添加资源
adapter = new com.example.myadapter.MyAdapter(MainActivity.this,
result);
listView.setAdapter(adapter);
adapter.notifyDataSetChanged();// 提醒数据更新
}
}


private Chat parseGson(String json) {
Gson gson = new Gson();
Chat chat = gson.fromJson(json, Chat.class);
return chat;
}
}


Utils工具类

package com.example.utils;


import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;


import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.ParseException;
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 org.apache.http.util.EntityUtils;


import android.graphics.Bitmap;
import android.graphics.BitmapFactory;


public class Utrls {
public static String getJsonContent(String url) {
String result = "";
HttpURLConnection connection = null;


BufferedReader br = null;
try {
URL url1 = new URL(url);
// 建立一个网络连接
connection = (HttpURLConnection) url1.openConnection();
br = new BufferedReader(new InputStreamReader(
connection.getInputStream(), "UTF-8"));


String line = "";
while ((line = br.readLine()) != null) {
result += line;
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
br.close();
connection.disconnect();
} catch (IOException e) {
e.printStackTrace();
}


}
return result;
}




// 获取图片
public static Bitmap getInage1(String url) {// HttpURLConnection获取
Bitmap bitmap = null;


try {
URL url1 = new URL(url);
// 建立一个网络连接
HttpURLConnection connection = (HttpURLConnection) url1
.openConnection();
// 获取网络返回的数据
BufferedInputStream bis = new BufferedInputStream(
connection.getInputStream());
// 通过decode方法把流转换为图片
bitmap = BitmapFactory.decodeStream(bis);


bis.close();
connection.disconnect();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return bitmap;
}
}

数据接口来源:图灵机器人http://www.tuling123.com/openapi/


你可能感兴趣的:(android,机器人)