MVC,MVP的对比(附代码)一次搞懂!

首先看下我们的页面

一个edittext,一个获取用户信息的button,一个展示用户信息的textview

实现:点击按钮,根据用户输入的账号,获取账号信息并显示在textview上

现在我们用三种方式实现

(1)不使用任何模式

(2)mvc模式

(3)mvp模式

前提:我们定义bean类和callback类

 Account.class
package com.jyr.kotlindemo.designPatterns

//账号信息
 class Account {
    //账号名称
    var accountName: String = ""
    //账号等级
    var level: Int = 1
}
CallBack.class
package com.jyr.kotlindemo.designPatterns

interface CallBack {
    fun onSuccess(account: Account)
    fun onFail()
}
 

1. 不使用任何模式下,页面效果如下

ModelActivity.class
package com.jyr.kotlindemo.designPatterns

import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import android.widget.Button
import android.widget.EditText
import android.widget.TextView
import com.jyr.kotlindemo.R
import com.jyr.kotlindemo.designPatterns.mvc.MVCActivity
import com.jyr.kotlindemo.designPatterns.mvp.MVPActivity
import kotlin.random.Random

public class ModelActivity : AppCompatActivity(), View.OnClickListener {

    private lateinit var etCountId: EditText
    private lateinit var btnGetInfo: Button
    private lateinit var tvUserAccountInfo: TextView
    private lateinit var btnMvcMode: Button
    private lateinit var btnMvpModel: Button

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_model)
        //初始化控件
        etCountId = findViewById<EditText>(R.id.et_account_id)
        btnGetInfo = findViewById<Button>(R.id.btn_get_info)
        tvUserAccountInfo = findViewById<TextView>(R.id.tv_user_account_info)
        btnMvcMode = findViewById<Button>(R.id.btn_mvc_mode)
        btnMvpModel = findViewById(R.id.btn_mvp_mode)
        btnGetInfo.setOnClickListener(this)
        btnMvcMode.setOnClickListener(this)
        btnMvpModel.setOnClickListener(this)
    }

    override fun onClick(v: View?) {
        when (v?.id) {
            R.id.btn_get_info -> {
                getAccountData(getUserInputAccount(), object : CallBack {
                    override fun onSuccess(account: Account) {
                        showSuccessPage(account)
                    }

                    override fun onFail() {
                        showFailPage()
                    }
                })
            }
            R.id.btn_mvc_mode -> startActivity(Intent(this, MVCActivity::class.java))
            R.id.btn_mvp_mode -> startActivity(Intent(this, MVPActivity::class.java))
        }
    }

    //获取用户输入的信息
    fun getUserInputAccount(): String {
        return etCountId.text.toString()
    }

    //展示获取数据成功的界面
    fun showSuccessPage(account: Account) {
        tvUserAccountInfo.setText("用户名为${account.accountName},用户等级为${account.level}")
    }

    //展示获取数据失败的界面
    fun showFailPage() {
        tvUserAccountInfo.setText("获取用户信息失败")
    }

    //请求接口:获取账户的数据(次数模拟网络请求)
    fun getAccountData(accountName: String, callBack: CallBack) {
        //随机一个boolean
        val random = Random
        val nextBoolean = random.nextBoolean()
        if (nextBoolean) {
            //成功的话,返回用户信息
            val account = Account()
            account.accountName = accountName
            account.level = 100
            callBack.onSuccess(account)
        } else {
            callBack.onFail()
        }
    }
}
activity_model.xml
<?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"
    android:padding="@dimen/dp_20"
    tools:context=".designPatterns.ModelActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="@color/black"
        android:textSize="@dimen/sp_18"
        android:text="不使用任何模式下" />

    <EditText
        android:id="@+id/et_account_id"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="请输入账号"
        android:maxLines="1" />

    <Button
        android:id="@+id/btn_get_info"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="@dimen/dp_10"
        android:text="获取用户信息" />

    <TextView
        android:id="@+id/tv_user_account_info"
        android:text="用户信息:"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="@dimen/dp_20" />

    <Button
        android:id="@+id/btn_mvc_mode"
        android:layout_marginTop="@dimen/dp_40"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="MVC模式" />

    <Button
        android:id="@+id/btn_mvp_mode"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="MVP模式" />

</LinearLayout>

2.使用mvc模式

MVCModel.class
package com.jyr.kotlindemo.designPatterns.mvc

