Android性能优化大图治理示例详解

目录
  • 引言
  • 1 自定义大图View
    • 1.1 准备工作
    • 1.2 图片宽高适配
    • 1.3 BitmapRegionDecoder
  • 2 大图View的手势事件处理
    • 2.1 GestureDetector
    • 2.2 双击放大效果处理
    • 2.3 手指放大效果处理

引言

在实际的Android项目开发中,图片是必不可少的元素,几乎所有的界面都是由图片构成的;像列表页、查看大图页等,都是需要展示图片,而且这两者是有共同点的,列表展示的Item数量多,如果全部加载进来势必会造成OOM,因此列表页通常采用分页加载,加上RecyclerView的复用机制,一般很少会发生OOM。

但是对于大图查看,通常在外界展示的是一张缩略图,点开之后放大就是原图,如果图片很大,OOM发生也是正常的,因此在加载大图的时候,可以看下面这张图

一张图片如果很大,在手机屏幕中并不能完全展示,那么其实就没有必要讲图片完全加载进来,而是可以采用分块加载的方式,只展示显示的那一部分,当图片向上滑动的时候,之前展示的区域内存能够复用,不需要开辟新的内存空间来承接新的模块,从而达到了大图的治理的目的。

1 自定义大图View

像在微信中点击查看大图,查看大图的组件就是一个自定义View,能够支持滑动、拖拽、放大等功能,因此我们也可以自定义一个类似于微信的大图查看器,从中了解图片加载优化的魅力

1.1 准备工作

class BigView : View{
    constructor(context: Context):super(context){
        initBigView(context)
    }
    constructor(context: Context,attributeSet: AttributeSet):super(context,attributeSet){
        initBigView(context)
    }
    private fun initBigView(context: Context) {
    }
}

本节使用的语言为kotlin,需要java代码的伙伴们可以找我私聊哦。

这个是我从网站上找的一张长图,大概700K左右,需要的可以自行下载,其实想要了解其中的原理和实现,不一定要找一张特别大的图片,所有的问题都是举一反三的。

class BigView : View, GestureDetector.OnGestureListener, View.OnTouchListener {
    //分块加载
    private lateinit var mRect: Rect
    //内存复用
    private lateinit var mOptions: BitmapFactory.Options
    //手势
    private lateinit var mGestureDetector: GestureDetector
    //滑动
    private lateinit var mScroller: Scroller
    constructor(context: Context) : super(context) {
        initBigView(context)
    }
    constructor(context: Context, attributeSet: AttributeSet) : super(context, attributeSet) {
        initBigView(context)
    }
    private fun initBigView(context: Context) {
        mRect = Rect()
        mOptions = BitmapFactory.Options()
        mGestureDetector = GestureDetector(context, this)
        mScroller = Scroller(context)
        setOnTouchListener(this)
    }
    override fun onDown(e: MotionEvent?): Boolean {
        return false
    }
    override fun onShowPress(e: MotionEvent?) {
    }
    override fun onSingleTapUp(e: MotionEvent?): Boolean {
        return false
    }
    override fun onScroll(
        e1: MotionEvent?,
        e2: MotionEvent?,
        distanceX: Float,
        distanceY: Float
    ): Boolean {
        return false
    }
    override fun onLongPress(e: MotionEvent?) {
    }
    override fun onFling(
        e1: MotionEvent?,
        e2: MotionEvent?,
        velocityX: Float,
        velocityY: Float
    ): Boolean {
        return false
    }
    override fun onTouch(v: View?, event: MotionEvent?): Boolean {
        return false
    }
}

前面我们提到的分块加载、内存复用、手势等操作,直接在view初始化时完成,这样我们前期的准备工作就完成了。

1.2 图片宽高适配

当我们加载一张图片的时候,要让这张图片完全展示在手机屏幕上不被裁剪,就需要做宽高的适配;如果这张图片大小是80M,那么为了获取宽高而将图片加载到内存中肯定会OOM,那么在图片加载到内存之前就像获取图片的宽高该怎么办呢?BitmapFactory.Options就提供了这个手段

fun setImageUrl(inputStream: InputStream) {
    //获取图片宽高
    mOptions.inJustDecodeBounds = true
    BitmapFactory.decodeStream(inputStream,null,mOptions)
    imageWidth = mOptions.outWidth
    imageHeight = mOptions.outHeight
    mOptions.inJustDecodeBounds = false
    //开启复用
    mOptions.inMutable = true
    mOptions.inPreferredConfig = Bitmap.Config.RGB_565
    //创建区域解码器
    try {
        BitmapRegionDecoder.newInstance(inputStream,false)
    }catch (e:Exception){
    }
    requestLayout()
}

