开发者社区> tonjies> 正文

使用kotlin实现一个智能聊天机器人「图灵机器人,Android,kotlin」

简介: 推荐一部关于Ai的系列漫画,叫做代码的深渊 相关代码已经上传到Github的仓库kotlinRobot 先来看一下实现的效果图 智能机器人效果预览~1.gif 文章思路参考自刘桂林前辈的带领新手快速开发APP ,聊天界面代码参考于郭神的第一行代码第二版第三章3.7节,由衷感谢。
+关注继续查看

推荐一部关于Ai的系列漫画,叫做代码的深渊

相关代码已经上传到Github的仓库kotlinRobot

先来看一下实现的效果图

img_a46578492e1029f1b2d6ef5dc5fcf711.gif
智能机器人效果预览~1.gif

文章思路参考自刘桂林前辈的带领新手快速开发APP ,聊天界面代码参考于郭神的第一行代码第二版第三章3.7节,由衷感谢。自己也写过一篇讲解kotlin几个基础小知识点的文章,kotlin初体验,文中不足之处,希望能得到您的指正,十分感谢。

布局文件

创建我们的项目,勾选kotlin支持,并在build.gradle下填入依赖

implementation 'com.android.support:design:26.1.0'

然后我们进去res-values-styles下,把主题修改成没有标题的样式

<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
    <!-- Customize your theme here. -->
    <item name="colorPrimary">@color/colorPrimary</item>
    <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
    <item name="colorAccent">@color/colorAccent</item>
</style>

在activity_main布局里写一个recyclerView,用来显示对话聊天消息,以及一个EditText输入框,和一个发送数据的button按钮

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.example.administrator.robot.MainActivity">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recycler"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical"
        android:gravity="center_vertical"
        android:orientation="horizontal">

        <EditText
            android:id="@+id/et_text"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:hint="输入" />

        <Button
            android:id="@+id/btn_send"
            android:layout_width="66dp"
            android:layout_height="36dp"
            android:layout_margin="5dp"
            android:background="#444444"
            android:text="发送"
            android:textColor="#FFFFFF" />
    </LinearLayout>

</LinearLayout>

先来写一个Log工具类,方便测试,创建util包,在util下创建L类

/**
 * Created by 舍长 on 2018/4/27.
 * 在kotlin中,加了object后,L类就成为了一个单例模式的类,相当于帮我们省略掉了以前Java实现单例的代码
 * 最后我们可以直接L.d调用类中的方法
 */
object L {

    //    TAG
    public var TAG: String = "tonJies"

    fun d(test: String) {
        Log.d(TAG, test)
    }
}

我们回到Activity,在activity的导包中添加这一行,我们就不需要再写findViewByid了

import kotlinx.android.synthetic.main.activity_main.*

我们给发送按钮设置点击事件,回调监听,在点击事件里面进行输入框内容的处理,依次是:

1,获取输入框的内容

2,判断是否为空

3,点击发送按钮后清空当前输入框

//加了这一行后我们就不需要再findViewById了
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity(), View.OnClickListener {


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
//       设置控件的回调监听
        init()
        L.d("Hello")
    }

    /**
     * 设置控件的回调监听
     */
    private fun init() {
        btn_send.setOnClickListener(this)
    }


    /**
     * 重写OnClickListener接口的onClick方法
     */
    override fun onClick(v: View?) {
        when (v!!.id) {
            R.id.btn_send -> {
                /**
                 * 1,获取输入框的内容
                 * 2,判断是否为空
                 * 4,发送后清空当前的输入框
                 */
//               1,获取输入框的内容
                val text: String = et_text.text.toString()
//               2,判断是否为空
                if (!TextUtils.isEmpty(text)) {
                    L.d("不为空")
                } else {
                    L.d("为空")
                }
            }
        }
    }

}

聊天界面的书写

思路是使用recyclerView作为聊天对话列表的显示控件。让我们先在build.gradle下添加圆形化图片的框架CircleImageView,用来对头像的圆形处理

//CircleImageView
compile 'de.hdodenhof:circleimageview:2.1.0'