import com.jyr.kotlindemo.designPatterns.Account
import com.jyr.kotlindemo.designPatterns.CallBack
import kotlin.random.Random

class MVCModel {
    //请求接口:获取账户的数据(次数模拟网络请求)
    public fun getAccountData(accountName: String, callBack: CallBack) {
        //随机一个boolean
        val random = Random
        val nextBoolean = random.nextBoolean()
        if (nextBoolean) {
            //成功的话,返回用户信息
            val account = Account()
            account.accountName = accountName
            account.level = 100
            callBack.onSuccess(account)
        } else {
            callBack.onFail()
        }
    }
}
MVCActivity.class
package com.jyr.kotlindemo.designPatterns.mvc

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import android.widget.Button
import android.widget.EditText
import android.widget.TextView
import com.jyr.kotlindemo.R
import com.jyr.kotlindemo.designPatterns.Account
import com.jyr.kotlindemo.designPatterns.CallBack

/**
 * MVC
 * model (请求网络数据) !!!!!!!!!!!!!新建model类
 * view (指的layout,view控件)
 * controller (指的activity,fragment)
 *
 * 1.需要新建一个接口,callback,里边定义onSuccess和onFail方法
 * 2.新建model类(来请求网络数据),里边getData方法,传进去一个callback,如果数据请求成功的话,就回调onSuccess,失败就回调onFail
 * 3.在activity中声明Model,并初始化,在需要网络请求的地方,通过model.getData,在onSuccess和onFail的回调里,设置UI
 * */
class MVCActivity : AppCompatActivity(), View.OnClickListener {

    private lateinit var etCountId: EditText
    private lateinit var btnGetInfo: Button
    private lateinit var tvUserAccountInfo: TextView
    private lateinit var mvcModel: MVCModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_m_v_c)
        //初始化控件
        etCountId = findViewById<EditText>(R.id.et_account_id)
        btnGetInfo = findViewById<Button>(R.id.btn_get_info)
        tvUserAccountInfo = findViewById<TextView>(R.id.tv_user_account_info)
        btnGetInfo.setOnClickListener(this)
        mvcModel = MVCModel()
    }

    override fun onClick(v: View?) {
        when (v?.id) {
            R.id.btn_get_info -> {
                mvcModel.getAccountData(getUserInputAccount(), object : CallBack {
                    override fun onSuccess(account: Account) {
                        showSuccessPage(account)
                    }

                    override fun onFail() {
                        showFailPage()
                    }
                })
            }
        }
    }

    //获取用户输入的信息
    fun getUserInputAccount(): String {
        return etCountId.text.toString()
    }

    //展示获取数据成功的界面
    fun showSuccessPage(account: Account) {
        tvUserAccountInfo.setText("用户名为${account.accountName},用户等级为${account.level}")
    }

    //展示获取数据失败的界面
    fun showFailPage() {
        tvUserAccountInfo.setText("获取用户信息失败")
    }
}
activity_m_v_c.xml
<?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"
    android:padding="@dimen/dp_20"
    tools:context=".designPatterns.ModelActivity">

    <TextView
        android:id="@+id/tv_mode_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="MVC模式下"
        android:textColor="@color/black"
        android:textSize="@dimen/sp_18" />

    <EditText
        android:id="@+id/et_account_id"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="请输入账号"
        android:maxLines="1" />

    <Button
        android:id="@+id/btn_get_info"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="@dimen/dp_20"
        android:text="获取用户信息" />

    <TextView
        android:id="@+id/tv_user_account_info"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="@dimen/dp_20"
        android:text="用户信息:" />

</LinearLayout>

3.mvp模式

MVPModel.class
package com.jyr.kotlindemo.designPatterns.mvp

import com.jyr.kotlindemo.designPatterns.Account
import com.jyr.kotlindemo.designPatterns.CallBack
import kotlin.random.Random

class MVPModel {
    //请求接口:获取账户的数据(次数模拟网络请求)
    public fun getAccountData(accountName: String, callBack: CallBack) {
        //随机一个boolean
        val random = Random
        val nextBoolean = random.nextBoolean()
        if (nextBoolean) {
            //成功的话,返回用户信息
            val account = Account()
            account.accountName = accountName
            account.level = 100
            callBack.onSuccess(account)
        } else {
            callBack.onFail()
        }
    }
}
IMVPView接口
package com.jyr.kotlindemo.designPatterns.mvp

import com.jyr.kotlindemo.designPatterns.Account

interface IMVPView {

    //获取用户输入的信息
    fun getUserInputAccount(): String

