使用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())
            }
        })
    }
}

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

目录
相关文章
|
16天前
|
移动开发 Java Android开发
构建高效Android应用:探究Kotlin与Java的性能差异
【4月更文挑战第3天】在移动开发领域,性能优化一直是开发者关注的焦点。随着Kotlin的兴起,其在Android开发中的地位逐渐上升,但关于其与Java在性能方面的对比,尚无明确共识。本文通过深入分析并结合实际测试数据,探讨了Kotlin与Java在Android平台上的性能表现,揭示了在不同场景下两者的差异及其对应用性能的潜在影响,为开发者在选择编程语言时提供参考依据。
|
17天前
|
数据库 Android开发 开发者
构建高效Android应用:Kotlin协程的实践指南
【4月更文挑战第2天】随着移动应用开发的不断进步,开发者们寻求更流畅、高效的用户体验。在Android平台上,Kotlin语言凭借其简洁性和功能性赢得了开发社区的广泛支持。特别是Kotlin协程,作为一种轻量级的并发处理方案,使得异步编程变得更加简单和直观。本文将深入探讨Kotlin协程的核心概念、使用场景以及如何将其应用于Android开发中,以提高应用性能和响应能力。通过实际案例分析,我们将展示协程如何简化复杂任务,优化资源管理,并为最终用户提供更加流畅的体验。
|
26天前
|
调度 数据库 Android开发
构建高效Android应用:Kotlin协程的实践与优化
在Android开发领域,Kotlin以其简洁的语法和平台友好性成为了开发的首选语言。其中,Kotlin协程作为处理异步任务的强大工具,它通过提供轻量级的线程管理机制,使得开发者能够在不阻塞主线程的情况下执行后台任务,从而提升应用性能和用户体验。本文将深入探讨Kotlin协程的核心概念,并通过实例演示如何在实际的Android应用中有效地使用协程进行网络请求、数据库操作以及UI的流畅更新。同时,我们还将讨论协程的调试技巧和常见问题的解决方法,以帮助开发者避免常见的陷阱,构建更加健壮和高效的Android应用。
35 4
|
28天前
|
移动开发 Java Android开发
构建高效Android应用:Kotlin协程的实践之路
【2月更文挑战第31天】 在移动开发领域,性能优化和流畅的用户体验一直是开发者追求的目标。随着Kotlin语言的流行,其异步编程解决方案——协程(Coroutines),为Android应用带来了革命性的并发处理能力。本文将深入探讨Kotlin协程的核心概念、设计原理以及在Android应用中的实际应用案例,旨在帮助开发者掌握这一强大的工具,从而提升应用的性能和响应能力。
|
18天前
|
Java Android开发 开发者
构建高效Android应用:Kotlin协程的实践与优化
在响应式编程范式日益盛行的今天,Kotlin协程作为一种轻量级的线程管理解决方案,为Android开发带来了性能和效率的双重提升。本文旨在探讨Kotlin协程的核心概念、实践方法及其在Android应用中的优化策略,帮助开发者构建更加流畅和高效的应用程序。通过深入分析协程的原理与应用场景,结合实际案例,本文将指导读者如何优雅地解决异步任务处理,避免阻塞UI线程,从而优化用户体验。
|
23天前
|
Java 编译器 Android开发
构建高效Android应用:探究Kotlin与Java的性能差异
在开发高性能的Android应用时,选择合适的编程语言至关重要。近年来,Kotlin因其简洁性和功能性受到开发者的青睐,但其性能是否与传统的Java相比有所不足?本文通过对比分析Kotlin与Java在Android平台上的运行效率,揭示二者在编译速度、运行时性能及资源消耗方面的具体差异,并探讨在实际项目中如何做出最佳选择。
17 4
|
2天前
|
移动开发 Android开发 开发者
构建高效Android应用:采用Kotlin进行内存优化的策略
【4月更文挑战第18天】 在移动开发领域,性能优化一直是开发者关注的焦点。特别是对于Android应用而言,由于设备和版本的多样性,确保应用流畅运行且占用资源少是一大挑战。本文将探讨使用Kotlin语言开发Android应用时,如何通过内存优化来提升应用性能。我们将从减少不必要的对象创建、合理使用数据结构、避免内存泄漏等方面入手,提供实用的代码示例和最佳实践,帮助开发者构建更加高效的Android应用。
5 0
|
12天前
|
移动开发 API Android开发
构建高效Android应用:探究Kotlin协程的优势与实践
【4月更文挑战第7天】 在移动开发领域,性能优化和应用响应性的提升一直是开发者追求的目标。近年来,Kotlin语言因其简洁性和功能性在Android社区中受到青睐,特别是其对协程(Coroutines)的支持,为编写异步代码和处理并发任务提供了一种更加优雅的解决方案。本文将探讨Kotlin协程在Android开发中的应用,揭示其在提高应用性能和简化代码结构方面的潜在优势,并展示如何在实际项目中实现和优化协程。
|
26天前
|
移动开发 Android开发 开发者
构建高效Android应用:探究Kotlin协程的优势
在移动开发领域,尤其是针对Android平台,性能优化和流畅的用户体验始终是开发者追求的核心目标。近年来,Kotlin语言凭借其简洁性和功能性成为Android开发的新宠,特别是Kotlin协程的引入,为编写异步代码提供了一种全新的范式。本文将深入探讨Kotlin协程在Android应用开发中的应用及其带来的优势,旨在帮助开发者理解并运用协程来提高应用的性能和响应性。
|
26天前
|
存储 数据库 Android开发
构建高效Android应用:Kotlin协程的全面应用
在现代Android开发中,Kotlin协程作为一种优化异步编程和提升应用性能的强大工具,已经受到越来越多开发者的关注。本文将深入探讨Kotlin协程的核心概念、设计原理以及如何在实际Android项目中实现高效的协程应用。我们将从基础出发,逐步揭示协程如何简化线程管理,提供流畅的代码结构,并通过实际案例分析其在网络请求、数据库操作和UI响应中的应用效果。