Github项目地址:https://github.com/Churchillhua-wangfei/WF_Violet_RobotProject
基于这钉钉时代(疫情阶段),在家闷得慌,以一个初学者的身份制作一个Android图灵机器人聊天项目。
效果图:
简单的XML基础 + ListView控件 + 数据器适配 + Postman请求调用API
操作系统:windows10 1909
开发工具: JDK8 + Android studio 3.5.2 + 模拟器(Android8.0)
Android版本: Android API29
第三方API网址:http://www.turingapi.com/
<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:background="#E7E7E7"
android:orientation="vertical"
tools:context=".MainActivity">
<RelativeLayout
android:id="@+id/head"
android:layout_width="match_parent"
android:layout_height="42dp"
android:background="#fff">
<ImageView
android:id="@+id/re"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:layout_marginTop="7dp"
android:src="@drawable/re" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_marginLeft="5dp"
android:layout_toRightOf="@+id/re"
android:text="王飞的Robot-薇尔莉特"
android:textColor="#000000"
android:textSize="16dp" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_marginTop="4dp"
android:layout_marginRight="10dp"
android:src="@drawable/config" />
RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:id="@+id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="@+id/rl_bottom"
android:cacheColorHint="@android:color/black"
android:divider="@null"
android:listSelector="@null"
android:transcriptMode="alwaysScroll" />
<RelativeLayout
android:id="@+id/rl_bottom"
android:layout_width="match_parent"
android:layout_height="55dp"
android:layout_alignParentBottom="true"
android:background="@drawable/bottom_bg">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_marginLeft="3dp"
android:layout_marginTop="10dp"
android:src="@drawable/voice"
/>
<Button
android:id="@+id/btn_send"
android:layout_width="75dp"
android:layout_height="40dp"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_marginRight="10dp"
android:background="@drawable/button_circle_shape"
android:fontFamily="sans-serif-black"
android:text="发 送"
android:textColor="#FCFCFC"
android:textSize="14sp" />
<EditText
android:id="@+id/et_send_msg"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_centerVertical="true"
android:layout_marginLeft="45dp"
android:layout_marginRight="10dp"
android:layout_toLeftOf="@id/btn_send"
android:background="@drawable/textview_circle_item"
android:singleLine="true"
android:textColor="@android:color/black"
android:textSize="18sp" />
RelativeLayout>
RelativeLayout>
LinearLayout>
详细可以参考Github:https://github.com/Churchillhua-wangfei/WF_Violet_RobotProject
1.btn_send_selector.xm
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@android:color/darker_gray"
android:state_pressed="true" />
<item android:drawable="@color/btn_send_bg_normal" />
selector>
2.button_circle_shape.xml
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#6EA74F" />
<corners android:radius="25dp" />
<padding
android:bottom="2dp"
android:left="2dp"
android:right="2dp"
android:top="2dp"
/>
shape>
3.chat_left_selector.xml
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/chat_left_bg_pressed"
android:state_pressed="true" />
<item android:drawable="@drawable/chat_left_bg_normal" />
selector>
4.chat_right_selector.xml
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/chat_right_bg_pressed"
android:state_pressed="true" />
<item android:drawable="@drawable/chat_right_bg_normal" />
selector>
5.textview_circle_item.xml
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#FFFFFF" />
<corners android:radius="25dp" />
<padding
android:bottom="10dp"
android:left="10dp"
android:right="10dp"
android:top="10dp"
/>
shape>
6.values(clolors.xml)
<resources>
<color name="colorPrimary">#F57C00color>
<color name="colorPrimaryDark">#F57C00color>
<color name="colorAccent">#D81B60color>
<color name="btn_send_bg_normal">#95EC69color>
resources>
7.values(strings.xml)
图灵机器人
- 遇见你,真美好
- 客人,听好了。我的名字是薇尔莉特
- 这位客人,我的名字是薇尔莉特
- 请问客人你有什么需要?
8.values(styles.xml)
<resources>
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
- "colorPrimary"
>#0288D1
- "colorPrimaryDark">#0288D1
- "colorAccent">@color/colorAccent
style>
<style name="chat_content_style">
- "android:minHeight"
>50dp
- "android:gravity">center_vertical
- "android:layout_marginTop">12dp
- "android:textColor">#000000
- "android:textStyle">bold
- "android:textSize">15sp
- "android:lineSpacingExtra">2dp
- "android:clickable">true
- "android:focusable">true
- "android:background">#bfbfbf
style>
resources>
1.chatting_robot_item.xml
``
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="6dp">
<TextView
android:id="@+id/tv_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="2020年4月29日上午10点"
android:layout_centerHorizontal="true"
android:textColor="#000000"
android:textSize="18sp"
/>
<ImageView
android:id="@+id/iv_head"
android:layout_width="60dp"
android:layout_height="60dp"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_marginTop="15dp"
android:layout_marginLeft="2dp"
android:background="@drawable/violet"
android:focusable="false"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="薇尔莉特"
android:layout_below="@+id/iv_head"
android:textColor="#7B1FA2"
android:textSize="16dp"
android:layout_marginTop="5dp"
android:layout_marginLeft="5dp"
android:textStyle="bold"
/>
<TextView
android:id="@+id/tv_chat_content"
style="@style/chat_content_style"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:layout_marginTop="30dp"
android:layout_toEndOf="@id/iv_head"
android:background="@drawable/chat_left_selector" />
RelativeLayout>
2.chatting_user_item.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="6dp">
<TextView
android:id="@+id/tv_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="2020年4月29日上午10点"
android:layout_centerHorizontal="true"
android:textColor="#000000"
android:textSize="18sp"
/>
<ImageView
android:id="@+id/iv_head"
android:layout_width="55dp"
android:layout_height="55dp"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:layout_marginTop="15dp"
android:layout_marginRight="2dp"
android:background="@drawable/wangfei"
android:focusable="false"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/iv_head"
android:layout_alignParentRight="true"
android:text="王飞"
android:textSize="20dp"
android:textStyle="bold"
android:layout_marginTop="8dp"
android:layout_marginRight="8dp"
android:textColor="#090909"
/>
<TextView
android:id="@+id/tv_chat_content"
style="@style/chat_content_style"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="5dp"
android:layout_marginTop="30dp"
android:layout_toLeftOf="@id/iv_head"
android:background="@drawable/chat_right_selector" />
RelativeLayout>
package com.mzxy.wangfei_robot_chattingproject;
public class ChatBean {
public static final int SEND = 1; // 发送消息
public static final int RECEIVE = 2; // 接受消息
private int state; // 消息的状态(是接受还是发送)
private String message; // 消息的内容
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
package com.mzxy.wangfei_robot_chattingproject;
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 java.util.List;
class ChatAdapter extends BaseAdapter {
private List<ChatBean> chatBeanList; //聊天数据
private LayoutInflater layoutInflater;
public ChatAdapter(List<ChatBean> chatBeanList, Context context) {
this.chatBeanList = chatBeanList;
layoutInflater = LayoutInflater.from(context);
}
@Override
public int getCount() {
return chatBeanList.size();
}
@Override
public Object getItem(int position) {
return chatBeanList.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View contentView, ViewGroup viewGroup) {
Holder holder = new Holder();
//判断当前的信息是发送的信息还是接收到的信息,不同信息加载不同的view
if (chatBeanList.get(position).getState() == ChatBean.RECEIVE) {
//加载左边布局,也就是机器人对应的布局信息
contentView = layoutInflater.inflate(R.layout.chatting_robot_item,
null);
} else {
//加载右边布局,也就是用户对应的布局信息
contentView = layoutInflater.inflate(R.layout.chatting_user_item,
null);
}
holder.tv_chat_content = (TextView) contentView.findViewById(R.id.
tv_chat_content);
holder.tv_chat_content.setText(chatBeanList.get(position).getMessage());
return contentView;
}
class Holder {
public TextView tv_chat_content; // 聊天内容
}
}
1.在清单文件添加网络权限
<uses-permission android:name="android.permission.INTERNET" />
2.添加okhttp库(在build.gradle文件中添加)–在dependencies
dependencies{
implementation 'com.squareup.okhttp3:okhttp:3.12.0'
}
package com.mzxy.wangfei_robot_chattingproject;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.text.TextUtils;
import android.view.KeyEvent;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
public class MainActivity extends AppCompatActivity {
/**
*
* @author:王飞
* @My_GitHub:https://github.com/Churchillhua-wangfei
* @Project_FinishTime:2020年4月29日——09点09分
*/
private ListView listView;
private ChatAdapter adpter;
private List<ChatBean> chatBeanList; // 存放所有聊天数据的集合
private EditText et_send_msg;
private Button btn_send;
// 接口地址
private static final String WEB_SITE = "http://www.tuling123.com/openapi/api";
// 唯一key,该key的值是从官网注册账号后获取的,注册地址来自于:http://www.tuling123.com/
private static final String KEY = "40abb943778c472ea40e9c31e1a3c339";
private String sendMsg; // 发送的信息
private String welcome[]; // 存储欢迎信息
private MHandler mHandler;
public static final int MSG_OK = 1; // 获取数据
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
chatBeanList = new ArrayList<ChatBean>();
mHandler = new MHandler();
// 获取内置的欢迎信息
welcome = getResources().getStringArray(R.array.welcome);
initView(); // 初始化界面控件
}
public void initView() {
listView = findViewById(R.id.list);
et_send_msg = findViewById(R.id.et_send_msg);
btn_send = findViewById(R.id.btn_send);
adpter = new ChatAdapter(chatBeanList, this);
listView.setAdapter(adpter);
btn_send.setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View arg0) {
sendData(); // 点击发送按钮,发送信息
}
});
et_send_msg.setOnKeyListener(
new View.OnKeyListener() {
@Override
public boolean onKey(View view, int keyCode, KeyEvent keyEvent) {
if (keyCode == KeyEvent.KEYCODE_ENTER && keyEvent.getAction() == KeyEvent.ACTION_DOWN) {
sendData(); // 点击Enter键也可以发送信息
}
return false;
}
});
int position = (int) (Math.random() * welcome.length - 1); // 获取一个随机数
showData(welcome[position]); // 用随机数获取机器人的首次聊天信息
}
private void sendData() {
sendMsg = et_send_msg.getText().toString(); // 获取你输入的信息
if (TextUtils.isEmpty(sendMsg)) { // 判断是否为空
Toast.makeText(this, "你还未输任何信息,输出框不能为空", Toast.LENGTH_SHORT).show();
return;
}
et_send_msg.setText("");
// 替换空格和换行
sendMsg = sendMsg.replaceAll(" ", "").replaceAll("\n", "").trim();
ChatBean chatBean = new ChatBean();
chatBean.setMessage(sendMsg);
chatBean.setState(chatBean.SEND); // SEND表示自己发送的信息
chatBeanList.add(chatBean); // 将发送的信息添加到chatBeanList集合中
adpter.notifyDataSetChanged(); // 更新ListView列表
getDataFromServer(); // 从服务器获取机器人发送的信息
}
private void getDataFromServer() {
OkHttpClient okHttpClient = new OkHttpClient();
Request request =
new Request.Builder().url(WEB_SITE + "?key=" + KEY + "&info=" + sendMsg).build();
Call call = okHttpClient.newCall(request);
// 开启异步线程访问网络
call.enqueue(
new Callback() {
@Override
public void onResponse(Call call, Response response) throws IOException {
String res = response.body().string();
Message msg = new Message();
msg.what = MSG_OK;
msg.obj = res;
mHandler.sendMessage(msg);
}
@Override
public void onFailure(Call call, IOException e) {}
});
}
/** 事件捕获 */
class MHandler extends Handler {
@Override
public void dispatchMessage(Message msg) {
super.dispatchMessage(msg);
switch (msg.what) {
case MSG_OK:
if (msg.obj != null) {
String vlResult = (String) msg.obj;
paresData(vlResult);
}
break;
}
}
}
private void paresData(String JsonData) { // Json解析
try {
JSONObject obj = new JSONObject(JsonData);
String content = obj.getString("text"); // 获取的机器人信息
int code = obj.getInt("code"); // 服务器状态码
updateView(code, content); // 更新界面
} catch (JSONException e) {
e.printStackTrace();
showData("网络未连接");
}
}
private void showData(String message) {
ChatBean chatBean = new ChatBean();
chatBean.setMessage(message);
chatBean.setState(ChatBean.RECEIVE); // RECEIVE表示接收到机器人发送的信息
chatBeanList.add(chatBean); // 将机器人发送的信息添加到chatBeanList集合中
adpter.notifyDataSetChanged();
}
private void updateView(int code, String content) {
// code有很多种状,先弄几组测试数据
switch (code) {
case 4004:
showData("测试数据01");
break;
case 40005:
showData("测试数据02");
break;
case 40006:
showData("测试数据03");
break;
case 40007:
showData("测试数据04");
break;
default:
showData(content);
break;
}
}
protected long exitTime; // 记录第一次点击时的时间
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_DOWN) {
if ((System.currentTimeMillis() - exitTime) > 2000) {
Toast.makeText(MainActivity.this, "请再按一次退出应用程序", Toast.LENGTH_SHORT).show();
exitTime = System.currentTimeMillis();
} else {
MainActivity.this.finish();
System.exit(0);
}
return true;
}
return super.onKeyDown(keyCode, event);
}
}
试数据04");
break;
default:
showData(content);
break;
}
}
protected long exitTime; // 记录第一次点击时的时间
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_DOWN) {
if ((System.currentTimeMillis() - exitTime) > 2000) {
Toast.makeText(MainActivity.this, “请再按一次退出应用程序”, Toast.LENGTH_SHORT).show();
exitTime = System.currentTimeMillis();
} else {
MainActivity.this.finish();
System.exit(0);
}
return true;
}
return super.onKeyDown(keyCode, event);
}
}
##### 7.最后清理不必要的文件,Build APK文件