View->Bitmap缩放到自定义ViewGroup的任意区域(Matrix方式绘制Bitmap)

Bitmap缩放和平移

  • 加载一张Bitmap可能为宽高相同的正方形,也可能为宽高不同的矩形
  • 缩放方向可以为中心缩放,左上角缩放,右上角缩放,左下角缩放,右下角缩放
  • Bitmap中心缩放,包含了缩放和平移两个操作,不可拆开
  • Bitmap其余四个方向的缩放,可以单独缩放不带平移,也可以缩放带平移

XML文件

<?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="match_parent"
    android:orientation="vertical">

    <com.yang.app.MyRelativeLayout
        android:id="@+id/real_rl"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:layout_marginTop="30dp"
        android:layout_marginBottom="30dp"
        android:layout_marginLeft="30dp"
        android:layout_marginRight="30dp"
        android:background="@color/gray">
        <com.yang.app.MyImageView
            android:id="@+id/real_iv"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_marginTop="30dp"
            android:layout_marginBottom="30dp"
            android:layout_marginLeft="30dp"
            android:layout_marginRight="30dp" />
    </com.yang.app.MyRelativeLayout>

    <ImageView
        android:layout_width="match_parent"
        android:layout_height="200dp"
        android:layout_weight="0"
        android:background="#00ff00" />
</LinearLayout>

Activity代码

const val TAG = "Yang"
class MainActivity : AppCompatActivity() {
    var mRealView : MyImageView ?= null
    var mRelativeLayout : MyRelativeLayout ?= null
    var tempBitmap : Bitmap ?= null
    var mHandler = Handler(Looper.getMainLooper())
    var screenWidth = 0
    var screenHeight = 0
    var srcRect = RectF()
    var destRect = RectF()
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        mRealView = findViewById(R.id.real_iv)
        mRelativeLayout = findViewById(R.id.real_rl)
        // 屏幕宽高的一半作为临时RectF, 用于压缩Bitmap
        screenWidth = resources.displayMetrics.widthPixels
        screenHeight = resources.displayMetrics.heightPixels
        val tempRect = RectF(0f, 0f, screenWidth.toFloat() / 2, screenHeight.toFloat() / 2)
        
        
        CoroutineScope(Dispatchers.IO).launch {
            tempBitmap = getBitmap(resources, tempRect, R.drawable.fake)
            withContext(Dispatchers.Main) {
                mRelativeLayout?.post {
                    // 获取初始区域的RectF
                    srcRect.set(
                        0f,
                        0f,
                        mRelativeLayout?.width?.toFloat()!!,
                        mRelativeLayout?.height?.toFloat()!!
                    )
                    // 获取结束区域的RectF
                    mRelativeLayout?.forEach { childView ->
                        if (childView is MyImageView) {
                            destRect.set(
                                0f,
                                0f,
                                 childView.width.toFloat(),
                                childView.height.toFloat()
                            )
                        }
                    }
                    scaleRectFun(tempBitmap, srcRect, destRect)
                }
            }
        }
    }
    
    fun scaleRectFun(tempBitmap: Bitmap?, srcRect : RectF, destRect: RectF){
        tempBitmap?.let { bitmap->
            mRelativeLayout?.setBitmap(bitmap)
            val animator = ValueAnimator.ofFloat(0f, 1f)
            animator.duration = 5000L
            animator.interpolator = DecelerateInterpolator()
            animator.addUpdateListener {
                val value = it.animatedValue as Float
                // 中心缩放
                mRelativeLayout?.setDestRectCenterWithTranslate(srcRect, destRect, value)
                // 左上角不带平移缩放
                // mRelativeLayout?.setDestRectLeftTopNoTranslate(srcRect, destRect, value)
                // 左上角平移缩放
                // mRelativeLayout?.setDestRectLeftTopWithTranslate(srcRect, destRect, value)
                // 右上角不带平移缩放
                // mRelativeLayout?.setDestRectRightTopNoTranslate(srcRect, destRect, value)
                // 右上角平移缩放
                // mRelativeLayout?.setDestRectRightTopWithTranslate(srcRect, destRect, value)
                // 左下角不带平移缩放
                // mRelativeLayout?.setDestRectLeftBottomNoTranslate(srcRect, destRect, value)
                // 左下角平移缩放
                // mRelativeLayout?.setDestRectLeftBottomWithTranslate(srcRect, destRect, value)
                // 右下角不带平移缩放
                // mRelativeLayout?.setDestRectRightBottomNoTranslate(srcRect, destRect, value)
                // 右下角平移缩放
                // mRelativeLayout?.setDestRectRightBottomWithTranslate(srcRect, destRect, value)
            }
            animator.start()
        }
    }
}
fun getBitmap(resources : Resources, destRect : RectF, imageId: Int): Bitmap? {
    var imageWidth = -1
    var imageHeight = -1
    val preOption = BitmapFactory.Options().apply {
        // 只获取图片的宽高
        inJustDecodeBounds = true
        BitmapFactory.decodeResource(resources, imageId, this)
    }
    imageWidth = preOption.outWidth
    imageHeight = preOption.outHeight
    // 计算缩放比例
    val scaleMatrix = Matrix()
    // 确定未缩放Bitmap的RectF
    var srcRect = RectF(0f, 0f, imageWidth.toFloat(), imageHeight.toFloat())
    // 通过目标RectF, 确定缩放数值,存储在scaleMatrix中
    scaleMatrix.setRectToRect(srcRect, destRect, Matrix.ScaleToFit.CENTER)
    // 缩放数值再映射到原始Bitmap上,得到缩放后的RectF
    scaleMatrix.mapRect(srcRect)

    val finalOption = BitmapFactory.Options().apply {
        if (imageHeight > 0 && imageWidth > 0) {
            inPreferredConfig = Bitmap.Config.RGB_565
            inSampleSize = calculateInSampleSize(
                imageWidth,
                imageHeight,
                srcRect.width().toInt(),
                srcRect.height().toInt()
            )
        }
    }
    return BitmapFactory.decodeResource(resources, imageId, finalOption)
}