    //展示获取数据成功的界面
    fun showSuccessPage(account: Account)

    //展示获取数据失败的界面
    fun showFailPage()
}
MVPPresenter.class
package com.jyr.kotlindemo.designPatterns.mvp

import com.jyr.kotlindemo.designPatterns.Account
import com.jyr.kotlindemo.designPatterns.CallBack

class MVPPresenter {

    private var mvpModel: MVPModel
    private var imvpView: IMVPView

    constructor(imvpView: IMVPView) {
        this.imvpView = imvpView
        mvpModel = MVPModel()
    }

    fun getData(account: String) {
        mvpModel.getAccountData(account, object : CallBack {
            override fun onSuccess(account: Account) {
                imvpView.showSuccessPage(account)
            }

            override fun onFail() {
                imvpView.showFailPage()
            }

        })
    }
}
MVPActivity.class
package com.jyr.kotlindemo.designPatterns.mvp

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import android.widget.Button
import android.widget.EditText
import android.widget.TextView
import com.jyr.kotlindemo.R
import com.jyr.kotlindemo.designPatterns.Account
import com.jyr.kotlindemo.designPatterns.CallBack
import com.jyr.kotlindemo.designPatterns.mvc.MVCModel

/**
 * MVP
 * model (请求网络数据) !!!!!!!!!!!!!新建model类
 * view (指的layout,view控件)!!!!!!!!!!新建view类
 * presenter (指的activity,fragment)!!!!!!m层和p层的桥梁
 *
 * m层和v层不直接交互,通过presenter来作为沟通的桥梁
 * 用户点击页面,p通知m去请求数据,m请求数据完成后把结果回调给p,p再告诉v,v再去显示页面
 * 全程m和v层没有直接交流
 *
 * 1.需要新建一个接口,callback,里边定义onSuccess和onFail方法
 * 2.新建model类(来请求网络数据),里边getData方法,传进去一个callback,如果数据请求成功的话,就回调onSuccess,失败就回调onFail
 * 3.新建IView接口,里边定义页面显示的一些方法(showSuccessPage,showFailPage)
 * 4.新建一个presenter类,声明model和view,在构造方法里,传进来参数view,实例化model
 *   创建一个getData的方法,里边调用model的getData方法,然后在成功和失败的回调里,用view层设置页面
 * 5.在activity里声明Presenter,并初始化,在需要网络请求的地方,通过Presenter.getData
 *  在activity中实现view接口,重写它的方法,在这些重写的方法里设置UI
 *
 *
 *  MVC和MVP的区别(mvc和mvp的优缺点)
 *  mvc的话,m和v还是可以直接交互的,代码还是耦合比较高,一些逻辑简单的页面可以使用mvc
 *  mvp的话,例如点击页面,v通知p,p通知m去获取数据,m获取数据之后把结果传给p,p再传给v
 *           m层和v层不能直接交互,他们之间需要通过presenter来作为桥梁沟通
 *          降低了代码的耦合性,但是也会增加一些类
 *
 *
 *   MVVM的话,就是在MVP的基础上,增加了数据的双向绑定,数据绑定是通过databinding来实现的
 * */
class MVPActivity : AppCompatActivity(), View.OnClickListener, IMVPView {
    private lateinit var tvModeName: TextView
    private lateinit var etCountId: EditText
    private lateinit var btnGetInfo: Button
    private lateinit var tvUserAccountInfo: TextView
    private lateinit var mvpPresenter: MVPPresenter

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_m_v_c)
        //初始化控件
        tvModeName = findViewById(R.id.tv_mode_name)
        etCountId = findViewById<EditText>(R.id.et_account_id)
        btnGetInfo = findViewById<Button>(R.id.btn_get_info)
        tvUserAccountInfo = findViewById<TextView>(R.id.tv_user_account_info)
        btnGetInfo.setOnClickListener(this)
        tvModeName.setText("MVP模式")
        mvpPresenter = MVPPresenter(this)
    }

    override fun onClick(v: View?) {
        when (v?.id) {
            R.id.btn_get_info -> {
                mvpPresenter.getData(getUserInputAccount())
            }
        }
    }

    override fun getUserInputAccount(): String {
        return etCountId.text.toString()
    }

    override fun showSuccessPage(account: Account) {
        tvUserAccountInfo.setText("用户名为${account.accountName},用户等级为${account.level}")
    }

    override fun showFailPage() {
        tvUserAccountInfo.setText("获取用户信息失败")
    }
}
activity_m_v_c.xml
<?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"
    android:padding="@dimen/dp_20"
    tools:context=".designPatterns.ModelActivity">

    <TextView
        android:id="@+id/tv_mode_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="MVC模式下"
        android:textColor="@color/black"
        android:textSize="@dimen/sp_18" />

    <EditText
        android:id="@+id/et_account_id"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="请输入账号"
        android:maxLines="1" />

    <Button
        android:id="@+id/btn_get_info"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="@dimen/dp_20"
        android:text="获取用户信息" />

    <TextView
        android:id="@+id/tv_user_account_info"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="@dimen/dp_20"
        android:text="用户信息:" />