创建bean包,在文件夹类创建实体类Chat,用于储存后面的聊天数据

/**
 * data 数据类,默认帮我们实现了实体类的几个方法,例如toString,赋值
 * text 聊天文本数据
 * type 用来标示此对话类型是属于左边机器人发来的文本类型还是我们向机器人发送的文本类型,后面我们就根据这个属性来
 */
data class Chat(var text: String,var type: Int) {

}

这里的实体类和我们在Java中的实体类写法有些不同,data关键字在kotlin是数据类的意思,它的作用是帮我们默认生成了几个常用方法,如toString()方法,而且加了data后,括号里面的参数就可直接作为属性值使用了,不需要再像Java一样,在类中声明,然后再到构造方法中进行赋值。

然后我们开始写recyclerview的item布局,命名为chat_item,图片素材参考源代码

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:padding="10dp">

    <LinearLayout
        android:id="@+id/left_layout"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="left">

        <de.hdodenhof.circleimageview.CircleImageView
            android:layout_width="50dp"
            android:layout_height="50dp"
            android:layout_marginLeft="10dp"
            android:src="@drawable/robot_logo" />

        <TextView
            android:id="@+id/tv_left_text"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="10dp"
            android:background="@drawable/chat_bg_cloud"
            android:gravity="center_vertical"
            android:padding="20dp"
            android:text="你好,我叫阿紫"
            android:textColor="@android:color/black" />
    </LinearLayout>

    <LinearLayout
        android:id="@+id/right_layout"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="right">

        <TextView
            android:id="@+id/tv_right_text"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginRight="10dp"
            android:background="@drawable/chat_bg_user"
            android:gravity="center_vertical"
            android:padding="20sp"
            android:text="你好,你会些什么啊"
          android:textColor="@android:color/white" />

        <de.hdodenhof.circleimageview.CircleImageView
            android:layout_width="50dp"
            android:layout_height="50dp"
            android:layout_marginRight="10dp"
            android:src="@drawable/user_logo" />
    </LinearLayout>

</LinearLayout>

我们先把左右的消息都写出来,在后面Adapter加载每一个item的时候我们再根据Chat的type类型来决定要隐藏那边的布局

img_fa62bf2db2b7611ed625ea3d4012bd7a.png
这里写图片描述

接下来开始写RecyclerView的适配器

/**
 * Created by 舍长 on 2018/5/7.
 * 描述: 聊天布局适配器
 */

class RecyclerViewAdapter : RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder> {


    //    上下文
    private var context: Context? = null

    //   用户聊天消息列表
    private var mlist = ArrayList<Chat>()

    /**
     * 空参构造方法
     */
    constructor() {

    }

    /**
     * context 上下文
     * list  对话列表聊天数据
     */
    constructor(context: Context, list: ArrayList<Chat>) {
        this.context = context
        this.mlist = list
    }

    /**
     * 加载item布局
     */
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val view = LayoutInflater.from(parent.context).inflate(R.layout.chat_item, parent, false)
        return ViewHolder(view)
    }


    /**
     *  在onBindViewHolder()方法中判断是要显示对应position位置的item布局要隐藏哪边的布局
     *  我们用0来表示机器人的文本类型,用1来表示用户的文本类型。
     */
    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        val chat = mlist[position]
//
        if (chat.type == 0) {
            //   如果数据是机器人的文本类型,就显示左边的布局,隐藏右边的布局
            holder.leftLayout.visibility = View.VISIBLE
            holder.rightLayout.visibility = View.GONE
            //    把文本设置到机器人对话框内
            holder.leftChat.text = chat.text
            //
        } else if (chat.type == 1) {
            //   如果数据是用户的文本类型,就隐藏左边布局,显示右边的布局
            holder.rightLayout.visibility = View.VISIBLE
            holder.leftLayout.visibility = View.GONE
            //            把文本设置到用户对话框内
            holder.rightChat.text = chat.text
        }
    }

    override fun getItemCount(): Int {
        return mlist.size
    }

    /**
     * 声明控件
     *
     */
    class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {

        //      左边的机器人布局
        var leftLayout: LinearLayout

        //      右边的用户布局
        var rightLayout: LinearLayout

        //      左边的机器人文本
        var leftChat: TextView

        //      右边的用户发送文本
        var rightChat: TextView

        /**
         *
         */
        init {
            leftLayout = itemView.findViewById(R.id.left_layout)
            rightLayout = itemView.findViewById(R.id.right_layout)
            leftChat = itemView.findViewById(R.id.tv_left_text)
            rightChat = itemView.findViewById(R.id.tv_right_text)
        }
    }
}