fun calculateInSampleSize(fromWidth: Int, fromHeight: Int, toWidth: Int, toHeight: Int): Int {
    var bitmapWidth = fromWidth
    var bitmapHeight = fromHeight
    if (fromWidth > toWidth|| fromHeight > toHeight) {
        var inSampleSize = 2
        // 计算最大的inSampleSize值,该值是2的幂,并保持原始宽高大于目标宽高
        while (bitmapWidth >= toWidth && bitmapHeight >= toHeight) {
            bitmapWidth /= 2
            bitmapHeight /= 2
            inSampleSize *= 2
        }
        return inSampleSize
    }
    return 1
}

自定义ViewGroupView代码

class MyImageView @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : AppCompatImageView(context, attrs, defStyleAttr)


class MyRelativeLayout @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : RelativeLayout(context, attrs, defStyleAttr) {
    var mBitmap : Bitmap ?= null
    var mScaleMatrix = Matrix()
    var mDestRect = RectF()

    fun setBitmap(bitmap: Bitmap?) {
        mBitmap = bitmap
    }

    override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)
        mBitmap?.let {
            canvas?.drawBitmap(it, mScaleMatrix, Paint().apply {
                isAntiAlias = true
            })
        }
    }
}

中心缩放

  • ValueAnimator动画的初始值为0,开始区域为外层MyRelativeLayout区域,结束区域为内层MyImageView区域,缩放区域在这两个区域大小之间变化
  • 缩放比例取决于当前缩放区域mDestRectBitmap宽高的最小宽高比
fun setDestRectCenterWithTranslate(srcRect: RectF, destRect: RectF, value : Float) {
    mDestRect = RectF(
        srcRect.left + (destRect.left - srcRect.left) * value,
        srcRect.top  + (destRect.top - srcRect.top) * value,
        srcRect.right + (destRect.right - srcRect.right) * value,
        srcRect.bottom + (destRect.bottom - srcRect.bottom) * value
    )
    val scale = Math.min(mDestRect.width() / mBitmap?.width!!, mDestRect.height() / mBitmap!!.height)
    val dx = (srcRect.width() - mBitmap?.width!! * scale) / 2
    val dy = (srcRect.height() - mBitmap?.height!! * scale) / 2
    mScaleMatrix.reset()
    mScaleMatrix.postScale(scale, scale)
    mScaleMatrix.postTranslate(dx, dy)
    invalidate()
}
  • 中心缩放效果图
    在这里插入图片描述