</LinearLayout>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/443211.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Unity性能优化篇(十) 模型优化之网格合并 Easy Mesh Combine Tool插件使用以及代码实现网格合并

把多个模型的网格合并为一个网格。可以使用自己写代码&#xff0c;使用Unity自带的CombineMeshes方法&#xff0c;也可以使用资源商店的插件&#xff0c;在资源商店搜Mesh Combine可以搜索到相关的插件&#xff0c;例如Easy Mesh Combine Tool等插件。 可大幅度减少Batches数量…

Nginx实现高并发

注&#xff1a;文章是4年前在自己网站上写的&#xff0c;迁移过来了。现在看我之前写的这篇文章&#xff0c;描述得不是特别详细&#xff0c;但描述了Nginx的整体架构思想。如果对Nginx玩得透得或者想了解深入的&#xff0c;可以在网上找找其他的文章。 ......................…

C++顺序结构实例

1.计算浮点数相除的余数 计算两个双精度浮点数a和b相除的余数,a和b都是双精度浮点数。这里的余数r的定义是: a=k * 吧+r,其中k是整数,0<=r<b。 输入 一行,包括两个双精度浮点数a和b 输出 一行,a➗b的余数 样例输入 73.263 0.9973 样例输出 0.4301 #i…

机器学习--循环神经网络(RNN)3

本篇文章结合具体的例子来介绍一下LSTM运算方式以及原理。请结合上篇文章的介绍食用。 一、具体例子 如上图所示&#xff0c;网络里面只有一个 LSTM 的单元&#xff0c;输入都是三维的向量&#xff0c;输出都是一维的输出。 这三维的向量跟输出还有记忆元的关系是这样的。 假设…

小小磁珠对EMC的作用竟然这么大?

磁珠&#xff0c;作为一种电感型的EMI静噪滤波器&#xff0c;其外观与电感颇为相似。目前&#xff0c;应用最为广泛的磁珠类型是铁氧体磁珠&#xff0c;也称作Ferrite Bead。它的度量单位是欧姆&#xff0c;根据型号的不同&#xff0c;磁珠能够抑制的频率范围广泛&#xff0c;覆…

考研数学——高数:重积分

直角坐标系下二重积分 助记1&#xff1a; 因为一重积分求出的是二维平面的面积&#xff0c;类比得到二重积分得到的是三维的体积 而用之前求旋转体体积的思路&#xff1a;已知截面面积可求得体积。来表示二重积分 在控制一个变量不变&#xff08;x / y&#xff09;时&#x…

AVL树讲解

AVL树 1. 概念2. AVL节点的定义3. AVL树插入3.1 旋转 4.AVL树的验证 1. 概念 AVL树是一种自平衡二叉搜索树。它的每个节点的左子树和右子树的高度差&#xff08;平衡因子&#xff0c;我们这里按右子树高度减左子树高度&#xff09;的绝对值不超过1。AVL的左子树和右子树都是AV…

论文阅读笔记 | MetaIQA: Deep Meta-learning for No-Reference Image Quality Assessment

文章目录 文章题目发表年限期刊/会议名称论文简要动机主要思想或方法架构实验结果 文章链接&#xff1a;https://doi.org/10.48550/arXiv.2004.05508 文章题目 MetaIQA: Deep Meta-learning for No-Reference Image Quality Assessment 发表年限 2020 期刊/会议名称 Publi…

vue router 解决路由带参数跳转时出现404问题

我的页面是从一个vue页面router跳转到另一个vue页面&#xff0c;并且利用windows.open() 浏览器重新创建一个页签。但是不知道为什么有时候可以有时候又不行&#xff0c;经过反复测试与分析&#xff0c;最终发现是因为有一个参数的值里包含了小数点., 小数点是浏览器合法字符&a…