我们声明一个mlist集合来进行数据的传递,在onBindViewHolder方法中使用0作为机器人文本的标识,使用1作为用户文本的标识,根据标识的不同来决定显示item哪边的布局,再把Chat类对象的text属性传入要显示布局的TextView上

Adapter写好后我们在Activity中进行数据的填充,并设置RecyclerView的布局管理器,以及适配器

//加了这一行后我们就不需要再findViewById了
import kotlinx.android.synthetic.main.activity_main.*

/**
 * 继承于AppCompatActivity()
 * 继承于View.OnClickListener接口,复写onClick点击事件
 */
class MainActivity : AppCompatActivity(), View.OnClickListener {
    //    对话列表集合
    private var list = ArrayList<Chat>()

    //    recyclerView适配器
    private var recyclerViewAdapter = RecyclerViewAdapter(this, list)

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
//       设置控件的回调监听
        init()
//       加载数据
        initData();
        //       设置recyclerView布局管理
        val linearLayoutManager = LinearLayoutManager(this)
//       把布局管理器添加到recyclerView中
        recycler.setLayoutManager(linearLayoutManager)
//       把适配器添加到recyclerView中
        recycler.setAdapter(recyclerViewAdapter)
    }

    /**
     * 设置控件的回调监听
     */
    private fun init() {
        btn_send.setOnClickListener(this)
    }

    /**
     * 重写OnClickListener接口的onClick方法
     */
    override fun onClick(p0: View?) {
        when (p0!!.id) {
            R.id.btn_send -> {
                /**
                 * 1,获取输入框的内容
                 * 2,判断是否为空
                 * 4,发送后清空当前的输入框
                 */
//               1,获取输入框的内容
                val text: String = et_text.text.toString()
//               2,判断是否为空
                if (!TextUtils.isEmpty(text)) {
                    L.d(text + "")
                    addData(text, 1)
                } else {
                    L.d("你的输入为空")
                }
            }
        }
    }

    /**
     * 通过传递进来的test和type创建数据实体类,添加到聊天数据集合list中
     * @param text 文本信息
     * @param type 标示类型
     */
    private fun addData(mtext: String, mtype: Int) {
        L.d(mtext + "" + mtype)
        var c = Chat(mtext, mtype)
        list.add(c)

//      更新适配器,插入新数据
        recyclerViewAdapter.notifyItemInserted(list.size - 1)
//      把显示的位置定位到最后一行
        recycler.scrollToPosition(list.size - 1)
    }

    /**
     * 模拟加载数据
     */
    private fun initData() {
//      传入0到c1的type属性来表示该数据是接受到的数据,然后把实体类c1添加到对话列表集合中
        var c1: Chat = Chat("你好,我叫阿紫", 0)
        list.add(c1)
//      使用1到c2的type属性来表示该数据是输入框的发送数据,然后把实体类c2添加到对话列表集合中
        var c2: Chat = Chat("你好,你现在会些什么呢?", 1)
        list.add(c2)
//      接受数据
        var c3: Chat = Chat("我还在成长中,很多东西还不懂,但是你可以考考我\"", 0)
        list.add(c3)
//      发送数据
        var c4: Chat = Chat("1+1等于几?\"", 1)
        list.add(c4)
    }


}

我们声明了一个集合list来存储我们的聊天数据,在initData()方法中加载数据,然后设置RecyclerView的布局管理器,并将集合数据填充到适配器中。

