内容简介:ViewStub的父类是当类注释提供以下示例:
ViewStub的父类是 View ,但 ViewStub 本质是 不可见 、 无尺寸 、 运行时懒加载布局资源 的工具,自身不会展示到界面。
public final class ViewStub extends View
当 ViewStub 被设置为可见,或调用 inflate() 时开始布局资源填充。填充过程 ViewStub 会从所在的父布局中移除,并把懒加载填充的布局添加到相应位置,新填充的布局沿用预设给 ViewStub 的 LayoutParams 。
类注释提供以下示例:
<ViewStub android:id="@+id/stub" android:inflatedId="@+id/subTree" android:layout="@layout/mySubTree" android:layout_width="120dip" android:layout_height="40dip" />
示例定义 ViewStub 的id为 R.id.stub ,填充布局资源为 R.layout.mySubTree ,填充后会指定 R.id.subTree 为新视图的id,以后就能用这个id引用布局。由于 ViewStub 加载布局后就被移除,再次填充就会出现异常。
// 填充之后给新视图设置的id private int mInflatedId; // 需要填充布局的id private int mLayoutResource; private WeakReference<View> mInflatedViewRef;
有多个重载构造方法,都会来到这里:
public ViewStub(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context); // 从xml获取值 final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ViewStub, defStyleAttr, defStyleRes); // 获取填充之后给新视图设置的id mInflatedId = a.getResourceId(R.styleable.ViewStub_inflatedId, NO_ID); // 获取需要填充布局的id mLayoutResource = a.getResourceId(R.styleable.ViewStub_layout, 0); mID = a.getResourceId(R.styleable.ViewStub_id, NO_ID); a.recycle(); setVisibility(GONE); setWillNotDraw(true); }
触发懒加载的方法,和 ViewStub 的 setVisibility() 调用后作用一样。
填充过程检查父布局合法性,其父布局为空可能存在两种情况:
- ViewStub 通过代码构建,但没添加到父布局;
- 该 ViewStub 曾加载过,已从父布局移除;
这两种情况都不允许 ViewStub 继续填充,所以要抛出异常。
public View inflate() { // 获取ViewStub所在父布局 final ViewParent viewParent = getParent(); // 检查父布局是否为空,且父布局必须为ViewGroup,只有ViewGroup才能存放子视图 if (viewParent != null && viewParent instanceof ViewGroup) { // 检查是否指定要加载布局的资源id if (mLayoutResource != 0) { final ViewGroup parent = (ViewGroup) viewParent; // 填充需要懒加载的视图 final View view = inflateViewNoAdd(parent); // 替换视图 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"); } }
懒加载视图经过LayoutInflater 填充为实例
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); // 如果额外指定id,就把指定的id设置给新视图 if (mInflatedId != NO_ID) { view.setId(mInflatedId); } return view; }
获得填充后的布局实例,还要处理 ViewStub 本身,前后共四个步骤:
-
获取 ViewStub 在父布局的位置,就一个索引值;
- ViewStub 从所在父布局中移除,结束占位;
- 把 ViewStub 的 LayoutParams 提供给新视图;
- 新视图放在父布局 ViewStub 原来的位置,即第一步获取的索引值;
private void replaceSelfWithView(View view, ViewGroup parent) { // 获取ViewStub在父布局的位置 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); } }
在 LayoutInflater 实现里面,遇到 ViewStub 只是创建新实例,然后把当前使用的 LayoutInflater 实例提供给 ViewStub ,这样以后 ViewStub 加载视图时就使用同一个 LayoutInflater 。
// 创建类实例,args是自定义主题相关变量 final View view = constructor.newInstance(args); // 类型是ViewStub if (view instanceof ViewStub) { // 使用同一个Context给ViewStub设置LayoutInflater,日后View填充会使用 final ViewStub viewStub = (ViewStub) view; viewStub.setLayoutInflater(cloneInContext((Context) args[0])); } mConstructorArgs[0] = lastContext; return view;
参考链接:
- https://stackoverflow.com/questions/23783101/how-to-check-if-a-viewstub-is-already-inflated
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 【源码阅读】AndPermission源码阅读
- ReactNative源码解析-初识源码
- 【源码阅读】Gson源码阅读
- Spring源码系列:BeanDefinition源码解析
- istio 源码 – Citadel 源码分析 (原创)
- istio 源码 – pilot 源码分析(原创)
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Lua设计与实现
codedump / 人民邮电出版社 / 2017-8 / 49.00元
本书首先介绍了Lua中的数据结构,比如通用数据是如何表示的、Lua的字符串以及表类型的实现原理,接着讨论了Lua虚拟机的实现,并且将其中重点的一些指令进行了分类讲解,最后讨论了垃圾回收、模块实现、热更新、协程等的实现原理。一起来看看 《Lua设计与实现》 这本书的介绍吧!