左上角缩放

  • 左上角不带平移缩放
fun setDestRectLeftTopNoTranslate(srcRect: RectF, destRect: RectF, value : Float) {
    mDestRect = RectF(
        srcRect.left + (destRect.left - srcRect.left) * value,
        srcRect.top + (destRect.top - srcRect.top) * value,
        srcRect.right + (destRect.right - srcRect.right) * value,
        srcRect.bottom + (destRect.bottom - srcRect.bottom) * value
    )
    val scale = Math.min(mDestRect.width() / mBitmap?.width!!, mDestRect.height() / mBitmap!!.height)
    mScaleMatrix.setScale(scale, scale)
    invalidate()
}
  • 左上角不带平移缩放效果图
    在这里插入图片描述

  • 左上角带平移缩放

fun setDestRectLeftTopWithTranslate(srcRect: RectF, destRect: RectF, value : Float) {
    mDestRect = RectF(
        srcRect.left + (destRect.left - srcRect.left) * value,
        srcRect.top + (destRect.top - srcRect.top) * value,
        srcRect.right + (destRect.right - srcRect.right) * value,
        srcRect.bottom + (destRect.bottom - srcRect.bottom) * value
    )
    val scale = Math.min(mDestRect.width() / mBitmap?.width!!, mDestRect.height() / mBitmap!!.height)
    val dx = ((srcRect.width() - destRect.width())/2) * value + (destRect.left - srcRect.left) * value
    val dy = ((srcRect.height() - destRect.height())/2) * value + (destRect.top - srcRect.top) * value
    mScaleMatrix.reset()
    mScaleMatrix.postScale(scale, scale)
    mScaleMatrix.postTranslate(dx, dy)
    invalidate()
}
  • 左上角带平移缩放效果图
    在这里插入图片描述

右上角缩放

  • 右上角不带平移缩放
fun setDestRectRightTopNoTranslate(srcRect: RectF, destRect: RectF, value : Float) {
    mDestRect = RectF(
        srcRect.left  + (destRect.left - srcRect.left) * value,
        srcRect.top + (destRect.top - srcRect.top) * value,
        srcRect.right + (destRect.right - srcRect.right) * value,
        srcRect.bottom + (destRect.bottom - srcRect.bottom) * value
    )
    val scale = Math.min(mDestRect.width() / mBitmap?.width!!, mDestRect.height() / mBitmap!!.height)
    val dx = srcRect.width() - mBitmap!!.width * scale
    val dy = 0f
    mScaleMatrix.reset()
    mScaleMatrix.postScale(scale, scale)
    mScaleMatrix.postTranslate(dx, dy)
    invalidate()
}
  • 右上角不带平移缩放效果图
    在这里插入图片描述
  • 右上角带平移缩放
fun setDestRectRightTopWithTranslate(srcRect: RectF, destRect: RectF, value : Float) {
    mDestRect = RectF(
        srcRect.left  + (destRect.left - srcRect.left) * value,
        srcRect.top + (destRect.top - srcRect.top) * value,
        srcRect.right + (destRect.right - srcRect.right) * value,
        srcRect.bottom + (destRect.bottom - srcRect.bottom) * value
    )
    val scale = Math.min(mDestRect.width() / mBitmap?.width!!, mDestRect.height() / mBitmap!!.height)
    val dx = ((srcRect.width() - destRect.width())/2) * value + (destRect.left - srcRect.left) * value
    val dy = ((srcRect.height() - destRect.height())/2) * value + (destRect.top - srcRect.top) * value
    mScaleMatrix.reset()
    mScaleMatrix.postScale(scale, scale)
    mScaleMatrix.postTranslate(dx, dy)
    invalidate()
}
  • 右上角不带平移缩放效果图
    在这里插入图片描述