在按钮点击事件里获取数据框的输入内容,设置标识为1,填充到list集合中,刷新适配器并把显示的地方定位到最后一行,清空输入框。

至此,我们的聊天界面就写好了,让我们来看看目前的效果

img_f2001f2a9a7de6008d1721e7cac1038b.gif
这里写图片描述

后端接口的调试

后端的接口我们使用图灵机器人,打开网站,注册好账号,创建一个机器人,我把机器人命名为阿紫,然后我们先用调试工具进行接口的调试,这里我用的调试工具是PostMan,参照图灵机器人官方文档

img_f625eca7fc97d1cce524f4eac8dc02db.png
这里写图片描述

格式是发送Json数据的Post请求,在Json数据中带着两个参数apikey和userId:

{
    "reqType":0,
    "perception": {
        "inputText": {
            "text": "你叫什么"
        }
    },
    "userInfo": {
        "apiKey": "c00282de107144fb940adab994d9ff98",
        "userId": "225167"
    }
}

1,apikey(在机器人管理页),比如这里我的是:c00282de107144fb940adab994d9ff98,

2,userId(右上角用户头像右边的数字),这里我的是:225167

3,text就是表示我们想要和机器人聊天的具体文字

img_cbf29e16f33ed06def8815cda0d0a44b.png
这里写图片描述

进行调试

img_1357cf3f878c715306379a380323d1d6.png
这里写图片描述

不得已截了大图,缩略图又有点模糊,点击图片就可以放大查看了

 {
    "emotion": {
        "robotEmotion": {
            "a": 0,
            "d": 0,
            "emotionId": 0,
            "p": 0
        },
        "userEmotion": {
            "a": 0,
            "d": 0,
            "emotionId": 0,
            "p": 0
        }
    },
    "intent": {
        "actionName": "",
        "code": 10004,
        "intentName": ""
    },
    "results": [{
        "groupType": 0,
        "resultType": "text",
        "values": {
            "text": "叫我阿紫就可以了"
        }
    }]
}

数据比较多,但是我们关心得的,仅仅只是Json数组result里面的test:我叫阿紫,不要被我的名字所迷倒哦而已,我们可以试试发送不同的文本,看看机器人会怎么回答,比如:

什么是Android? -- Android是一种基于Linux的自由及开放源代码的操作系统,主要使用于移...

你喜欢我吗? -- 你喜欢我,我就喜欢你。

至此,我们确定接口调试成功了

对接网络接口:

我们采用Retrofit作为我们网络框架,还没接触过Retrofit的伙伴可以参考这篇文章

添加依赖:

//    retrofit
    compile 'com.squareup.okhttp3:okhttp:3.1.2'
    compile 'com.squareup.retrofit2:retrofit:2.0.1'
    compile 'com.squareup.retrofit2:converter-gson:2.0.1'

别忘了在AndroidManifest.xml文件中添加网络权限

<uses-permission android:name="android.permission.INTERNET"/>

让我们来看看要作为body发送给服务端是Json数据

{
    "reqType":0,
    "perception": {
        "inputText": {
            "text": "你叫什么"
        }
    },
    "userInfo": {
        "apiKey": "c00282de107144fb940adab994d9ff98",
        "userId": "225167"
    }
}

我们在bean包创建实体类Ask,作为发送请求的请求体。

使用Java代码时,生成JavaBean实体类一般使用JsonFormat插件进行生成,但是JsonFormat在Kotlin中是使用不了的,所以我们要使用kotlin的生成JavaBean插件,JSON To Kotlin

打开File-Setting-Plugins-Browse respositories 搜索 JSON To Kotlin Class 安装,然后使用它生成Ask请求体实体类

img_b83873430485c93a060a2603384e1883.gif
这里写图片描述

打开Setting-Other-勾选Enable Inner Class Model 的作用是使得生成的数据都以内部类的形式出现,不勾选的话会默认把所有的类都生成在包内

/**
 * 请求数据请求体实体类
 * reqType 传0就行
 */