当设置inJustDecodeBounds为true(记住要成对出现,使用完成之后需要设置为false),意味着我调用decodeStream方法的时候,不会将图片的内存加载而是仅仅为了获取宽高。

然后拿到了图片的宽高之后呢,调用requestLayout方法,会回调onMeasure方法,这个方法大家就非常熟悉了,能够拿到view的宽高,从而完成图片的适配

override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec)
    //适配
    viewWidth = measuredWidth
    viewHeight = measuredHeight
    originScale = viewWidth / imageWidth.toFloat()
    mScale = originScale
    //分块加载首次进入展示的rect
    mRect.left = 0
    mRect.top = 0
    mRect.right = imageWidth
    mRect.bottom = (viewHeight / mScale).toInt()
}

这里设置Rect的right就是图片的宽度,因为原始图片的宽度可能比控件的宽度要宽,因此是将控件的宽度与图片的宽度对比获取了缩放比,那么Rect的bottom就需要等比缩放

这里的mRect可以看做是这张图片上的一个滑动窗口,无论是放大还是缩小,只要在屏幕上看到的区域,都可以看做是mRect在这张图片上来回移动截取的目标区域

1.3 BitmapRegionDecoder

在onMeasure中,我们定义了需要加载的图片的Rect,这是一块区域,那么我们通过什么样的方式能够将这块区域的图片加载出来,就是通过BitmapRegionDecoder区域解码器。

区域解码器,顾名思义,能够在某个区域进行图片解码展示

//创建区域解码器
try {
    BitmapRegionDecoder.newInstance(inputStream,false)
}catch (e:Exception){
}

在传入图片流的时候,我们就已经创建了BitmapRegionDecoder,同时将图片流作为参数构建了解码器,那么这个解码器其实已经拿到了整张图片的资源,因此任意一块区域,通过BitmapRegionDecoder都能够解码展示出来

override fun onDraw(canvas: Canvas?) {
    super.onDraw(canvas)
    mRegionDecoder ?: return
    //复用bitmap
    mOptions.inBitmap = mutableBitmap
    mutableBitmap = mRegionDecoder?.decodeRegion(mRect, mOptions)
    //画出bitmap
    val mMatrix = Matrix()
    mMatrix.setScale(mScale, mScale)
    mutableBitmap?.let {
        canvas?.drawBitmap(it, mMatrix, null)
    }
}

首先我们想要进行内存复用,需要调用BitmapFactory.Options的inBitmap,这个参数的含义就是,当我们在某块区域加载图片之后,如果图片上滑那么就需要重新加载,那么这个时候就不会重新开辟一块内存空间,而是复用之前的这块区域,所以调用BitmapRegionDecoder的decodeRegion方法,传入需要展示图片的区域,就能够给mutableBitmap赋值,这样就达成了一块内存空间,多次复用的效果。

这样通过压缩之后,在屏幕中展示了这个长图的最上边部分,那么剩下就需要做的是手势事件的处理。

2 大图View的手势事件处理

通过前期的准备工作,我们已经实现了图片的区域展示,那么接下来关键在于,我们通过手势来查看完整的图片,对于手势事件的响应,在onTouch方法中处理。

override fun onTouch(v: View?, event: MotionEvent?): Boolean {
    return mGestureDetector.onTouchEvent(event)
}

2.1 GestureDetector

通常来说,手势事件的处理都是通过GestureDetector来完成,因此当onTouch方法监听到手势事件之后,直接传给GestureDetector,让GestureDetector来处理这个事件。

override fun onDown(e: MotionEvent?): Boolean {
    return false
}
override fun onShowPress(e: MotionEvent?) {
}
override fun onSingleTapUp(e: MotionEvent?): Boolean {
    return false
}
override fun onScroll(
    e1: MotionEvent?,
    e2: MotionEvent?,
    distanceX: Float,
    distanceY: Float
): Boolean {
    return false
}
override fun onLongPress(e: MotionEvent?) {
}
override fun onFling(
    e1: MotionEvent?,
    e2: MotionEvent?,
    velocityX: Float,
    velocityY: Float
): Boolean {
    return false
}

首先,我们先看下之前注册的GestureDetector.OnGestureListener监听器中实现的方法:

(1)onDown

