Android View的22500行源码分析系列(一)setFlag 方法,setVisibilty,setFoucus,setEnable等方法内部实现

一直想系统地学习Android源码,最近又计划写一个自定义控件系列的博客,而在自定义控件的过程中,许多技巧和方法需要你在Android View,ViewGroup的源码中得到灵感。因此,这一个View的22500行源码分析系列就诞生了《Android View的22500行源码分析系列(一)setFlag 方法,setVisibilty,setFoucus,setEnable等方法内部实现》《Android View的22500行源码分析系列(一)setFlag 方法,setVisibilty,setFoucus,setEnable等方法内部实现》,本系列基于api23的View的源码,主要是对源码的注解作一些翻译和简单的总结,目的是让大家了解整个View的工

作流程,一些主要方法实现里面的细节问题。说时迟那时快,老司机开车啦,赶紧上车《Android View的22500行源码分析系列(一)setFlag 方法,setVisibilty,setFoucus,setEnable等方法内部实现》


首先我们看一下View常用的几个方法:

@RemotableViewMethod public void setVisibility(@Visibility int visibility) {
    setFlags(visibility, VISIBILITY_MASK);
}
public void setFocusable(boolean focusable) {
    if (!focusable) {
        setFlags(0, FOCUSABLE_IN_TOUCH_MODE);
    }
    setFlags(focusable ? FOCUSABLE : NOT_FOCUSABLE, FOCUSABLE_MASK);
}
public void setEnabled(boolean enabled) {
    if (enabled == isEnabled()) return;

    setFlags(enabled ? ENABLED : DISABLED, ENABLED_MASK);

    /*  * The View most likely has to change its appearance, so refresh  * the drawable state.
 视图大多数可能改变它的样子,所以刷新drawable的状态  */  refreshDrawableState();

    // Invalidate too, since the default behavior for views is to be  // be drawn at 50% alpha rather than to change the drawable.
 //同时也要刷新,因为默认的View enable为false的行为是透明度为原来的50%而不是改变drawable  invalidate(true);

    if (!enabled) {
        cancelPendingInputEvents();
    }
}
兄弟们,看见没,看见没!这三个方法里面都会调用关键的setFlags()方法,这方法到底是何方神圣!让我们慢慢揭开其神秘的面纱吧



/**
* Set flags controlling behavior of this view.
*设置标记来控制视图的状态
*
* @param flags Constant indicating the value which should be set 表示应该要被设置的值常量
* @param mask Constant indicating the bit range that should be changed 表示应该要被改变的位的常量
*/
该函数在View中多处被调用,例如 View.setEnable()、View.setClickable(),setFocusable()等很多函数都调用到该函数。
在View中使用mViewFlags和mPrivateFlags变量保存大多数的属性:
– mViewFlags:该标记用来保存和视图状态相关的属性。
– mPrivateFlags 该标记用来保存和内部逻辑相关的属性
如Visible相关的Flag:

public static final int VISIBLE = 0x00000000; public static final int INVISIBLE = 0x00000004; public static final int GONE = 0x00000008; static final int VISIBILITY_MASK = 0x0000000C;