data class Ask(val reqType: Int,
        val perception: Perception,
        val userInfo: UserInfo
) {
    data class Perception(
            val inputText: InputText
    ) {
//      要发送的文本消息
        data class InputText(
                val text: String
        )
    }

    data class UserInfo(
//          机器人apiKey
            val apiKey: String,
//          用户id
            val userId: String
    )
}

上面提到加了data关键字,会帮我们默认生成几个方法,比如toString(),格式是例如"User(name=John, age=42)";但是要注意的一点是加了data,类就不能有无参构造方法了,如果我们输入这样的代码,var ask=Ask(),那就会报错了。

再看看响应体的Json数据

{
    "emotion": {
        "robotEmotion": {
            "a": 0,
            "d": 0,
            "emotionId": 0,
            "p": 0
        },
        "userEmotion": {
            "a": 0,
            "d": 0,
            "emotionId": 0,
            "p": 0
        }
    },
    "intent": {
        "actionName": "",
        "code": 10004,
        "intentName": ""
    },
    "results": [{
        "groupType": 0,
        "resultType": "text",
        "values": {
            "text": "叫我阿紫就可以了"
        }
    }]
}

一样,在bean目录下创建接受数据实体类Take(响应体)


/**
 * 返回数据响应体实体类
 */
data class Take(
        val emotion: Emotion,
        val intent: Intent,
        val results: List<Result>
) {
    data class Emotion(
            val robotEmotion: RobotEmotion,
            val userEmotion: UserEmotion
    ) {
        data class UserEmotion(
                val a: Int,
                val d: Int,
                val emotionId: Int,
                val p: Int
        )

        data class RobotEmotion(
                val a: Int,
                val d: Int,
                val emotionId: Int,
                val p: Int
        )
    }

    data class Result(
            val groupType: Int,
            val resultType: String,
            val values: Values
    ) {
        //      返回文本 "叫我阿紫就可以了"
        data class Values(
                val text: String
        )
    }

    data class Intent(
            val actionName: String,
            val code: Int,
            val intentName: String
    )
}

响应体实体类中,我们关注的只有text返回的文本数据

接下来我们创建net包,在net包内写一个Retrofit的实现接口


/**
 * Created by 舍长 on 2018/5/10.
 * 描述: Retrofit接口
 */

public interface Api {
    //发送json数据形式的post请求,把网络请求接口的后半部分openapi/api/v写在里面
    //Get是请求数据实体类,Take接受数据实体类
    @POST("openapi/api/v2")
    Call<Take> request(@Body Ask ask);
}

之后就直接在MainActivity写我们的数据请求方法了


//加了这一行后我们就不需要再findViewById了
import kotlinx.android.synthetic.main.activity_main.*
import retrofit2.Response
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory

/**
 * 继承于AppCompatActivity()
 * 继承于View.OnClickListener接口,复写onClick点击事件
 */
class MainActivity : AppCompatActivity(), View.OnClickListener {
    //    对话列表集合
    private var list = ArrayList<Chat>()