override fun onDown(e: MotionEvent?): Boolean {
    if(!mScroller.isFinished){
        mScroller.forceFinished(true)
    }
    return true
}

当手指按下时,因为滑动的惯性,所以down事件的处理就是如果图片还在滑动时,按下就停止滑动;

(2)onScroll

那么当你的手指按下之后,可能还会继续滑动,那么就是会回调到onScroll方法,在这个方法中,主要做滑动的处理

override fun onScroll(
    e1: MotionEvent?,
    e2: MotionEvent?,
    distanceX: Float,
    distanceY: Float
): Boolean {
    mRect.offset(0, distanceY.toInt())
    //边界case处理
    if (mRect.bottom > imageHeight) {
        mRect.bottom = imageHeight
        mRect.top = imageHeight - (viewHeight / mScale).toInt()
    }
    if (mRect.top < 0) {
        mRect.top = 0
        mRect.bottom = (viewHeight / mScale).toInt()
    }
    postInvalidate()
    return false
}

在onScroll方法中,其实已经对滑动的距离做了计算(这个真的太nice了,不需要我们自己手动计算),因此只需要对mRect展示区域进行变换即可;

但是这里会有两个边界case,例如滑动到底部时就不能再滑了,这个时候,mRect的底部很可能都已经超过了图片的高度,因此需要做边界的处理,那么滑动到顶部的时候同样也是需要做判断。

(3)onFling

惯性滑动。我们在使用列表的时候,我们在滑动的时候,虽然手指的滑动距离很小,但是列表划出去的距离却很大,就是因为惯性,所以GestureDetector中对惯性也做了处理。

override fun onFling(
    e1: MotionEvent?,
    e2: MotionEvent?,
    velocityX: Float,
    velocityY: Float
): Boolean {
    mScroller.fling(0, mRect.top, 0, -velocityY.toInt(), 0, 0, 0, imageHeight - viewHeight)
    return false
}
//计算惯性
override fun computeScroll() {
    super.computeScroll()
    if (mScroller.isFinished) {
        return
    }
    if (mScroller.computeScrollOffset()) {
        //正在滑动
        mRect.top = mScroller.currY
        mRect.bottom = mScroller.currY + (viewHeight / mScale).toInt()
        postInvalidate()
    }
}

这个还是比较好理解的,就是设置最大的一个惯性滑动距离,无论怎么滑动,边界值就是从顶部一划到底,这个最大的距离就是 imageHeight - viewHeight

设置了惯性滑动的距离,那么在惯性滑动时,也需要实时改变mRect的解码范围,需要重写computeScroll方法,判断如果是正在滑动(通过 mScroller.computeScrollOffset() 判断),那么需要改变mRect的位置。

2.2 双击放大效果处理

我们在使用app时,双击某张图片或者双指拉动某张图片的时候,都会讲图片放大,这也是业内主流的两种图片放大的方式。

override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec)
    //适配
    viewWidth = measuredWidth
    viewHeight = measuredHeight
    //缩放比
    val radio = viewWidth / imageWidth.toFloat()
    //分块加载首次进入展示的rect
    mRect.left = 0
    mRect.top = 0
    mRect.right = imageWidth
    mRect.bottom = viewHeight
}

我们先看一下不能缩放时,mRect的赋值;那么当我们双击放大时,left和top的位置不会变,因为图片放大了,但是控件的大小不会变,因此left的最大值就是控件的宽度,bottom的最大值就是控件的高度。

override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec)
    //适配
    viewWidth = measuredWidth
    viewHeight = measuredHeight
    originScale = viewWidth / imageWidth.toFloat()
    mScale = originScale
    //分块加载首次进入展示的rect
    mRect.left = 0
    mRect.top = 0
    mRect.right = Math.min(imageWidth, viewWidth)
    mRect.bottom = Math.min((viewHeight / mScale).toInt(), viewHeight)
}

这里就将onMeasure进行改造;那么对于双击事件的处理,可以使用GestureDetector.OnDoubleTapListener来处理,在onDoubleTap事件中回调。

override fun onDoubleTap(e: MotionEvent?): Boolean {
    if (mScale < originScale * 2) {
        mScale = originScale * 2
    } else {
        mScale = originScale
    }
    postInvalidate()
    return false
}

这里做了缩放就是判断mScale的值,因为一开始进来不是缩放的场景,因此 mScale = originScale,当双击之后,需要将mScale扩大2倍,当重新绘制的时候,Bitmap就放大了2倍。

