天天看点

安卓View布局优化杂谈

有些东西,一直忽略了它们,因为它们是空气

现在发现,它们很重要,因为它们是空气

嘿嘿,文艺一把

安卓5大布局:LinearLayout、FrameLayout、TableLayout、RelativeLayout、AbsoluteLayout

老生常谈的东西,这个时代也可以称为LinearLayout、RelativeLayout时代

后面出现了PercentLayout,最近几年又出现了ConstraintLayout

为什么会这样演化,ConstraintLayout又有什么优势?

一、LinearLayout优化

常用的include,布局复用。项目中一些View多次被引用就出现了这个方案

include虽然好用,可是容易造成一些多余的层级结构,然后诞生了merge,合并融合。

merge需要注意:

1、<merge /> must be the root element(必须是根元素)。

2、LayoutInflater.from(context()).inflate(int resource,ViewGroup root,boolean attachToRoot)中resource为根节点merge的xml布局时,必须指定root,和attachToRoot为true

二、LinearLayout与RelativeLayout 性能区别

LinearLayout用起来很舒服,方方正正的。但是有些需求,会造成嵌套嵌套再嵌套

RelativeLayout可以让我们减少一点嵌套,既减少一些布局层次结构

大体来说,相对于层次相同的布局,LinearLayout性能是高于RelativeLayout的。因为RelativeLayout可能存在参照物,所以会对子View做两次measure,横向、纵向各一次。

RelativeLayout是为了让我们减少布局层次,从而提高性能

三、PercentLayout

PercentLayout接触不多。适配是安卓很让人头疼的问题。每次这个时候我都很羡慕前端网页的百分比布局,不要太爽

虽然LinearLayout有比重,但是不适用很多场景

四、ConstraintLayout

不止ios有约束布局了。

ConstraintLayout的推出,个人觉得google的宗旨很明确:减少布局层次,提高性能

它有LinearLayout的比重、有PercentLayout的百分比、是升级版的RelativeLayout

具体属性,用法自行百度

ConstraintLayout使用中,需要动态修改布局时,习惯找LayoutParams。然后发现ConstraintSet 

多处理细节,APP性能的提升会让人大吃一惊

ConstraintSet很舒服,最后贴个简单的demo,看看

安卓View布局优化杂谈
安卓View布局优化杂谈

简单的tab切换。间距等分、下标跟随标题长度、选中标题变大

可以自己封装成列表+适配器,也可以跟viewpage关联起来

布局代码

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/parent_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="@dimen/dp_20">

    <TextView
            android:id="@+id/tv1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="@dimen/dp_24"
            android:gravity="center_vertical"
            android:text="卖商品"
            android:textColor="@color/gray_6d7074"
            android:textSize="@dimen/dp_15"
            app:layout_constraintLeft_toLeftOf="parent"/>

    <TextView
            android:id="@+id/tv2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:gravity="center_vertical"
            android:text="支付"
            android:textColor="@color/gray_6d7074"
            android:textSize="@dimen/dp_15"
            app:layout_constraintBaseline_toBaselineOf="@+id/tv1"
            app:layout_constraintLeft_toRightOf="@+id/tv1"/>

    <TextView
            android:id="@+id/tv3"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:gravity="center_vertical"
            android:text="测试"
            android:textColor="@color/gray_6d7074"
            android:textSize="@dimen/dp_15"
            app:layout_constraintBaseline_toBaselineOf="@+id/tv2"
            app:layout_constraintLeft_toRightOf="@+id/tv2"/>

    <TextView
            android:id="@+id/tv4"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:gravity="center_vertical"
            android:text="你咬我啊"
            android:textColor="@color/gray_6d7074"
            android:textSize="@dimen/dp_19"
            app:layout_constraintBaseline_toBaselineOf="@+id/tv3"
            app:layout_constraintLeft_toRightOf="@+id/tv3"/>

    <TextView
            android:id="@+id/tv5"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:gravity="center_vertical"
            android:text="开心"
            android:textColor="@color/gray_6d7074"
            android:textSize="@dimen/dp_15"
            app:layout_constraintBaseline_toBaselineOf="@+id/tv4"
            app:layout_constraintLeft_toRightOf="@+id/tv4"/>

    <LinearLayout
            android:id="@+id/lin_bg"
            android:layout_width="0dp"
            android:layout_height="@dimen/dp_7"
            android:background="#ff103663"
            android:orientation="vertical"
            app:layout_constraintEnd_toEndOf="@+id/tv5"
            app:layout_constraintStart_toEndOf="@+id/tv5"
            app:layout_constraintStart_toStartOf="@+id/tv5"
            app:layout_constraintTop_toBottomOf="@+id/tv5">

        <View
                android:layout_width="fill_parent"
                android:layout_height="@dimen/dp_2"
                android:layout_marginTop="@dimen/dp_4"
                android:background="#ffffff"/>

    </LinearLayout>