    //    recyclerView适配器
    private var recyclerViewAdapter = RecyclerViewAdapter(this, list)

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
//       设置控件的回调监听
        init()
//       加载数据
        initData()
        //       设置recyclerView布局管理
        val linearLayoutManager = LinearLayoutManager(this)
//       把布局管理器添加到recyclerView中
        recycler.setLayoutManager(linearLayoutManager)
//       把适配器添加到recyclerView中
        recycler.setAdapter(recyclerViewAdapter)


    }

    /**
     * 设置控件的回调监听
     */
    private fun init() {
        btn_send.setOnClickListener(this)
    }

    /**
     * 重写OnClickListener接口的onClick方法
     */
    override fun onClick(p0: View?) {
        when (p0!!.id) {
            R.id.btn_send -> {
                /**
                 * 1,获取输入框的内容
                 * 2,判断是否为空
                 * 4,发送后清空当前的输入框
                 */
//               1,获取输入框的内容
                val text: String = et_text.text.toString()
//               2,判断是否为空
                if (!TextUtils.isEmpty(text)) {
                    addData(text, 1)
                    request(text)
                } else {
                    L.d("你的输入为空")
                }
            }
        }
    }

    /**
     * 通过传递进来的test和type创建数据实体类,添加到聊天数据集合list中
     * @param text 文本信息
     * @param type 标示类型
     */
    private fun addData(mtext: String, mtype: Int) {
        L.d("addData  mtext:" + mtext + "  mtype" + mtype)
        var c = Chat(mtext, mtype)
        list.add(c)
//      更新适配器,插入新数据
        recyclerViewAdapter.notifyItemInserted(list.size - 1)
//      把显示的位置定位到最后一行
        recycler.scrollToPosition(list.size - 1)
    }

    /**
     * 模拟加载数据
     */
    private fun initData() {
//      传入0到c1的type属性来表示该数据是接受到的数据,然后把实体类c1添加到对话列表集合中
        var c1: Chat = Chat("你好,我叫阿紫", 0)
        list.add(c1)
//      使用1到c2的type属性来表示该数据是输入框的发送数据,然后把实体类c2添加到对话列表集合中
        var c2: Chat = Chat("你好,你现在会些什么呢?", 1)
        list.add(c2)
//      接受数据
        var c3: Chat = Chat("我还在成长中,很多东西还不懂,但是你可以考考我\"", 0)
        list.add(c3)
//      发送数据
        var c4: Chat = Chat("1+1等于几?\"", 1)
        list.add(c4)
        //      接受数据
        var c5: Chat = Chat("1+1=2", 0)
        list.add(c5)
    }


    /**
     * 请求数据
     *
     */
    private fun request(mText: String) {
//      存储要发送的的文本
        var perceotion = Ask.Perception(Ask.Perception.InputText(mText))
//      设置用户id和ApidKey
        val userInfo = Ask.UserInfo("c00282de107144fb940adab994d9ff98", "225167")
//      填充到请求体Ask中
        var ask = Ask(0, perceotion, userInfo)

//      使用retiofit进行请求
        var retrofit = Retrofit.Builder()
                .baseUrl("http://openapi.tuling123.com/")
                .addConverterFactory(GsonConverterFactory.create())
                .build()
//      创建网络请求接口的实例
        val api = retrofit.create(Api::class.java)
//
        val call = api.request(ask)

//
        call.enqueue(object : retrofit2.Callback<Take> {
            //          请求成功
            override fun onResponse(call: retrofit2.Call<Take>, response: Response<Take>) {
                //              接受到的机器人回复的数据
                L.d("返回的全部信息:" + response.body().toString())
                var text = response.body().results.get(0).values.text
                //在这里进行处理,防止接口没有返回数据时抛出异常
                if (text == null) {
                    text = "我还小,不知道这句话的意思"
                    //把接受到的数据传入addData方法中,类型是TYPE_RECEIVED接受数据
                    addData(text, 0)
                } else {
                    //把接受到的数据传入addData方法中,类型是TYPE_RECEIVED接受数据
                    addData(text, 0)
                }
                L.d("接受到的机器人回复的数据: " + text)
            }

            //            请求失败
            override fun onFailure(call: retrofit2.Call<Take>, t: Throwable) {
                L.d("请求失败: " + t.toString())
            }
        })
    }
}

在方法中我们创建了一个Ask请求体对象,把机器人key,userId和要发送的文本传入,注意reqType我们传入0就可以了。使用Retrofit进行网络请求,在回调里接收返回的文本,把返回的文本传入addData中,标识为0,机器人文本。

当然request方法是在点击按钮时调用的,Activty完整代码

//加了这一行后我们就不需要再findViewById了
import kotlinx.android.synthetic.main.activity_main.*
import retrofit2.Response
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory

/**
 * 继承于AppCompatActivity()
 * 继承于View.OnClickListener接口,复写onClick点击事件
 */
class MainActivity : AppCompatActivity(), View.OnClickListener {
    //    对话列表集合
    private var list = ArrayList<Chat>()

