Android 布局优化
2020-04-27  本文已影响0人 
折剑游侠
首先可以通过AS自带的工具查看布局层次
Tools->Layout Inspector
老生常谈的几个点
- 减少布局层次,移除不必要的嵌套
 - 防止过度绘制(手机->开发者选项->调试GPU过度绘制)
 - 移除不必要的背景background
 - 使用merge、include、ViewStub标签
 - 使用ConstraintLayout优化复杂布局
 - 对于习惯使用RelativeLayout和LinearLayout布局的开发者而言,同层级优先使用LinearLayout,因为RelativeLayout至少会Measure两次;复杂布局优先使用RelativeLayout减少层级。
 
merge标签通常配合include标签使用,作为被include的xml布局顶层。在include的时候直接将当前父View作为被include的xml顶层View,可以减少一层嵌套。
ViewStub用来延迟加载,通常使用在网络请求结果有数据时展示的View,无数据则不加载,不浪费资源。
activity_stub.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    <ViewStub
        android:id="@+id/vs"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout="@layout/view_stub" />
</RelativeLayout>
view_stub.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    <Button
        android:id="@+id/bt"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="ViewStub" />
</RelativeLayout>
使用
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_stub)
        val stub = findViewById<ViewStub>(R.id.vs)
        stub.inflate()
    }
看看源码实现
ViewStub构造方法
    public ViewStub(Context context, @LayoutRes int layoutResource) {
        this(context, null);
        mLayoutResource = layoutResource;
    }
    public ViewStub(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }
    public ViewStub(Context context, AttributeSet attrs, int defStyleAttr) {
        this(context, attrs, defStyleAttr, 0);
    }
    public ViewStub(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context);
        final TypedArray a = context.obtainStyledAttributes(attrs,
                R.styleable.ViewStub, defStyleAttr, defStyleRes);
        saveAttributeDataForStyleable(context, R.styleable.ViewStub, attrs, a, defStyleAttr,
                defStyleRes);
        //inflatedId属性
        mInflatedId = a.getResourceId(R.styleable.ViewStub_inflatedId, NO_ID);
        //layout属性引入的xml
        mLayoutResource = a.getResourceId(R.styleable.ViewStub_layout, 0);
        mID = a.getResourceId(R.styleable.ViewStub_id, NO_ID);
        a.recycle();
        setVisibility(GONE);
        setWillNotDraw(true);
    }
ViewStub.inflate
    public View inflate() {
        final ViewParent viewParent = getParent();
        if (viewParent != null && viewParent instanceof ViewGroup) {
            if (mLayoutResource != 0) {
                //获取ViewStub父View
                final ViewGroup parent = (ViewGroup) viewParent;
                //解析xml成View树
                final View view = inflateViewNoAdd(parent);
                //添加View树到父View
                replaceSelfWithView(view, parent);
                mInflatedViewRef = new WeakReference<>(view);
                if (mInflateListener != null) {
                    //回调
                    mInflateListener.onInflate(this, view);
                }
                return view;
            } else {
                throw new IllegalArgumentException("ViewStub must have a valid layoutResource");
            }
        } else {
            throw new IllegalStateException("ViewStub must have a non-null ViewGroup viewParent");
        }
    }
ViewStub.inflateViewNoAdd
    private View inflateViewNoAdd(ViewGroup parent) {
        final LayoutInflater factory;
        if (mInflater != null) {
            factory = mInflater;
        } else {
            factory = LayoutInflater.from(mContext);
        }
        final View view = factory.inflate(mLayoutResource, parent, false);
        //inflatedId属性设置为View树的id
        if (mInflatedId != NO_ID) {
            view.setId(mInflatedId);
        }
        return view;
    }
LayoutInflater解析ViewStub的layout属性下xml资源文件成View树
ViewStub. replaceSelfWithView()
    private void replaceSelfWithView(View view, ViewGroup parent) {
        final int index = parent.indexOfChild(this);
        parent.removeViewInLayout(this);
        final ViewGroup.LayoutParams layoutParams = getLayoutParams();
        if (layoutParams != null) {
            parent.addView(view, index, layoutParams);
        } else {
            parent.addView(view, index);
        }
    }
将View树添加到父View->parent
ConstraintLayout
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'