- CompoundButton 源码分析
- LinearLayout 源码分析
- SearchView 源码解析
- LruCache 源码解析
- ViewDragHelper 源码解析
- BottomSheets 源码解析
- Media Player 源码分析
- NavigationView 源码解析
- Service 源码解析
- Binder 源码分析
- Android 应用 Preference 相关及源码浅析 SharePreferences 篇
- ScrollView 源码解析
- Handler 源码解析
- NestedScrollView 源码解析
- SQLiteOpenHelper/SQLiteDatabase/Cursor 源码解析
- Bundle 源码解析
- LocalBroadcastManager 源码解析
- Toast 源码解析
- TextInputLayout
- LayoutInflater 和 LayoutInflaterCompat 源码解析
- TextView 源码解析
- NestedScrolling 事件机制源码解析
- ViewGroup 源码解析
- StaticLayout 源码分析
- AtomicFile 源码解析
- AtomicFile 源码解析
- Spannable 源码分析
- Notification 之 Android 5.0 实现原理
- CoordinatorLayout 源码分析
- Scroller 源码解析
- SwipeRefreshLayout 源码分析
- FloatingActionButton 源码解析
- AsyncTask 源码分析
- TabLayout 源码解析
文章来源于网络收集而来,版权归原创者所有,如有侵权请及时联系!
子 View 的添加和删除
ViewGroup 添加子 View 的方法有多个,但是最终都会调 addView(child, index, params) 方法,并在这个方法内调用私有方法 addViewInner 来实现的。
我们前面知道了每个子 View 都需要有一个 LayoutParams 来告诉 ViewGroup 它布局的时候需要的一些参数,而当添加 View 的时候,View 没有 LayoutParams,或者我们调用了没有 LayoutParams 作为形参的 addView 方法的时候,ViewGroup 会调用自身的 generatedDefaultLayoutParams 来帮子 View 生成一个默认的 LayoutParams:
public void addView(View child, int index) { if (child == null) { throw new IllegalArgumentException("Cannot add a null child view to a ViewGroup"); } LayoutParams params = child.getLayoutParams(); if (params == null) { // params 为空的情况,调用 generateDefaultLayoutParams 方法生成 LayoutParams params = generateDefaultLayoutParams(); if (params == null) { throw new IllegalArgumentException("generateDefaultLayoutParams() cannot return null"); } } addView(child, index, params); } public void addView(View child, int width, int height) { // 调用这个方法的时候,会直接无视子 View 原来的 LayoutParams, // 直接调用 generateDefaultLayoutParams 生成新的 LayoutParams final LayoutParams params = generateDefaultLayoutParams(); params.width = width; params.height = height; addView(child, -1, params); }
然后是私有方法 addViewInner:
private void addViewInner(View child, int index, LayoutParams params, boolean preventRequestLayout) { ... // 子 View 的 parent 不为空,说明子 View 已经被添加到其他 ViewGroup 了。直接抛出异常。 // 也就是说子 View 是不允许同时被添加到多个 ViewGroup 中的。这边挺好理解的,因为子 View 的布局相关参数都是唯一的, // 如果同时被添加到多个 ViewGroup,而 ViewGroup 的布局规则各不相同,会导致我们从某一个 ViewGroup 获取子 View 的时候,没法得到它正确的尺寸等相关信息 if (child.getParent() != null) { throw new IllegalStateException("The specified child already has a parent. " + "You must call removeView() on the child's parent first."); } // 调用 checkLayoutParams 判断当前的 params 是不是我们需要的 LayoutParams, // 很多继承自 ViewGroup 的布局都会用自己的 LayoutParams,并有独立的一些布局属性。 // 如果不是当前布局所需的 LayoutParams 则调用 generateLayoutParams 来转换 if (!checkLayoutParams(params)) { params = generateLayoutParams(params); } // 给子 View 设置 LayoutParams if (preventRequestLayout) { child.mLayoutParams = params; } else { child.setLayoutParams(params); } if (index < 0) { index = mChildrenCount; } // 添加到子 View 的数组中 addInArray(child, index); // 给子 View 设置 parent if (preventRequestLayout) { child.assignParent(this); } else { child.mParent = this; } // 判断并设置焦点 if (child.hasFocus()) { requestChildFocus(child, child.findFocus()); } // 判断 AttachInfo 是否为空,AttachInfo 不为空说明当前的 ViewGroup 是已经添加到 Window 上了。 // 调用子 View 的 dispatchAttachedToWindow。通知当前子 View 已经被添加到 Window AttachInfo ai = mAttachInfo; if (ai != null && (mGroupFlags & FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW) == 0) { boolean lastKeepOn = ai.mKeepScreenOn; ai.mKeepScreenOn = false; child.dispatchAttachedToWindow(mAttachInfo, (mViewFlags&VISIBILITY_MASK)); if (ai.mKeepScreenOn) { needGlobalAttributesUpdate(true); } ai.mKeepScreenOn = lastKeepOn; } if (child.isLayoutDirectionInherited()) { child.resetRtlProperties(); } dispatchViewAdded(child); // 判断是否需要通知子 View 状态改变(state 这边指按下、放开、选中等状态) if ((child.mViewFlags & DUPLICATE_PARENT_STATE) == DUPLICATE_PARENT_STATE) { mGroupFlags |= FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE; } ... }
子 View 的移除则是最终调用了私有方法 removeViewInternal:
private void removeViewInternal(int index, View view) { if (mTransition != null) { mTransition.removeChild(this, view); } boolean clearChildFocus = false; // 判断并清除焦点 if (view == mFocused) { view.unFocus(null); clearChildFocus = true; } view.clearAccessibilityFocus(); // 取消相关的事件 Target cancelTouchTarget(view); cancelHoverTarget(view); // 当子 View 自身还有动画没有结束的时候,把子 View 添加到 disappearingChildren 列表中, // 在 disappearingChildren 列表中的子 View 会在动画结束后被移除 if (view.getAnimation() != null || (mTransitioningViews != null && mTransitioningViews.contains(view))) { addDisappearingView(view); } else if (view.mAttachInfo != null) { // 子 View 自身没有动画在执行中,通知子 View 从 Window 中脱离 view.dispatchDetachedFromWindow(); } if (view.hasTransientState()) { childHasTransientStateChanged(view, false); } needGlobalAttributesUpdate(false); // 把子 View 移出子 View 数组 removeFromArray(index); if (clearChildFocus) { clearChildFocus(view); if (!rootViewRequestFocus()) { notifyGlobalFocusCleared(this); } } dispatchViewRemoved(view); if (view.getVisibility() != View.GONE) { notifySubtreeAccessibilityStateChangedIfNeeded(); } ... }
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论