左下角缩放

  • 左下角不带平移缩放
fun setDestRectLeftBottomNoTranslate(srcRect: RectF, destRect: RectF, value : Float) {
    mDestRect = RectF(
        srcRect.left + (destRect.left - srcRect.left) * value,
        srcRect.top  + (destRect.top - srcRect.top) * value,
        srcRect.right + (destRect.right - srcRect.right) * value,
        srcRect.bottom + (destRect.bottom - srcRect.bottom) * value
    )
    val scale = Math.min(mDestRect.width() / mBitmap?.width!!, mDestRect.height() / mBitmap!!.height)
    val dx = 0f
    val dy = srcRect.height() - mBitmap?.height!! * scale
    mScaleMatrix.reset()
    mScaleMatrix.postScale(scale, scale)
    mScaleMatrix.postTranslate(dx, dy)
    invalidate()
}
  • 左下角不带平移缩放效果图
    在这里插入图片描述
  • 左下角平移缩放
fun setDestRectLeftBottomWithTranslate(srcRect: RectF, destRect: RectF, value : Float) {
    mDestRect = RectF(
        srcRect.left + (destRect.left - srcRect.left) * value,
        srcRect.top  + (destRect.top - srcRect.top) * value,
        srcRect.right + (destRect.right - srcRect.right) * value,
        srcRect.bottom + (destRect.bottom - srcRect.bottom) * value
    )
    val scale = Math.min(mDestRect.width() / mBitmap?.width!!, mDestRect.height() / mBitmap!!.height)
    val dx = ((srcRect.width() - destRect.width())/2 )* value
    val dy = ((srcRect.height() - destRect.height())/2 )* value + (destRect.bottom - srcRect.bottom) * value + (srcRect.height()- mBitmap?.height!! * scale)
    mScaleMatrix.reset()
    mScaleMatrix.postScale(scale, scale)
    mScaleMatrix.postTranslate(dx, dy)
    invalidate()
}
  • 左下角平移缩放效果图
    在这里插入图片描述

右下角缩放

  • 右下角不带平移缩放
fun setDestRectRightBottomNoTranslate(srcRect: RectF, destRect: RectF, value : Float) {
    mDestRect = RectF(
        srcRect.left + (destRect.left - srcRect.left) * value,
        srcRect.top  + (destRect.top - srcRect.top) * value,
        srcRect.right + (destRect.right - srcRect.right) * value,
        srcRect.bottom + (destRect.bottom - srcRect.bottom) * value
    )
    val scale = Math.min(mDestRect.width() / mBitmap?.width!!, mDestRect.height() / mBitmap!!.height)
    val dx = srcRect.width() - mBitmap!!.width * scale
    val dy = srcRect.height() - mBitmap?.height!! * scale
    mScaleMatrix.reset()
    mScaleMatrix.postScale(scale, scale)
    mScaleMatrix.postTranslate(dx, dy)
    invalidate()
}
  • 右下角不带平移缩放效果图

在这里插入图片描述

  • 右下角平移缩放
fun setDestRectRightBottomWithTranslate(srcRect: RectF, destRect: RectF, value : Float) {
    mDestRect = RectF(
        srcRect.left + (destRect.left - srcRect.left) * value,
        srcRect.top  + (destRect.top - srcRect.top) * value,
        srcRect.right + (destRect.right - srcRect.right) * value,
        srcRect.bottom + (destRect.bottom - srcRect.bottom) * value
    )
    val scale = Math.min(mDestRect.width() / mBitmap?.width!!, mDestRect.height() / mBitmap!!.height)
    val dx = ((srcRect.width() - destRect.width())/2 )* value
    val dy = ((srcRect.height() - destRect.height())/2 )* value + (destRect.bottom - srcRect.bottom) * value + (srcRect.height()- mBitmap?.height!! * scale)
    mScaleMatrix.reset()
    mScaleMatrix.postScale(scale, scale)
    mScaleMatrix.postTranslate(dx, dy)
    invalidate()
}
  • 右下角平移缩放效果图
    在这里插入图片描述

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

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

相关文章

Java进阶学习笔记32——Calendar