那么当图片放大之后,之前横向不能滑动现在也可以滑动查看图片,所以需要处理,同时也需要考虑边界case

override fun onDoubleTap(e: MotionEvent?): Boolean {
    if (mScale < originScale * 2) {
        mScale = originScale * 2
    } else {
        mScale = originScale
    }
    //
    mRect.right = mRect.left + (viewWidth / mScale).toInt()
    mRect.bottom = mRect.top + (viewHeight / mScale).toInt()
    if (mRect.bottom > imageHeight) {
        mRect.bottom = imageHeight
        mRect.top = imageHeight - (viewHeight / mScale).toInt()
    }
    if (mRect.top < 0) {
        mRect.top = 0
        mRect.bottom = (viewHeight / mScale).toInt()
    }
    if(mRect.right > imageWidth){
        mRect.right = imageWidth
        mRect.left = imageWidth - (viewWidth / mScale).toInt()
    }
    if(mRect.left < 0){
        mRect.left = 0
        mRect.right = (viewWidth / mScale).toInt()
    }
    postInvalidate()
    return false
}

当双击图片之后,mRect解码的区域也随之改变,因此需要对right和bottom做相应的改变,图片放大或者缩小,都是在控件宽高的基础之上

override fun onScroll(
    e1: MotionEvent?,
    e2: MotionEvent?,
    distanceX: Float,
    distanceY: Float
): Boolean {
    mRect.offset(distanceX.toInt(), distanceY.toInt())
    //边界case处理
    if (mRect.bottom > imageHeight) {
        mRect.bottom = imageHeight
        mRect.top = imageHeight - (viewHeight / mScale).toInt()
    }
    if (mRect.top < 0) {
        mRect.top = 0
        mRect.bottom = (viewHeight / mScale).toInt()
    }
    if(mRect.left < 0){
        mRect.left = 0
        mRect.right = (viewWidth / mScale).toInt()
    }
    if(mRect.right > imageWidth){
        mRect.right = imageWidth
        mRect.left = imageWidth - (viewWidth / mScale).toInt()
    }
    postInvalidate()
    return false
}

因为需要左右滑动,那么onScroll方法也需要做相应的改动,mRect的offset需要加上x轴的偏移量。

2.3 手指放大效果处理

上一小节介绍了双击事件的效果处理,那么这一节就介绍另一个主流的放大效果实现 - 手指缩放,是依赖 ScaleGestureDetector,其实跟GestureDetector的使用方式一致,这里就不做过多的赘述。

mScaleGestureDetector = ScaleGestureDetector(context, ScaleGesture())

在初始化ScaleGestureDetector的时候,需要传入一个ScaleGesture内部类,集成ScaleGestureDetector.SimpleOnScaleGestureListener,在onScale方法中获取缩放因子来绘制

inner class ScaleGesture : ScaleGestureDetector.SimpleOnScaleGestureListener() {
    override fun onScale(detector: ScaleGestureDetector?): Boolean {
        var scale = detector?.scaleFactor ?: mScale//可以代替mScale
        if (scale < originScale) {
            scale = originScale
        } else if (scale > originScale * 2) {
            scale = originScale * 2
        }
        //在原先基础上缩放
        mRect.right = mRect.left + (viewWidth / scale).toInt()
        mRect.bottom = mRect.top + (viewHeight / scale).toInt()
        mScale = scale
        postInvalidate()
        return super.onScale(detector)
    }
}

这里别忘记了别事件传递出来,对于边界case可自行处理

override fun onTouch(v: View?, event: MotionEvent?): Boolean {
    mGestureDetector.onTouchEvent(event)
    mScaleGestureDetector.onTouchEvent(event)
    return true
}

下面附上大图治理的流程图

黄颜色模块: BitmapFactory.Options配置,避免整张大图直接加载在内存当中,通过开启内存复用(inMutable),使用区域解码器,绘制一块可见区域‘

浅黄色模块: View的绘制流程

以上就是Android性能优化大图治理示例详解的详细内容,更多关于Android性能优化大图治理的资料请关注我们其它相关文章!

(0)