</android.support.constraint.ConstraintLayout>

           

java代码

import android.content.Context
import android.content.Intent
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.support.constraint.ConstraintLayout
import android.support.constraint.ConstraintSet
import android.util.DisplayMetrics
import android.util.Log
import android.view.View
import android.widget.TextView

class MainActivity2 : AppCompatActivity(), View.OnClickListener {
    private var parentLayout: ConstraintLayout? = null
    private var tv1: TextView? = null
    private var tv2: TextView? = null
    private var tv3: TextView? = null
    private var tv4: TextView? = null
    private var tv5: TextView? = null

    companion object {
        fun startActivity(context: Context) {
            context.startActivity(Intent(context, MainActivity2::class.java))
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main2)

        parentLayout = findViewById(R.id.parent_layout)
        tv1 = findViewById(R.id.tv1)
        tv2 = findViewById(R.id.tv2)
        tv3 = findViewById(R.id.tv3)
        tv4 = findViewById(R.id.tv4)
        tv5 = findViewById(R.id.tv5)
        tv1?.setOnClickListener(this)
        tv2?.setOnClickListener(this)
        tv3?.setOnClickListener(this)
        tv4?.setOnClickListener(this)
        tv5?.setOnClickListener(this)

        initTitleData()
        changeView(1)
    }

    override fun onClick(v: View?) {
        when (v?.id) {
            R.id.tv1 -> {
                changeView(0)
            }
            R.id.tv2 -> {
                changeView(1)
            }
            R.id.tv3 -> {
                changeView(2)
            }
            R.id.tv4 -> {
                changeView(3)
            }
            R.id.tv5 -> {
                changeView(4)
            }
        }
    }

    //二级标题列表
    private val fontTextList = ArrayList<TextView>()
    internal var set: ConstraintSet? = null

    private var widthCount = 0
    private var leftMargin: Int = 0

    private fun initTitleData() {
        fontTextList.add(tv1!!)
        fontTextList.add(tv2!!)
        fontTextList.add(tv3!!)
        fontTextList.add(tv4!!)
        fontTextList.add(tv5!!)

        set = ConstraintSet()

        widthCount = 0
        leftMargin = this.resources.getDimensionPixelSize(R.dimen.dp_48)
        for (view in fontTextList) {
            val w = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
            val h = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
            view.measure(w, h)
            val width = view.measuredWidth
            widthCount += width
            Log.e("view width", width.toString())
        }
        Log.e("view widthCount", widthCount.toString() + "")

        val dm = DisplayMetrics()

        this.windowManager.defaultDisplay.getMetrics(dm)

        val screenWidth = dm.widthPixels
        Log.e("屏幕宽度", screenWidth.toString() + "")

        val paddWidth = screenWidth - (widthCount + leftMargin)
        if (paddWidth <= 0) {
            return
        }
        val viewMargin = paddWidth / (fontTextList.size - 1)
        Log.e("单个view的间距", viewMargin.toString())

        for (i in fontTextList.indices) {
            if (i != 0) {
                val topView = fontTextList[i - 1]
                val currentView = fontTextList[i]
                set!!.clone(parentLayout)
                set!!.connect(currentView.id, ConstraintSet.START, topView.id, ConstraintSet.END, viewMargin)
                set!!.applyTo(parentLayout)
                Log.e("单个view的间距$i", viewMargin.toString())
            }
        }
    }

    //刷新view
    private fun changeView(index: Int) {
        for (i in fontTextList.indices) {
            fontTextList[i].setTextSize(
                android.util.TypedValue.COMPLEX_UNIT_PX,
                resources.getDimensionPixelOffset(R.dimen.sp_15).toFloat()
            )
            fontTextList[i].setTextColor(resources.getColor(R.color.gray_6d7074))//#6d7074
            if (i == index) {
                fontTextList[i].setTextSize(
                    android.util.TypedValue.COMPLEX_UNIT_PX,
                    resources.getDimensionPixelOffset(R.dimen.sp_19).toFloat()
                )
                fontTextList[i].setTextColor(resources.getColor(R.color.blue_ff103663))//#ff103663
            }
        }

        set?.clone(parentLayout)
        set?.connect(R.id.lin_bg, ConstraintSet.START, fontTextList[index].id, ConstraintSet.START, 0)
        set?.connect(R.id.lin_bg, ConstraintSet.END, fontTextList[index].id, ConstraintSet.END, 0)
        set?.applyTo(parentLayout)
    }
}