为什么要学习Calendar&#xff1f; 原来的方法&#xff1a; 使用Calendar 从API文档中&#xff0c;可以看到Calendar是一个抽象类&#xff0c;抽象类是不能创建对象&#xff0c;不能直接使用的。 package cn.ensource.d3_time;import java.util.Calendar; import java.util.Da…

基于Raspi的Opencv-Python开发笔记

本文所有未强调 “windows终端” 的 “终端”字眼&#xff0c;都是默认树莓派的终端 系统版本 系统版本有必要强调一下&#xff0c;因为不同版本很多操作需要修改 在终端输入uname -a Release就是版本号&#xff0c;Codename是版本名 以下操作仅在此版本验证可行 使能摄像…

传感器和变送器的区别介绍

从它的名称来看&#xff0c;传与感二字。传是指传输&#xff0c;感是指感知。实际上是先有感知&#xff0c;其次转换&#xff0c;最后传输。因此传输是目的&#xff0c;转换是手段&#xff0c;感知是基础。把能够将被测变量&#xff08;温度、压力、液位、流量&#xff09;感知…

内存管理【C++】

内存分布 C中的内存区域主要有以下5种 栈&#xff08;堆栈&#xff09;&#xff1a;存放非静态局部变量/函数参数/函数返回值等等&#xff0c;栈是向下增长的【地址越高越先被使用】。栈区内存的开辟和销毁由系统自动执行 堆&#xff1a;用于程序运行时动态内存分配&#xff…

C++入门3——类与对象2(类的6个默认成员函数)

目录 1.类的6个默认成员函数 2. 构造函数 2.1 构造函数的概念 2.2 构造函数的特性 3. 析构函数 3.1 析构函数的概念 3.2 析构函数的特性 4.拷贝构造函数 4.1 拷贝构造函数的概念 4.2 拷贝构造函数的特性 5.赋值运算符重载函数 5.1运算符重载函数 5.2 赋值运算符重…

docker一键部署EFK系统(elasticsearch filebeat kibana metricbeat es-head)

EFK日志系统搭建 EFK日志系统介绍功能需求搭建elasticsearch集群规划前提部署核对证书及权限 EFK日志系统介绍 Elasticsearch 是一个实时的、分布式的可扩展的搜索引擎&#xff0c;允许进行全文、结构化搜索&#xff0c;它通常用于索引和搜索大量日志数据&#xff0c;也可用于…

Vue3-路由详解

文章目录 路由对路由的理解安装 Vue Router基本切换效果两个注意点路由器工作模式to的两种写法命名路由嵌套路由路由传参query参数params参数 路由的props配置replace属性编程式导航重定向 更多相关内容可查看 路由 附git地址&#xff1a;https://gitee.com/its-a-little-bad/…

打印机的ip不同且连不上

打印机的ip不同且连不上 1.问题分析2.修改网段3.验证网络 1.问题分析 主要是打印机的网段和电脑不在同一个网段 2.修改网段 3.验证网络

CATO原理中的数学与魔术(十一)——Parity Principle及其应用二:集合的可视化...

早点关注我&#xff0c;精彩不错过&#xff01; 上篇文章中&#xff0c;我们已经进入了CATO原理魔术介绍的深水区&#xff0c;是第3个系列Parity Principle中集合性质的章节&#xff0c;聊到了关于张数和求和集合性质&#xff0c;并对性质之间的偏序关系&#xff0c;性质之间的…

基于单片机的微型嵌入式温度测量仪的设计与实现分析

摘要 &#xff1a; 作为信息技术中重要的技术手段之一嵌入式单片机系统已经被应用到越来越多不同的行业领域中。如&#xff0c;各种手持监测设备、智能家电设备等。当前展开对单片机的微型嵌入式温度测量仪的设计和实现研究&#xff0c;从微型嵌入式单片机相关理论入手&#xf…

桶形畸变和枕形畸变

桶形畸变和枕形畸变是两种常见的光学畸变现象&#xff0c;主要发生在使用广角镜头或远摄镜头拍摄时。这些畸变是因为镜头的光学特性不能完美地将光线汇聚到一个共同的焦点上&#xff0c;导致图像的不同部分在形状上发生扭曲。下面分别对这两种畸变进行详细描述&#xff1a; 桶…