visualization_msgs::Marker 的pose设置,map坐标系的3d box显示问题

3D框显示 3D框显示可以使用visualization_msgs::Marker::LINE_LIST或者LINE_STRIP&#xff0c;前者使用方法需要指明线的两个端点&#xff0c;后者自动连接相邻两个点。 姿态问题 网上看了一些&#xff0c;没有涉及到朝向设置&#xff0c;Pose.orientation默认构造为4个0 至…

域控操作十:安装包exe转msi软件下发

需要的文件 Advanced Installer 软件用来将exe转换成msi因为域控只能下发msi格式 一个exe安装包这里拿微信举例 一个没有密码的共享文件夹 1.exe转MSI 2&#xff0c;开始下发 服务器和用户刷新策略 #完成

解决方案TypeError: string indices must be integers

文章目录 一、现象&#xff1a;二、解决方案 一、现象&#xff1a; PyTorch深度学习框架&#xff0c;运行bert-mini&#xff0c;本地环境是torch1.4-gpu&#xff0c;发现报错显示&#xff1a;TypeError: string indices must be integers 后面报字符问题&#xff0c;百度过找…

【附教程】2024,人工智能+声音,看这里就够了~16款AI音乐/音频/音效,声音克隆等ai软件与工具大合集~

AI音乐音频领域的技术正在迅速发展&#xff0c;为音乐创作和编辑带来了革命性的改变。这些技术通过深度学习和生成式模型&#xff0c;能够理解并模仿音乐的复杂结构和情感&#xff0c;从而创作出高质量的音乐作品。 AI音乐音频技术使得音乐创作变得更加高效和便捷。创作者只需…

Unity DropDown 组件 详解

Unity版本 2022.3.13f1 Dropdown下拉菜单可以快速创建大量选项 一、 Dropwon属性详解 属性&#xff1a;功能&#xff1a;Interactable此组件是否接受输入&#xff1f;请参阅 Interactable。Transition确定控件以何种方式对用户操作进行可视化响应的属性。请参阅过渡选项。Nav…

CodeSys通过C函数接口调用Qt

建议先查看之前的文章【CodeSys中调用C语言写的动态库】&#xff0c;了解如何创建一个能够被codesys调用的动态库。 假如想要在函数中使用Qt或者第三方库&#xff08;比如opencv等&#xff09;&#xff0c;可以在其自动生成的makefile文件中设置好相应的参数。 比如我这里就是…

洗地机怎么选|洗地机哪款好用?添可、希亦、美的洗地机哪个最耐用质量好?

在现代生活中&#xff0c;屋内清洁是一项必不可少的工作&#xff0c;但也是一项费时费力的工作。随着科技的进步&#xff0c;家庭清洁工具也正经历着革命性的变革。洗地机&#xff0c;一种集吸尘、拖地、清洗于一体的智能家居清洁工具&#xff0c;正逐渐成为现代家庭必备的家电…

鸿蒙Harmony应用开发—ArkTS声明式开发(基础手势:ImageSpan)

Text组件的子组件&#xff0c;用于显示行内图片。 说明&#xff1a; 该组件从API Version 10开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本。 子组件 无 接口 ImageSpan(value: ResourceStr | PixelMap) 参数&#xff1a; 参数名参数类…

后量子时代,未来密码该何去何从?

古有飞鸽&#xff0c;现有网络&#xff0c;在知识经济为基础的信息化社会中&#xff0c;保障网络信息安全无疑成为成为国与国之间无形的较量。小到个人通讯&#xff0c;大到机要信息传输&#xff0c;信息安全对于国家安全和经济活动正常运转至关重要。密码学作为保障网络与信息…

消息队列以及Kafka的使用

什么是消息队列 消息队列&#xff1a;一般我们会简称它为MQ(Message Queue)。其主要目的是通讯。 ps&#xff1a;消息队列是以日志的形式将数据顺序存储到磁盘当中。通常我们说从内存中IO读写数据的速度要快于从硬盘中IO读写的速度是对于随机的写入和读取。但是对于这种顺序存…

QGridLayout网格布局和QVBoxLayout垂直布局有着非常大的差别

QGridLayout网格布局&#xff1a;1.把这块控件划分成一个个的 单元格 2.把你的控件填充进入 单元格 3.这些有关限制大小的函数接口统统失效 setMaximumWidth&#xff08;&#xff09; setMinimumWidth() setPolicySize()图示&#xff1a;我是用的网格布局&#xff0c;左边放QT…