天天看点

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()方法,这方法到底是何方神圣!让我们慢慢揭开其神秘的面纱吧
        
Android View的22500行源码分析系列(一)setFlag 方法,setVisibilty,setFoucus,setEnable等方法内部实现
/**
    
    
      * 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;//记录当前逻辑属性标记 检查Focusable位有没有改变 if (((changed & FOCUSABLE_MASK) != 0) && ((privateFlags & PFLAG_HAS_BOUNDS) !=0)) { if (((old & FOCUSABLE_MASK) == FOCUSABLE) && ((privateFlags & PFLAG_FOCUSED) != 0)) { 如果不再是focusable清空焦点。 clearFocus(); } else if (((old & FOCUSABLE_MASK) == NOT_FOCUSABLE) && ((privateFlags & PFLAG_FOCUSED) == 0)) { if (mParent != null) mParent.focusableViewAvailable(this); } } final int newVisibility = flags & VISIBILITY_MASK; if (newVisibility == VISIBLE) { if ((changed & VISIBILITY_MASK) != 0) { 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); } } } 检查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; } } if ((changed & INVISIBLE) != 0) { needGlobalAttributesUpdate(false); 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等方法内部实现

继续阅读