我一直坚信着,即使一个最简单的APP,也有他的难点,如果一个复杂的APP你无法做出来,那肯定是你基础不扎实,我身边的人总是问我,安卓为什么学了有忘记了,总是学不好,有些东西记不住,其实这不能怪你,如果别人开发了那么久的东西,你一下子就学会了,你让谷歌怎么活。
篇幅有限,如果读者看不懂的话,可以复习下基础,我尽量将代码简单化了。 先看效果图
如果你是初学安卓,这个APP你可能要半小时
如果你是有经验的开发者,OK,你可能看一遍就能将自己的开发类带入,10分钟不到就搞定。
开发步骤:
1. 先去图灵机器人注册KEY
http://www.tuling123.com/
2.准备好联网操作,将联网工具类放入
PS:这里是我自己的联网工具类,如果喜欢可以收藏
3.准备json解析
json解析并不复杂,所以只是用了自带json解析了
4.UI准备
5.业务逻辑准备
第一步 你先去 图灵机器人的官网申请key 这个不复杂,因为可以QQ登录 如果不想申请 那就用我的呗
key = 349baa5d2bd5d2d7612e4f2c1fcd973d
第二步 联网获取json
其实这个请求数据,没什么区别的,看看他的请求示例
private String url="http://www.tuling123.com/openapi/api?key=349baa5d2bd5d2d7612e4f2c1fcd973d&info=";
后面加入你的输入的信息 即可
联网:1.Httpclient 2. Httpconnection 3.Xutils 4.各种工具类。
一个开发者,随着开发的东西越来越来多,积累的工具也越来越多,在这个小Demo 里 你可以使用自己的工具类
我的联网工具类,是自己封装的 你可以在自带加入线程,用的是Httpconnection联网,然后根据IO操作 转成String 通过接口放回String类型。
package com.example.AiComputer;import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.nio.ByteOrder;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Handler;/*** 封装的 网络 + 线程*/
public class HttpUtils {// 使用线程池来下载图片,同一时刻,最多有3个线程在运行private static ExecutorService execuotrs = Executors.newFixedThreadPool(3);interface OnBitmapNetWorkResponse {public void ok(Bitmap bitmap);public void error(String error);}public static void RequestBitmapNetWork(final String path,final OnBitmapNetWorkResponse response) {final Handler handler = new Handler();execuotrs.execute(new Runnable() {@Overridepublic void run() {boolean isNetWorkOK = false;try {URL url = new URL(path);HttpURLConnection openConnection = (HttpURLConnection) url.openConnection();openConnection.setConnectTimeout(5000);openConnection.connect();if (openConnection.getResponseCode() == 200) {InputStream inputStream = openConnection.getInputStream();final Bitmap bitmap = BitmapFactory.decodeStream(inputStream);handler.post(new Runnable() {@Overridepublic void run() {response.ok(bitmap);}});inputStream.close();isNetWorkOK = true;}} catch (MalformedURLException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();} finally {if (!isNetWorkOK) {handler.post(new Runnable() {@Overridepublic void run() {response.error("服务器不在服务区内!");}});}}}});}public interface OnNetWorkResponse {public void ok(String response);public void error(String error);}public static void RequestNetWork(final String path,final OnNetWorkResponse response) {//实例化handler final Handler hanlder = new Handler();new Thread() {public void run() {//标志位boolean isWorkOK = false;InputStream inputStream = null;ByteArrayOutputStream outStream = null;try {URL url = new URL(path);System.out.println("=======path========="+path);HttpURLConnection connection = (HttpURLConnection) url.openConnection();connection.setConnectTimeout(5000);connection.setDoInput(true);connection.connect();if (connection.getResponseCode() == 200) {inputStream = connection.getInputStream();outStream = new ByteArrayOutputStream();byte[] b = new byte[1024];int len = 0;while ((len = inputStream.read(b)) != -1) {outStream.write(b, 0, len);}outStream.flush();final String result = new String(outStream.toByteArray());hanlder.post(new Runnable() {@Overridepublic void run() {response.ok(result);}});isWorkOK = true;}} catch (MalformedURLException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();} finally {// 网络操作出问题if (!isWorkOK) {response.error("服务器走神拉!");}if (inputStream != null) {try {inputStream.close();} catch (IOException e) {e.printStackTrace();}}if (outStream != null) {try {outStream.close();} catch (IOException e) {e.printStackTrace();}}}};}.start();}}
如果对于这一节,不懂的可以看下 我写的 网络编程系列
从java网络编程学起(2)
第三步,就是json 解析
我们在url后面加入 深圳的天气 返回的是
{"code":100000,"text":"今天多云 24~19°C明天阴 26~21°C后天阴 22~17°C"}
最外层是一个大括号,so 直接用JsonObject,解析即可
JSONObject jb = new JSONObject(response);
4.UI准备
其实对于ui 并不复杂, 最外层是一个 线性布局,里面是 两个线性布局,第一个装着listview ----->显示聊天信息 第二个 装着 一个button 发送消息 一个Edittext 输入信息
布局文件:activity_main.xml
<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" ><ListViewandroid: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><LinearLayoutandroid:layout_width="fill_parent"android:layout_height="wrap_content"android:orientation="horizontal" ><EditTextandroid:id="@+id/sendText"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="1" /><Button android:id="@+id/send_btn"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="发送"/></LinearLayout></LinearLayout>
既然说道了listview 就知道我们的聊天信息,要在上面显示,我们要在适配器中 判断是那个发送的消息,进行判断是哪个发送过来的 显示那部分布局 ,左边的是机器人的消息布局 右边是 我们发送消息的布局
也不复杂, 一个相对布局中有一个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="match_parent" ><TextViewandroid:id="@+id/time"android:layout_width="fill_parent"android:layout_height="wrap_content"android:gravity="center_horizontal" /><ImageViewandroid:id="@+id/iv"android:layout_width="70dp"android:layout_height="70dp"android:layout_below="@id/time"android:padding="10dp"android:src="@drawable/robot" /><TextViewandroid:id="@+id/tv"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_below="@id/time"android:layout_marginRight="50dp"android:layout_toRightOf="@id/iv"android:background="@drawable/aio_friend_bg_nor_11"android:gravity="center" /></RelativeLayout>
我们发送消息的内容
<?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" ><TextViewandroid:id="@+id/time"android:layout_width="fill_parent"android:layout_height="wrap_content"android:gravity="center_horizontal" /><ImageViewandroid:id="@+id/iv"android:layout_width="70dp"android:layout_height="70dp"android:layout_alignParentRight="true"android:layout_below="@id/time"android:padding="10dp"android:src="@drawable/xiaoxin" /><TextViewandroid:id="@+id/tv"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_below="@id/time"android:layout_marginLeft="50dp"android:layout_toLeftOf="@id/iv"android:background="@drawable/aio_user_bg_nor_11"android:gravity="center" /></RelativeLayout>
不知不觉,就走到了最后一步,相信 你一定会觉得,好简单呀,是的很简单,因为最难的在后面
业务逻辑 :
1.Edittext 和listview 和button的实例化
2.button点击事件的处理
获取 Edittext输入的内容,进行处理————>去掉空格和回车的信息,替代成空 。 然后将数据封装到实体类中去。 当大于30条的时候清空一次 然后刷新一下适配器
将处理好的信息 发送到服务器 获取 回复的信息 然后也封装进行实体类,刷新一下适配器
3. 设置欢迎语言,每一次进来,为了提高用户体验,我们进行欢迎语出现, 先建立一个数组 用来装 欢迎语
来到res/values/Strings 加入数组
<string-array name="welcome_tips"><item>主人,奴婢在此等候多时了</item><item>主人,近来一切可好</item><item>亲爱的,我想死你了</item><item>欢迎归来,我亲爱的主人</item><item>我是小新机器人,很高兴为您服务</item></string-array>
然后再 java 代码中如一个方法
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 "";}}
也就是获取了下 系统时间,然后格式化了下
Java代码 MainActivity
package com.example.AiComputer;import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;import org.json.JSONException;
import org.json.JSONObject;import com.example.AiComputer.HttpUtils.OnNetWorkResponse;import android.app.Activity;
import android.os.Bundle;
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 implements OnClickListener{//输入框private EditText sendtext;private Button send_btn;private ListView listView;private String content_str;private String[] welcome_array;private String url="http://www.tuling123.com/openapi/api?key=349baa5d2bd5d2d7612e4f2c1fcd973d&info=";private double currentTime=0, oldTime = 0;private List<ListData> lists;private ListData listData;private MyAdapter adapter;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);initView();}private void initView() {listView = (ListView) findViewById(R.id.lv);sendtext = (EditText) findViewById(R.id.sendText);send_btn = (Button) findViewById(R.id.send_btn);lists = new ArrayList<ListData>();//发送的点击事件send_btn.setOnClickListener(this);adapter = new MyAdapter(lists, this);listView.setAdapter(adapter);listData = new ListData(getRandomWelcomeTips(), ListData.RECEIVER,getTime());lists.add(listData);}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;}
/*1.获取 Edittext输入的内容,进行处理————>去掉空格和回车的信息,替代成空 。* 2.然后将数据封装到实体类中去。 当大于30条的时候清空一次 然后刷新一下适配器* */@Overridepublic void onClick(View v) {getTime();content_str = sendtext.getText().toString();sendtext.setText("");String dropk = content_str.replace(" ", "");String droph = dropk.replace("\n", "");listData = new ListData(content_str, ListData.SEND, getTime());lists.add(listData);if (lists.size() > 30) {for (int i = 0; i < lists.size(); i++) {lists.remove(i);}}adapter.notifyDataSetChanged();HttpUtils.RequestNetWork(url+droph, new OnNetWorkResponse() {@Overridepublic void ok(String response) {try {JSONObject jb = new JSONObject(response);// System.out.println(jb.getString("code"));// System.out.println(jb.getString("text"));ListData listData;listData = new ListData(jb.getString("text"), ListData.RECEIVER,getTime());lists.add(listData);adapter.notifyDataSetChanged();} catch (JSONException e) {// TODO Auto-generated catch blocke.printStackTrace();}}@Overridepublic void error(String error) {// TODO Auto-generated method stub}});}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 "";}}
}
其实在没有点击的时候,欢迎语 已经通过适配器 显示到listview上面去了 所以我们可以放在adapter实例化下面 ,实体类,主要起的作用就是将 发送的时间和内容 封装好,在适配器中判断 进行展示
其实最重要的是 适配器的内容,应该怎么处理 发送过来的消息:
我们向适配器 传了两个值集合,装满了实体类数据的集合 一个上下文
我们先建立一个相对布局,用来填充条目的内容 我们要想一个list装着实体类,实体类,有很多数据 ,我们需要通过标识符来判断显示那部分内容。
package com.example.AiComputer;import java.util.List;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 MyAdapter extends BaseAdapter{private List<ListData> lists;private Context mContext;private RelativeLayout layout;public MyAdapter(List<ListData> lists,Context mContext) {this.lists = lists;this.mContext = mContext;}@Overridepublic int getCount() {return lists.size();}@Overridepublic Object getItem(int position) {return lists.get(position);}@Overridepublic long getItemId(int position) {return position;}@Overridepublic View getView(int position, View convertView, ViewGroup parent) {LayoutInflater inflater = LayoutInflater.from(mContext);if(lists.get(position).getFlag() == ListData.RECEIVER){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 time = (TextView) layout.findViewById(R.id.time);tv.setText(lists.get(position).getContent());time.setText(lists.get(position).getTime());return layout;}}
如有问题,可以在我博客上面留言