    //    recyclerView适配器
    private var recyclerViewAdapter = RecyclerViewAdapter(this, list)

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
//       设置控件的回调监听
        init()
//       加载数据
        initData()
        //       设置recyclerView布局管理
        val linearLayoutManager = LinearLayoutManager(this)
//       把布局管理器添加到recyclerView中
        recycler.setLayoutManager(linearLayoutManager)
//       把适配器添加到recyclerView中
        recycler.setAdapter(recyclerViewAdapter)


    }

    /**
     * 设置控件的回调监听
     */
    private fun init() {
        btn_send.setOnClickListener(this)
    }

    /**
     * 重写OnClickListener接口的onClick方法
     */
    override fun onClick(p0: View?) {
        when (p0!!.id) {
            R.id.btn_send -> {
                /**
                 * 1,获取输入框的内容
                 * 2,判断是否为空
                 * 4,发送后清空当前的输入框
                 */
//               1,获取输入框的内容
                val text: String = et_text.text.toString()
//               2,判断是否为空
                if (!TextUtils.isEmpty(text)) {
                    addData(text, 1)
                    request(text)
                } else {
                    L.d("你的输入为空")
                }
            }
        }
    }

    /**
     * 通过传递进来的test和type创建数据实体类,添加到聊天数据集合list中
     * @param text 文本信息
     * @param type 标示类型
     */
    private fun addData(mtext: String, mtype: Int) {
        L.d("addData  mtext:" + mtext + "  mtype" + mtype)
        var c = Chat(mtext, mtype)
        list.add(c)
//      更新适配器,插入新数据
        recyclerViewAdapter.notifyItemInserted(list.size - 1)
//      把显示的位置定位到最后一行
        recycler.scrollToPosition(list.size - 1)
    }

    /**
     * 模拟加载数据
     */
    private fun initData() {
//      传入0到c1的type属性来表示该数据是接受到的数据,然后把实体类c1添加到对话列表集合中
        var c1: Chat = Chat("你好,我叫阿紫", 0)
        list.add(c1)
//      使用1到c2的type属性来表示该数据是输入框的发送数据,然后把实体类c2添加到对话列表集合中
        var c2: Chat = Chat("你好,你现在会些什么呢?", 1)
        list.add(c2)
//      接受数据
        var c3: Chat = Chat("我还在成长中,很多东西还不懂,但是你可以考考我\"", 0)
        list.add(c3)
//      发送数据
        var c4: Chat = Chat("1+1等于几?\"", 1)
        list.add(c4)
        //      接受数据
        var c5: Chat = Chat("1+1=2", 0)
        list.add(c5)
    }


    /**
     * 请求数据
     *
     */
    private fun request(mText: String) {
//      存储要发送的的文本
        var perceotion = Ask.Perception(Ask.Perception.InputText(mText))
//      设置用户id和ApidKey
        val userInfo = Ask.UserInfo("c00282de107144fb940adab994d9ff98", "225167")
//      填充到请求体Ask中
        var ask = Ask(0, perceotion, userInfo)

//      使用retiofit进行请求
        var retrofit = Retrofit.Builder()
                .baseUrl("http://openapi.tuling123.com/")
                .addConverterFactory(GsonConverterFactory.create())
                .build()
//      创建网络请求接口的实例
        val api = retrofit.create(Api::class.java)
//
        val call = api.request(ask)

//
        call.enqueue(object : retrofit2.Callback<Take> {
            //          请求成功
            override fun onResponse(call: retrofit2.Call<Take>, response: Response<Take>) {
                //              接受到的机器人回复的数据
                L.d("返回的全部信息:" + response.body().toString())
                var text = response.body().results.get(0).values.text
                //在这里进行处理,防止接口没有返回数据时抛出异常
                if (text == null) {
                    text = "我还小,不知道这句话的意思"
                    //把接受到的数据传入addData方法中,类型是TYPE_RECEIVED接受数据
                    addData(text, 0)
                } else {
                    //把接受到的数据传入addData方法中,类型是TYPE_RECEIVED接受数据
                    addData(text, 0)
                }
                L.d("接受到的机器人回复的数据: " + text)
            }

            //            请求失败
            override fun onFailure(call: retrofit2.Call<Take>, t: Throwable) {
                L.d("请求失败: " + t.toString())
            }
        })
    }
}