Ceph集群存储案例

Ceph是一种可靠的、可扩展的、统一的、分布式的存储系统。Ceph高度可靠、易于管理且免费。Ceph提供了非凡的可扩展性——数以千计的客户端访问PB到EB的数据。Ceph存储集群相互通信以动态复制和重新分配数据。目前众多云厂商都在使用Ceph&#xff0c;应用广泛。如&#xff1a;华…

[线程与网络] 网络编程与通信原理(四):深入理解传输层UDP与TCP协议

&#x1f338;个人主页:https://blog.csdn.net/2301_80050796?spm1000.2115.3001.5343 &#x1f3f5;️热门专栏:&#x1f355; Collection与数据结构 (92平均质量分)https://blog.csdn.net/2301_80050796/category_12621348.html?spm1001.2014.3001.5482 &#x1f9c0;Java …

研发效能DevOps: Ubuntu 部署 JFrog 制品库

目录 一、实验 1.环境 2.Ubuntu 部署 JFrog 制品库 3.Ubuntu 部署 postgresql数据库 4.Ubuntu 部署 Xray 5. 使用JFrog 增删项目 二、问题 1.Ubuntu 如何通过apt方式部署 JFrog 制品库 2.Ubuntu 如何通过docker方式部署 JFrog 制品库 3.安装jdk报错 4.安装JFrog Ar…

用follow.it为您的网站添加邮箱订阅功能(附2024版教程)

多数情况下网站用户浏览一次就不会来了&#xff08;即使用户已收藏您的网站&#xff09;&#xff0c;因为用户很可能已把您的网站忘了。那么怎么样才能抓住网站回头客&#xff0c;让用户再次回到您的网站呢&#xff1f;除了提供更优质的原创内容外&#xff0c;比较好的方法是给…

第十九讲:动态内存分配

第十九讲&#xff1a;动态内存分配 1.为什么要有动态内存分配2.malloc和free2.1malloc2.1.1函数原型2.1.2函数使用 2.2free2.2.1函数原型2.2.2函数使用2.2.3函数使用注意事项2.2.3.1注意点12.2.3.2注意点22.2.3.3注意点32.2.3.4注意点4 2.3malloc和free使用注意事项2.3.1内存覆…

zynq PS点灯

摸鱼碎碎念&#xff1a; 需要做ADC采集并在TFT屏幕实时显示波形&#xff08;简易示波器&#xff09; 发现只使用fpga实现比较困难 使用的是zynq&#xff0c;刚好来把arm部分也学到 参考视频 与 教材文档 01_Zynq SoC FPGA的诞生_哔哩哔哩_bilibili (这是俺点开AXI4接口协…

【小白专用24.5.30已验证】Composer安装php框架thinkPHP6的安装教程

一、框架介绍 1、框架简介和版本选择 Thinkphp是一种基于php的开源web应用程序开发框架ThinkPHP框架&#xff0c;是免费开源的、轻量级的、简单快速且敏捷的php框架。你可以免费使用TP框架&#xff0c;甚至可以将你的项目商用&#xff1b; ThinkPHP8.0 是目前框架正式版的最新版…

windows上CMake、Mingw和VSCode配置调试C/C++代码

大型项目里经常使用CMake&#xff0c;我在Windows平台上开发&#xff0c;使用的代码编辑器是VSCode&#xff0c;我使用的是mingw的编译器&#xff0c;以前使用的是一个脚本来进行编译&#xff1a; run.bat cmake -G "MinGW Makefiles" .. mingw32-makeVSCode可以方…

Linux实验报告(二)——Linux系统中的常用命令

目录 一、实验名称&#xff1a; 二、仪器、设备&#xff1a; 三、参考资料&#xff1a; 四、实验目的&#xff1a; 五、实验内容&#xff08;步骤&#xff09;&#xff1a; 六、实验数据&#xff08;程序&#xff09;记录&#xff1a; ​编辑 ​编辑 七、实验结果分析…