相关推荐

  • Android性能优化方案详情

    目录 1.指标 2.包大小优化 3.响应时间优化 4.内存优化 5.CPU优化 6.耗电量优化 前言: 上一个季度在百度工作挺忙碌,在最后期限完成了OKR目标,因此有一段时间没有写文章.今天趁有机会想分享下在大型Android项目工程内的一些性能优化方式. 1.指标 量化性能的指标有很多,但最重要的就是以下5种: 包大小 响应时间 内存 CPU 耗电量 优化性能就是可以从以上5点入手. 2.包大小优化 顾名思义就是减少apk包体积大小,apk大小主要取决于res下的资源文件..class文件,

  • Android 进阶实现性能优化之OOM与Leakcanary详解原理

    目录 Android内存泄漏常见场景以及解决方案 资源性对象未关闭 注册对象未注销 类的静态变量持有大数据 单例造成的内存泄漏 非静态内部类的静态实例 Handler临时性内存泄漏 容器中的对象没清理造成的内存泄漏 WebView 使用ListView时造成的内存泄漏 Leakcanary leakcanary 导入 leakcanary 是如何安装的 leakcanary 如何监听Activity.Fragment销毁 RefWatcher 核心原理 流程图 本文主要探讨以下几个问题: And

  • Android图片性能优化详解

    1. 图片的格式 目前移动端Android平台原生支持的图片格式主要有:JPEG.PNG.GIF.BMP.和WebP(自从Android 4.0开始支持),但是在Android应用开发中能够使用的编解码格式只有三种:JPEG.PNG.WebP,图片格式可以通过查看Bitmap类的CompressFormat枚举值来确定. public static enum CompressFormat { JPEG. PNG. WebP; private CompressFormat() { } } 如果要在

  • Android性能优化及性能优化工具

    目录 1.Allaction Tracking (1)追踪 (2)分类我们的内存分配 (3)查看统计图 2.LeakCanary (1)配置 (2)制造一个单例内存泄漏的点 (3)LeakCanary 发出内存泄漏通知 (4)LeakCanary 分析 3.Lint分析工具  性能优化的帮助工具: MAT, Memory Monitor(属于AndroidMonitor中一个模块), HeapTool(查看堆信息), Allaction Tracking, LeakCanary Lint工具 1

  • Android性能优化之图片大小,尺寸压缩综合解决方案

    目录 前言 常见的图片压缩方法 质量压缩 尺寸压缩 libjpeg 图片压缩流程 总结 前言 在Android中我们经常会遇到图片压缩的场景,比如给服务端上传图片,包括个人信息的用户头像,有时候人脸识别也需要捕获图片等等.这种情况下,我们都需要对图片做一定的处理,比如大小,尺寸等的压缩. 常见的图片压缩方法 质量压缩 尺寸压缩 libjpeg 质量压缩 首先我们要介绍一个api--Bitmap.compress() @WorkerThread public boolean compress(Co

  • Android 性能优化系列之bitmap图片优化

    背景 Android开发中,加载图片过多.过大很容易引起OutOfMemoryError异常,即我们常见的内存溢出.因为Android对单个应用施加内存限制,默认分配的内存只有几M(具体视不同系统而定).而载入的图片如果是JPG之类的压缩格式(JPG支持最高级别的压缩,不过该压缩是有损的),在内存中展开会占用大量的内存空间,也就容易形成内存溢出.那么高效的加载Bitmap是很重要的事情.Bitmap在Android中指的是一张图片,图片的格式有.jpg .jpg .webp 等常见的格式. 如何

  • Android性能优化大图治理示例详解

    目录 引言 1 自定义大图View 1.1 准备工作 1.2 图片宽高适配 1.3 BitmapRegionDecoder 2 大图View的手势事件处理 2.1 GestureDetector 2.2 双击放大效果处理 2.3 手指放大效果处理 引言 在实际的Android项目开发中,图片是必不可少的元素,几乎所有的界面都是由图片构成的:像列表页.查看大图页等,都是需要展示图片,而且这两者是有共同点的,列表展示的Item数量多,如果全部加载进来势必会造成OOM,因此列表页通常采用分页加载,加上

  • Android性能优化死锁监控知识点详解

    目录 前言 死锁检测 线程Block状态 获取当前线程所请求的锁 通过锁获取当前持有的线程 线程启动 nativePeer 与 native Thread tid 与java Thread tid dlsym与调用 系统限制 死锁检测所有代码 总结 前言 “死锁”,这个从接触程序开发的时候就会经常听到的词,它其实也可以被称为一种“艺术”,即互斥资源访问循环的艺术,在Android中,如果主线程产生死锁,那么通常会以ANR结束app的生命周期,如果是两个子线程的死锁,那么就会白白浪费cpu的调度资

  • java接口性能从20s优化到500ms示例详解

    目录 前言 1. 案发现场 2. 现状 3. 第一次优化 4. 第二次优化 5. 第三次优化 5.1 前端做分页 5.2 分批调用接口 前言 接口性能问题,对于从事后端开发的同学来说,是一个绕不开的话题.想要优化一个接口的性能,需要从多个方面着手. 其实,我之前也写过一篇接口性能优化相关的文章<java接口性能优化小技巧>,发表之后在全网广受好评,感兴趣的小伙们可以仔细看看. 本文将会接着接口性能优化这个话题,从实战的角度出发,聊聊我是如何优化一个慢查询接口的. 上周我优化了一下线上的批量评分

  • Vue.js3.2的vnode部分优化升级使用示例详解

    目录 背景 什么是 vnode 普通元素 vnode 组件 vnode vnode 的优势 如何创建 vnode 创建 vnode 过程的优化 总结 背景 上一篇文章,分析了 Vue.js 3.2 关于响应式部分的优化,此外,在这次优化升级中,还有一个运行时的优化: ~200% faster creation of plain element VNodes 即针对普通元素类型 vnode 的创建,提升了约 200% 的性能.这也是一个非常伟大的优化,是 Vue 的官方核心开发者 HcySunYa

  • Android RecyclerChart其它图表绘制示例详解

    目录 正文 1. 心电图 2. 睡眠图 正文 之前章节介绍了RecyclerChart 中一些通用的图表的相关绘制逻辑,本章节介绍两种Special的Chart的绘制,一种是心电图,一种是睡眠图.首先我们来看下心电图EcgChart的绘制. 1. 心电图 EcgChart 跟LineChart形态上是相似的,但是EcgChart的点相对于LineChart密集的多,之前的LineChart相当于每个RecyclerView的Itemview 中的Model对应的value值,而心电图的ItemD

  • Android如何实现一个DocumentProvider示例详解

    目录 前言 步骤 首先在Manifest 中注册这个Provider 创建这个Provider 重写queryRoot 重写queryDocument 重写getChildDocument 前言 假如你做了一个云盘类的app,或者可以保存用户导入的配置.用户在未来肯定需要获取这些文件,一个办法是写一个Activity,向一个文件管理软件一样把他们列出来.但是这个有一个问题是用户必须进入app 才能访问. 现在有一个解决方案是实现一个DocumentProvider 步骤 DocumentProv

  • Android编程之SurfaceView学习示例详解

    本文实例讲述了Android编程之SurfaceView学习示例.分享给大家供大家参考,具体如下: SurfaceView是View的子类,使用的方式与任何View所派生的类都是完全相同的,可以像其他View那样应用动画,并把它们放到布局中. SurfaceView封装的Surface支持使用本章前面所描述的所有标准Canvas方法进行绘图,同时也支持完全的OpenGL ES库. 使用OpenGL,你可以再Surface上绘制任何支持的2D或者3D对象,与在2D画布上模拟相同的效果相比,这种方法

  • React性能优化的实现方法详解

    目录 前言 遍历视图key使用 React.memo缓存组件 React.useCallback让函数保持相同的引用 避免使用内联对象 使用React.useMemo缓存计算结果或者组件 使用React.Fragment片段 组件懒加载 通过 CSS 加载和卸载组件 变与不变的地方做分离 总结 前言 想要写出高质量的代码,仅仅靠框架底层帮我们的优化还远远不够,在编写的过程中,需要我们自己去使用提高的 api,或者根据它底层的原理去做一些优化,以及规范. 相比于 Vue ,React 不会再框架源

  • tsc性能优化Project References使用详解

    目录 什么是 Project References 示例项目结构 不使用 Project References 带来的问题 tsconfig.json 的 references 配置项 tsconfig.json 的 composite 配置项 使用 Project References 改造示例项目 全量构建 增量构建 对__test__测试代码的处理 总结 什么是 Project References 在了解一个东西是什么的时候,直接看其官方定义是最直观的 TypeScript: Docum

  • Android如何自定义升级对话框示例详解

    前言 本文主要给大家介绍了关于Android自定义升级对话框的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. 实现的效果如下所示 其实这也只是一个DialogFragment 而已,重点只是在于界面的设计 想要使用做出这样一个DialogFragment ,需要自定义一个View,然后将该View传入到该Dialog中 先定义布局,一个TextView用于标题,一个TextView用于升级内容阐述,一个ImageView,一个确认升级的按钮 <?xml version

随机推荐