这一小节就到这里啦,谢谢您的观看,文中不足之处,希望能得到您的指正,期待您的留言

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
阿里云服务器如何登录?阿里云服务器的三种登录方法
购买阿里云ECS云服务器后如何登录?场景不同,阿里云优惠总结大概有三种登录方式: 登录到ECS云服务器控制台 在ECS云服务器控制台用户可以更改密码、更换系.
25039 0
学习笔记TF059:自然语言处理、智能聊天机器人
自然语言处理,语音处理、文本处理。语音识别(speech recognition),让计算机能够“听懂”人类语音,语音的文字信息“提取”。 日本富国生命保险公司花170万美元安装人工智能系统,客户语言转换文本,分析词正面或负面。
2132 0
《中国人工智能学会通讯》——1.20 聊天机器人研究存在的挑战
本节书摘来自CCAI《中国人工智能学会通讯》一书中的第1章,第1.20节, 更多章节内容可以访问云栖社区“CCAI”公众号查看。
983 0
阿里云服务器端口号设置
阿里云服务器初级使用者可能面临的问题之一. 使用tomcat或者其他服务器软件设置端口号后,比如 一些不是默认的, mysql的 3306, mssql的1433,有时候打不开网页, 原因是没有在ecs安全组去设置这个端口号. 解决: 点击ecs下网络和安全下的安全组 在弹出的安全组中,如果没有就新建安全组,然后点击配置规则 最后如上图点击添加...或快速创建.   have fun!  将编程看作是一门艺术,而不单单是个技术。
18610 0
android应用中去android市场去评分的功能实现(吐槽一波个人应用上线...)
一般的app可能会有这中功能,在应用中去android商店评分来提高排名,前段时间也把我的博客园上传到商店,这里不得不吐槽一些android商店的开放平台。 酷派,vivo,oppo,联想不支持个人开发者……. 有得还要软件著作证书,一个个人开发者,搞个应用练练手,哪有没精力和财力去搞这些玩意。
1975 0
阿里云服务器怎么设置密码?怎么停机?怎么重启服务器?
如果在创建实例时没有设置密码,或者密码丢失,您可以在控制台上重新设置实例的登录密码。本文仅描述如何在 ECS 管理控制台上修改实例登录密码。
20328 0
《中国人工智能学会通讯》——1.16 聊天机器人系统的组成结构及关键 技术
本节书摘来自CCAI《中国人工智能学会通讯》一书中的第1章,第1.16节, 更多章节内容可以访问云栖社区“CCAI”公众号查看。
2460 0
Android 程序员不得不收藏的 90+ 个人博客(持续更新...)
每周打开一次收藏夹里的个人博客,已经成为了我的人生一大乐趣。 相比各大博客平台,我一直更加偏爱个人博客。在每个人自己的这一亩三分地里,你能看到的,学到的,不仅仅只有知识,还有不一样的生活,不一样的态度。 在这把我收藏的个人博客分享给大家,如果里面有你,不妨点个赞吧 ! 以下排名不分先后。如果一定要有个顺序,大概是按我收藏的顺序。 Trinea 的博客 来自滴滴出行,Android 开发助手 开发者,android-open-project 维护者 ,android-open-project-analysis 维护者。 中二病也要开发 ANDROID 挺喜欢的一位作者,遗憾的是已经
45 0
寫一個智能聊天機器人
文本聊天 聲音聊天 視頻聊天 关注下面的公众号,体验聊天机器人:小贱贱 图片发自简书App 图片发自简书App 图片发自简书App 图片发自简书App 图片发自简书App 图片发...
752 0
+关注
tonjies
我是一名Android开发者,我的理念一直很认可以知识换知识的方式,所以分享知识就成为我一直在做的事情。
16
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
JS零基础入门教程(上册)
立即下载
性能优化方法论
立即下载
手把手学习日志服务SLS,云启实验室实战指南
立即下载