void setFlags(int flags, int mask) {
final boolean accessibilityEnabled =
AccessibilityManager.getInstance(mContext).isEnabled();
final boolean oldIncludeForAccessibility = accessibilityEnabled && includeForAccessibility();


int old = mViewFlags;//记录原来的视图状态标记
mViewFlags = (mViewFlags & ~mask) | (flags & mask);//更新视图状态标记


int changed = mViewFlags ^ old;//异或判断状态是否发生改变
if (changed == 0) {//如果没有改变,立刻返回
return;
}
int privateFlags = mPrivateFlags;//记录当前逻辑属性标记


/* Check if the FOCUSABLE bit has changed */ 检查Focusable位有没有改变
if (((changed & FOCUSABLE_MASK) != 0) &&
((privateFlags & PFLAG_HAS_BOUNDS) !=0)) {
if (((old & FOCUSABLE_MASK) == FOCUSABLE)
&& ((privateFlags & PFLAG_FOCUSED) != 0)) {
/* Give up focus if we are no longer focusable */如果不再是focusable清空焦点。
clearFocus();
} else if (((old & FOCUSABLE_MASK) == NOT_FOCUSABLE)
&& ((privateFlags & PFLAG_FOCUSED) == 0)) {
/*
* Tell the view system that we are now available to take focus
* if no one else already has it
*如果没有没有别的视图有焦点,告诉View系统我们可以获取焦点了。
*/
if (mParent != null) mParent.focusableViewAvailable(this);
}
}


final int newVisibility = flags & VISIBILITY_MASK;
if (newVisibility == VISIBLE) {
if ((changed & VISIBILITY_MASK) != 0) {
/*
* If this view is becoming visible, invalidate it in case it changed while
* it was not visible. Marking it drawn ensures that the invalidation will
* go through.
如果视图将要变成可见的,刷新它假设它是在不可见状态发生改变的。让它进行绘制标记保证刷新的进行。
*/
mPrivateFlags |= PFLAG_DRAWN;
invalidate(true);


needGlobalAttributesUpdate(true);


// a view becoming visible is worth notifying the parent
// about in case nothing has focus. even if this specific view
// isn’t focusable, it may contain something that is, so let
// the root view try to give this focus if nothing else does.
一个视图将要变成可见的值得通知父控件假设没有东西有焦点。即使指定的视图不是
可获取焦点的,它可能包含了一些能获取焦点的,因此让根视图尝试让其获取焦点。
if ((mParent != null) && (mBottom > mTop) && (mRight > mLeft)) {//可见需要获取焦点
mParent.focusableViewAvailable(this);
}
}
}


/* Check if the GONE bit has changed */
检查GONE标志位是否发生改变
if ((changed & GONE) != 0) {
needGlobalAttributesUpdate(false);
requestLayout();//如果Gone的标志位发生了改变,必然会请求重新Layout


if (((mViewFlags & VISIBILITY_MASK) == GONE)) {
if (hasFocus()) clearFocus();//如果变为gone,且当前是有焦点的,清除焦点
clearAccessibilityFocus();//清除可达的焦点
destroyDrawingCache();
if (mParent instanceof View) {
// GONE views noop invalidation, so invalidate the parent
((View) mParent).invalidate(true);//Gone的View不需要刷新,因此刷新它的父控件。
}
// Mark the view drawn to ensure that it gets invalidated properly the next
// time it is visible and gets invalidated
设置DRAWN标记保证其正确地得到刷新下次为可见并且刷新的时候
mPrivateFlags |= PFLAG_DRAWN;
}
if (mAttachInfo != null) {
mAttachInfo.mViewVisibilityChanged = true;
}
}


/* Check if the VISIBLE bit has changed */
if ((changed & INVISIBLE) != 0) {
needGlobalAttributesUpdate(false);
/*
* If this view is becoming invisible, set the DRAWN flag so that
* the next invalidate() will not be skipped.
如果视图将要变成不可见的,设置DRAWN 标记让其下次刷新的时候不会错过
*/
mPrivateFlags |= PFLAG_DRAWN; = 0x00000020;


if (((mViewFlags & VISIBILITY_MASK) == INVISIBLE)) {
// root view becoming invisible shouldn’t clear focus and accessibility focus
如果这是根视图的话,不应该清除焦点和可达的焦点。
if (getRootView() != this) {
if (hasFocus()) clearFocus();
clearAccessibilityFocus();
}
}
if (mAttachInfo != null) {
mAttachInfo.mViewVisibilityChanged = true;
}
}


if ((changed & VISIBILITY_MASK) != 0) {
// If the view is invisible, cleanup its display list to free up resources
如果视图不可见,清除它的显示列表以释放资源
if (newVisibility != VISIBLE && mAttachInfo != null) {
cleanupDraw();
}


if (mParent instanceof ViewGroup) {
((ViewGroup) mParent).onChildVisibilityChanged(this,
(changed & VISIBILITY_MASK), newVisibility);//回调可见性发生变化
((View) mParent).invalidate(true);
} else if (mParent != null) {
mParent.invalidateChild(this, null);
}


if (mAttachInfo != null) {
dispatchVisibilityChanged(this, newVisibility);//分发可见性发生变化
notifySubtreeAccessibilityStateChangedIfNeeded();
}
}


if ((changed & WILL_NOT_CACHE_DRAWING) != 0) {
destroyDrawingCache();
}


if ((changed & DRAWING_CACHE_ENABLED) != 0) {
destroyDrawingCache();
mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
invalidateParentCaches();
}


if ((changed & DRAWING_CACHE_QUALITY_MASK) != 0) {
destroyDrawingCache();
mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
}
static final int DRAW_MASK = 0x00000080;16*8=128—10000000
if ((changed & DRAW_MASK) != 0) {//检查DRAW_MASK标志位是否发生改变
if ((mViewFlags & WILL_NOT_DRAW) != 0) {
if (mBackground != null
|| (mForegroundInfo != null && mForegroundInfo.mDrawable != null)) {
mPrivateFlags &= ~PFLAG_SKIP_DRAW;
} else {
mPrivateFlags |= PFLAG_SKIP_DRAW;
}
} else {
mPrivateFlags &= ~PFLAG_SKIP_DRAW;
}
requestLayout();//这里也会重新请求Layout,如果draw_mask标志位发生了改变,也会重新Layout
invalidate(true);
}


if ((changed & KEEP_SCREEN_ON) != 0) {
if (mParent != null && mAttachInfo != null && !mAttachInfo.mRecomputeGlobalAttributes) {
mParent.recomputeViewAttributes(this);
}
}


if (accessibilityEnabled) {
//focus,visibilty,click,longclick,contextClick
if ((changed & FOCUSABLE_MASK) != 0 || (changed & VISIBILITY_MASK) != 0
|| (changed & CLICKABLE) != 0 || (changed & LONG_CLICKABLE) != 0
|| (changed & CONTEXT_CLICKABLE) != 0) {
if (oldIncludeForAccessibility != includeForAccessibility()) {
notifySubtreeAccessibilityStateChangedIfNeeded();//
Notifies that the accessibility state of this view changed.
} else {
notifyViewAccessibilityStateChangedIfNeeded(
AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
}
} else if ((changed & ENABLED_MASK) != 0) {
notifyViewAccessibilityStateChangedIfNeeded(
AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
}
}
}


总结一下:
①View的许多关于属性的set方法,内部都会调用setFlag方法,来通过Flag标志控制View的行为。其中:

mViewFlags:该标记用来保存和视图状态相关的属性。
mPrivateFlags 该标记用来保存和内部逻辑相关的属性
②View的可见性变为Gone的时候,都会调用requestLayout方法,请求parent重新measure和Layout。
③此外,如果DRAW_MASK标志位发生了改变,也会调用requestLayout方法和invalidate方法来刷新。
可见性发生变化的时候,会设置PFLAG_DRAWN标志来确保刷新的时候得到重绘。
如果视图不可见,清除它的显示列表以释放资源,调用cleanupDraw()方法。




今天,关于setFlag方法的一些了解就到这里啦,期待下一篇博文吧《Android View的22500行源码分析系列(一)setFlag 方法,setVisibilty,setFoucus,setEnable等方法内部实现》






    原文作者:Android源码分析
    原文地址: https://blog.csdn.net/b7223058/article/details/52922231
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