通过AS自带的工具查看布局层次Tools->Layout Inspector
老生常谈的几个点:
- 减少布局层次,移除不必要的嵌套
- 防止过度绘制(手机->开发者选项->调试GPU过度绘制)
- 移除不必要的背景background
- 使用merge、include、ViewStub标签
- 使用ConstraintLayout优化复杂布局
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
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'