美文网首页
减少自定义布局View嵌套

减少自定义布局View嵌套

作者: CentForever | 来源:发表于2021-01-03 20:58 被阅读0次

减少自定义布局View嵌套

通常遇到一些固定用法的组合布局,会把它们做成一个自定义View,以方便重复使用。比如说一个头像加一个Vip标志:

刚开始的时候,可能会这样写:

layout:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content">

    <ImageView
        android:id="@+id/avatar"
        android:layout_width="50dp"
        android:layout_height="50dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <ImageView
        android:id="@+id/vip"
        android:layout_width="16dp"
        android:layout_height="16dp"
        android:src="@drawable/vip"
        app:layout_constraintBottom_toBottomOf="@+id/avatar"
        app:layout_constraintEnd_toEndOf="@+id/avatar" />

</android.support.constraint.ConstraintLayout>

code:

class AvatarView1 @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : FrameLayout(context, attrs, defStyleAttr) {

    override fun onFinishInflate() {
        super.onFinishInflate()
        LayoutInflater.from(context).inflate(R.layout.avatar_view_1, this, true)
    }

    fun setAvatar(url: String) {
        ...
    }
}

没毛病,但是AvatarView1自身的根布局多产生了一层嵌套:

那么如何能减少这一层嵌套呢,有两种方法:

1. 使用merge标签

  • 让自定义View的类继承布局文件的根布局。
  • 将布局文件的根布局改成merge标签。

code:

class AvatarView2 @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : ConstraintLayout(context, attrs, defStyleAttr) {

    ...
}

layout:

<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    xmlns:tools="http://schemas.android.com/tools"
    tools:parentTag="android.support.constraint.ConstraintLayout">

    <ImageView
        android:id="@+id/avatar"
        android:layout_width="50dp"
        android:layout_height="50dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <ImageView
        android:id="@+id/vip"
        android:layout_width="16dp"
        android:layout_height="16dp"
        android:src="@drawable/vip"
        app:layout_constraintBottom_toBottomOf="@+id/avatar"
        app:layout_constraintEnd_toEndOf="@+id/avatar" />

</merge>

tools:parentTag=”android.support.constraint.ConstraintLayout”,加入该属性可以保持preview中的预览效果

使用:

<com.ezstudio.view.AvatarView2
    android:id="@+id/avatar_view"
    android:layout_width="50dp"
    android:layout_height="50dp" />

运行后可以看到,布局层次少了一层:

2. 使用view标签

  • 让自定义View的类继承布局文件的根布局,并去掉onFinishInflate中的相关代码。
  • 将布局文件的根布局改成view标签,并利用class属性将布局和类关联起来。

code:

class AvatarView3 @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : ConstraintLayout(context, attrs, defStyleAttr) {

    //override fun onFinishInflate() {
    //    super.onFinishInflate()
    //    LayoutInflater.from(context).inflate(R.layout.avatar_view_1, this, true)
    //}

    fun setAvatar(url: String) {
        ...
    }
}

layout:

<?xml version="1.0" encoding="utf-8"?>
<view xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    xmlns:tools="http://schemas.android.com/tools"
    class="com.ezstudio.view.AvatarView3"
    tools:parentTag="android.support.constraint.ConstraintLayout">

    <ImageView
        android:id="@+id/avatar"
        android:layout_width="50dp"
        android:layout_height="50dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <ImageView
        android:id="@+id/vip"
        android:layout_width="16dp"
        android:layout_height="16dp"
        android:src="@drawable/vip"
        app:layout_constraintBottom_toBottomOf="@+id/avatar"
        app:layout_constraintEnd_toEndOf="@+id/avatar" />

</view>

使用:

val avatarView = LayoutInflater.from(this).inflate(R.layout.avatar_view_3, main_layout, false) as AvatarView3
main_layout.addView(avatarView)

运行后可以看到,布局层次一样是少了一层:

3. 终极大法

所谓终极大法就是将自定义View的所有子元素,通过Canvas绘制到界面上,这样的效率是最高的,适合对性能要求变态的场景:

class AvatarView4 @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {

    private val paint by lazy {
        Paint().apply {
            isAntiAlias = true
        }
    }

    private var avatarBitmap: Bitmap? = null

    private val vipBitmap by lazy { BitmapFactory.decodeResource(resources, R.drawable.vip) }

    private val vipSize = dip2px(context, 16f)

    private val viewRect by lazy { RectF() }

    private val vipRect by lazy { RectF(0f, 0f, vipSize, vipSize) }

    private val target: SimpleTarget<Bitmap> by lazy {
        object : SimpleTarget<Bitmap>() {
            override fun onResourceReady(resource: Bitmap, transition: Transition<in Bitmap>?) {
                avatarBitmap = resource
                invalidate()
            }
        }
    }

    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)
        viewRect.set(0f, 0f, measuredWidth.toFloat(), measuredHeight.toFloat())
    }

    override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)
        canvas?.apply {
            avatarBitmap?.let {
                drawBitmap(it, null, viewRect, paint)
            }
            save()
            translate(viewRect.right - vipSize, viewRect.bottom - vipSize)
            drawBitmap(vipBitmap, null, vipRect, paint)
            restore()
        }
    }

    fun setAvatar(url: String) {
        val requestOptions = RequestOptions.circleCropTransform().dontAnimate()
        Glide.with(context).asBitmap().load(url).apply(requestOptions).into(target)
    }

    private fun dip2px(context: Context, @Dimension(unit = Dimension.DP) dpValue: Float): Float {
        return TypedValue.applyDimension(
            TypedValue.COMPLEX_UNIT_DIP,
            dpValue,
            context.resources.displayMetrics
        )
    }
}

运行后可以看到,布局层次只有一层:

总结

  • merge标签和view标签都能有效减少自定义View的嵌套层级
  • merge标签适合在其他布局文件直接使用的场景
  • view标签适合通过LayoutInflater加载的场景,比如在RecyclerView中作为Item使用
  • 终极大法效率最高,适合对性能要求变态的场景

相关文章

  • 减少自定义布局View嵌套

    减少自定义布局View嵌套 通常遇到一些固定用法的组合布局,会把它们做成一个自定义View,以方便重复使用。比如说...

  • Layout inflation 优化

    减少布局的嵌套层级 异步加载AsyncLayoutInflater为ViewGroup动态添加子View时,我们往...

  • 自定义View 自定义布局

    自定义View布局 自定义View布局的工作内容 View或者ViewGroup的布局过程 布局过程自定义的方式 ...

  • fragment性能优化方案

    一、View层面 1、布局嵌套最好不超过3层 2、自定义View绘制不宜过长 二、预加载 1、使用ad...

  • Android性能之布局优化

    优化布局层次 Google建议View 的高度不应超过10层,避免嵌套过多。 尽量减少布局层级和复杂度 尽量不要嵌...

  • Android:性能优化!!!

    布局优化 核心思想:减少布局的层级 优化方式: 多嵌套情况可以使用RelativeLayout减少嵌套。 布局层级...

  • Container view-collection View

    Container view-容器视图 Collection Views 使用可配置且高度可自定义的布局显示嵌套视...

  • 布局优化方法

    布局诱惑就是减少层级嵌套,减少overdraw。越简单越好1、善用相对布局Relativelayout在Relat...

  • 自定义View - 字母索引列表

    自定义view 布局 使用

  • 横向柱状图

    自定义View 布局文件 引用

网友评论

      本文标题:减少自定义布局View嵌套

      本文链接:https://www.haomeiwen.com/subject/ylagnktx.html