2behavior原理解析

上篇文章中硫眯,我們簡(jiǎn)單介紹了一下Behavior种蘸,今天對(duì)他的原理做進(jìn)一步分析蹋绽。主要介紹behavior如何自定義,behavior的構(gòu)造嘱丢,onPreDraw,fab為何隨snackbar變化的相關(guān)的知識(shí)祠饺。

自定義behavior

先看個(gè)例子越驻,上篇文章主要是重點(diǎn)分析了下,為什么snackbar出現(xiàn)和消失的時(shí)候,fab會(huì)做出相應(yīng)變化伐谈,那我們能否修改這種變化呢烂完?
比如我想要snackbar出現(xiàn)的時(shí)候,fab往上移動(dòng)100诵棵,snackbar消失的時(shí)候fab再往上移動(dòng)100抠蚣,能否實(shí)現(xiàn)呢?
當(dāng)然可以履澳,代碼也很簡(jiǎn)單嘶窄,自定義一個(gè)MyBehavior,注意必須加入一個(gè)MyBehavior的構(gòu)造器距贷,帶有Context和AttributeSet參數(shù)柄冲,原因后文會(huì)說。

public class MyBehavior extends CoordinatorLayout.Behavior<View> {

    //此構(gòu)造函數(shù)必須加入
    public MyBehavior(Context context, AttributeSet attrs) {
        super(context,attrs);
    }

    //child就是綁定此behavior的view忠蝗,dependency是發(fā)送變化的view
    @Override
    public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
        return dependency instanceof Snackbar.SnackbarLayout;
    }

    @Override
    public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
        //此處child 就是fab现横,dependency是被依賴的view
        if (dependency instanceof Snackbar.SnackbarLayout) {
            //SnackbarLayout 變化了,A該如何變化在這里寫
            child.setTranslationY(child.getTranslationY() - 100);
            return true;
        }
        return false;
    }

    @Override
    public void onDependentViewRemoved(CoordinatorLayout parent, View child, View dependency) {
        if (dependency instanceof Snackbar.SnackbarLayout) {
            //SnackbarLayout 變化了阁最,fab該如何變化在這里寫
            child.setTranslationY(child.getTranslationY() - 100);
        }
    }
}

然后在xml內(nèi)配置behavior,其實(shí)就是加入了一行代碼 app:layout_behavior="com.fish.a2.MyBehavior"

    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab"
        app:layout_behavior="com.fish.a2.MyBehavior"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|end"
        android:layout_margin="@dimen/fab_margin"
        android:src="@android:drawable/ic_dialog_email" />

看效果

所以說自定義Behavior也是很簡(jiǎn)單的事情

原理分析

behavior到底是什么

從上邊的代碼看來戒祠,behavior像是view的一個(gè)屬性,其實(shí)他是view的LayoutParam的一個(gè)屬性速种,就像寬高一樣姜盈。當(dāng)然不是任何一個(gè)view的LayoutParam都有這個(gè)屬性的,只有LayoutParam為android.support.design.widget.CoordinatorLayout.LayoutParams才有這個(gè)屬性配阵,說白了馏颂,就是只有CoordinatorLayout的子view的LayoutParam可以設(shè)置behavior。我們看看CoordinatorLayout.LayoutParams的代碼,可以發(fā)現(xiàn)Behavior變量的確在里面棋傍。

  public static class LayoutParams extends ViewGroup.MarginLayoutParams {
        Behavior mBehavior;
        boolean mBehaviorResolved = false;
        ...
        final Rect mLastChildRect = new Rect();
        }

再來看看app:layout_behavior="com.fish.a2.MyBehavior"這行代碼是怎么導(dǎo)致LayoutParams內(nèi)的mBehavior被賦值的
我們知道infate的時(shí)候救拉,會(huì)根據(jù)xml去構(gòu)造LayoutParams,所以我們看CoordinatorLayout.LayoutParams的構(gòu)造函數(shù)

LayoutParams(Context context, AttributeSet attrs) {
    super(context, attrs);

    final TypedArray a = context.obtainStyledAttributes(attrs,
            R.styleable.CoordinatorLayout_LayoutParams);
    ...
    mBehaviorResolved = a.hasValue(
            R.styleable.CoordinatorLayout_LayoutParams_layout_behavior);
    if (mBehaviorResolved) {
        mBehavior = parseBehavior(context, attrs, a.getString(
                R.styleable.CoordinatorLayout_LayoutParams_layout_behavior));
    }

    a.recycle();
}

當(dāng)我們構(gòu)造fab的LayoutParams時(shí)舍沙,走到L7近上,查一下是否存在layout_behavior值,如果存在拂铡,那就parseBehavior并且賦值給mBehavior壹无。parseBehavior就是把字符串com.fish.a2.MyBehavior變成一個(gè)對(duì)象,用反射的方法感帅。主要代碼如下所示

// 這里是指定的Behavior構(gòu)造器的參數(shù)類型
static final Class<?>[] CONSTRUCTOR_PARAMS = new Class<?>[] {
        Context.class,
        AttributeSet.class
};

...

static Behavior parseBehavior(Context context, AttributeSet attrs, String name) {
    ...
    try {
        Map<String, Constructor<Behavior>> constructors = sConstructors.get();
        if (constructors == null) {
            constructors = new HashMap<>();
            sConstructors.set(constructors);
        }
        Constructor<Behavior> c = constructors.get(fullName);
        
        if (c == null) {
            final Class<Behavior> clazz = (Class<Behavior>) Class.forName(fullName, true,
                    context.getClassLoader());
            //獲取特定參數(shù)的構(gòu)造器        
            c = clazz.getConstructor(CONSTRUCTOR_PARAMS);
            c.setAccessible(true);
            constructors.put(fullName, c);
        }
        return c.newInstance(context, attrs);
    } catch (Exception e) {
        throw new RuntimeException("Could not inflate Behavior subclass " + fullName, e);
    }
}

主要L23斗锭,這里是用特定參數(shù)的構(gòu)造器來c.newInstance的,CONSTRUCTOR_PARAMS就是一個(gè)Context失球,一個(gè)AttributeSet岖是,為什么我們開頭的時(shí)候說自定義Behavior必須帶一個(gè)這種類型的構(gòu)造器的帮毁,現(xiàn)在應(yīng)該有答案了。
那么現(xiàn)在問題來了豺撑,在上一篇文章中烈疚,我們根本就沒有設(shè)置layout_behavior,那這個(gè)LayoutParams里面的mBehavior是在哪里復(fù)制的呢聪轿?
我們?cè)賮砜纯? FloatingActionButton有個(gè)注解爷肝,CoordinatorLayout.DefaultBehavior(FloatingActionButton.Behavior.class),在這里指定了默認(rèn)的Behavior

@CoordinatorLayout.DefaultBehavior(FloatingActionButton.Behavior.class)
public class FloatingActionButton extends VisibilityAwareImageButton 

在CoordinatorLayout的onMeasure的時(shí)候會(huì)調(diào)用prepareChildren陆错,進(jìn)而調(diào)用getResolvedLayoutParams灯抛,在getResolvedLayoutParams里會(huì)把注解里的默認(rèn)Behavior賦值給mBehavior,主要代碼如下

    LayoutParams getResolvedLayoutParams(View child) {
        final LayoutParams result = (LayoutParams) child.getLayoutParams();
        //如果xml內(nèi)寫了behavior,此時(shí)result.mBehaviorResolved就為true音瓷,不會(huì)進(jìn)去
        if (!result.mBehaviorResolved) {
            Class<?> childClass = child.getClass();
            DefaultBehavior defaultBehavior = null;
            while (childClass != null &&
                    (defaultBehavior = childClass.getAnnotation(DefaultBehavior.class)) == null) {
                childClass = childClass.getSuperclass();
            }
            if (defaultBehavior != null) {
                try {
                    result.setBehavior(defaultBehavior.value().newInstance());
                } catch (Exception e) {
                    Log.e(TAG, "Default behavior class " + defaultBehavior.value().getName() +
                            " could not be instantiated. Did you forget a default constructor?", e);
                }
            }
            result.mBehaviorResolved = true;
        }
        return result;
    }

所以到了現(xiàn)在对嚼,我們知道設(shè)置一個(gè)view的behavior有2種方式,xml內(nèi)指定绳慎,或者注解里指定纵竖,xml優(yōu)先級(jí)高。xml內(nèi)指定的話杏愤,是在inflate的時(shí)候?qū)Behavior賦值的磨确,在注解里指定的話,是在onMeasure內(nèi)賦值的声邦,稍有不同。

behavior如何發(fā)揮作用

前面說了如何給view配置behavior摆舟,那配了behavior又有什么用呢亥曹?為何behavior能夠監(jiān)測(cè)到另一個(gè)view的變化情況,這都是CoordinatorLayout的功勞恨诱。

onMeasure

我們?cè)賮砜纯磑nMeasure的代碼媳瞪,主要看prepareChildren和ensurePreDrawListener

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        prepareChildren();
        ensurePreDrawListener();
        。照宝。蛇受。

先看prepareChildren

    private void prepareChildren() {
         //清空mDependencySortedChildren
        mDependencySortedChildren.clear();
        for (int i = 0, count = getChildCount(); i < count; i++) {
            final View child = getChildAt(i);

            final LayoutParams lp = getResolvedLayoutParams(child);
            lp.findAnchorView(this, child);
            //加入child
            mDependencySortedChildren.add(child);
        }
        // We need to use a selection sort here to make sure that every item is compared
        // against each other
        //排序
        selectionSort(mDependencySortedChildren, mLayoutDependencyComparator);
    }

prepareChildren內(nèi)做了什么,主要是搞出來一個(gè)mDependencySortedChildren,根據(jù)依賴關(guān)系對(duì)child進(jìn)行排序厕鹃。首先L3把mDependencySortedChildren clear兢仰,然后遍歷子view,全部加入到mDependencySortedChildren內(nèi)剂碴,最后對(duì)mDependencySortedChildren進(jìn)行排序把将。注意每次measure都會(huì)調(diào)用prepareChildren來搞出一個(gè)mDependencySortedChildren。
我們?cè)诳纯磁判虻拇a(用的冒泡)忆矛,看mLayoutDependencyComparator就行了,看下邊代碼可以知道察蹲,被依賴的view放前面,比如我們fab依賴于snackbar洽议,那么snackbar必然放在fab的前邊。這么排序有什么用亚兄?其實(shí)是提高一點(diǎn)效率,后文會(huì)說的儿捧。

    final Comparator<View> mLayoutDependencyComparator = new Comparator<View>() {
        @Override
        public int compare(View lhs, View rhs) {
            if (lhs == rhs) {
                return 0;
            } else if (((LayoutParams) lhs.getLayoutParams()).dependsOn(
                    CoordinatorLayout.this, lhs, rhs)) {
                return 1;
            } else if (((LayoutParams) rhs.getLayoutParams()).dependsOn(
                    CoordinatorLayout.this, rhs, lhs)) {
                return -1;
            } else {
                return 0;
            }
        }
    };

再看ensurePreDrawListener

在prepareChildren確定mDependencySortedChildren之后,會(huì)執(zhí)行ensurePreDrawListener颓影,在這里寫判斷下CoordinatorLayout的子view是否存在依賴關(guān)系懒鉴,如果存在的話就hasDependencies為true,后邊會(huì)加入PreDrawListener临谱。

    void ensurePreDrawListener() {
         //判斷是否存在依賴關(guān)系
        boolean hasDependencies = false;
        final int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            final View child = getChildAt(i);
            if (hasDependencies(child)) {
                hasDependencies = true;
                break;
            }
        }

        if (hasDependencies != mNeedsPreDrawListener) {
            if (hasDependencies) {
                //加入PreDrawListener
                addPreDrawListener();
            } else {
                removePreDrawListener();
            }
        }
    }

PreDrawListener是什么?看下邊代碼悉默,簡(jiǎn)單,就是在onPreDraw的時(shí)候調(diào)用dispatchOnDependentViewChanged唱星。

    void addPreDrawListener() {
        if (mIsAttachedToWindow) {
            // Add the listener
            if (mOnPreDrawListener == null) {
                mOnPreDrawListener = new OnPreDrawListener();
            }
            final ViewTreeObserver vto = getViewTreeObserver();
            vto.addOnPreDrawListener(mOnPreDrawListener);
        }

        // Record that we need the listener regardless of whether or not we're attached.
        // We'll add the real listener when we become attached.
        mNeedsPreDrawListener = true;
    }
    class OnPreDrawListener implements ViewTreeObserver.OnPreDrawListener {
        @Override
        public boolean onPreDraw() {
            dispatchOnDependentViewChanged(false);
            return true;
        }
    }

onPreDraw這個(gè)回調(diào)和onGlobalLayout類似的跟磨,他們的對(duì)象是ViewTreeObserver间聊,而不是某個(gè)view。在即將繪制的時(shí)候抵拘,會(huì)調(diào)用mTreeObserver.dispatchOnPreDraw()哎榴,然后分發(fā)到各個(gè)OnPreDrawListener,在回調(diào)onPreDraw的僵蛛。簡(jiǎn)單的說尚蝌,就是在重繪之前,會(huì)調(diào)用onPreDraw充尉。我們?cè)趏nPreDraw里面調(diào)用了dispatchOnDependentViewChanged驼壶,這個(gè)函數(shù)是CoordinatorLayout非常重要的函數(shù)。Behavior的主要行為都是寫在這里面的喉酌。我們先總結(jié)下ensurePreDrawListener做了什么热凹,判斷子view是否有依賴行為泵喘,如果有的話注冊(cè)一個(gè)onPreDraw監(jiān)聽

dispatchOnDependentViewChanged

這里傳進(jìn)來的fromNestedScroll為false,遍歷mDependencySortedChildren般妙,查一下每個(gè)view的rect是否發(fā)生了變化纪铺,如果發(fā)生了變化(假設(shè)變化的view為A),就遍歷后邊的view碟渺,判斷后邊view是否依賴于A(L33)鲜锚,如果依賴就做出相應(yīng)變化(L36)∩慌模看到L33和L36芜繁,終于舒了口氣,和Behavior里的行為扯上了關(guān)系绒极。

    void dispatchOnDependentViewChanged(final boolean fromNestedScroll) {
        final int layoutDirection = ViewCompat.getLayoutDirection(this);
        final int childCount = mDependencySortedChildren.size();
        for (int i = 0; i < childCount; i++) {
            final View child = mDependencySortedChildren.get(i);
            final LayoutParams lp = (LayoutParams) child.getLayoutParams();

            // Check child views before for anchor
            for (int j = 0; j < i; j++) {
                final View checkChild = mDependencySortedChildren.get(j);

                if (lp.mAnchorDirectChild == checkChild) {
                    offsetChildToAnchor(child, layoutDirection);
                }
            }

            // Did it change? if not continue
            final Rect oldRect = mTempRect1;
            final Rect newRect = mTempRect2;
            getLastChildRect(child, oldRect);
            getChildRect(child, true, newRect);
            if (oldRect.equals(newRect)) {
                continue;
            }
            recordLastChildRect(child, newRect);

            // Update any behavior-dependent views for the change
            for (int j = i + 1; j < childCount; j++) {
                final View checkChild = mDependencySortedChildren.get(j);
                final LayoutParams checkLp = (LayoutParams) checkChild.getLayoutParams();
                final Behavior b = checkLp.getBehavior();
                    //這里調(diào)用了behavior的layoutDependsOn
                if (b != null && b.layoutDependsOn(this, checkChild, child)) {
                骏令。。垄提。
                //這里調(diào)用了behavior的onDependentViewChanged
                    final boolean handled = b.onDependentViewChanged(this, checkChild, child);
                    ...
                }
            }
        }
    }

這里再說幾點(diǎn)榔袋,怎么知道哪些view發(fā)生了變化,代碼如下铡俐,就是看oldRect和 newRect 是否一致凰兑,getChildRect就是獲取view的當(dāng)前rect审丘,而getLastChildRect是獲取view的舊的rect滩报,這個(gè)比較奇怪露泊,居然知道舊的rect惭笑。

            final Rect oldRect = mTempRect1;
            final Rect newRect = mTempRect2;
            getLastChildRect(child, oldRect);
            getChildRect(child, true, newRect);
            if (oldRect.equals(newRect)) {
                continue;
            }
            recordLastChildRect(child, newRect);

看看getLastChildRect的代碼沉噩,原來CoordinatorLayout的LayoutParams里面存儲(chǔ)了mLastChildRect柱蟀〕ひ眩看上邊的L8可以知道,會(huì)記錄newRect到LayoutParams里康聂。

    void getLastChildRect(View child, Rect out) {
        final LayoutParams lp = (LayoutParams) child.getLayoutParams();
        out.set(lp.getLastChildRect());
    }

還有個(gè)問題恬汁,比如我們知道子view A發(fā)生了變化氓侧,可能有B依賴于A约巷,C依賴于A载庭,怎么去找B,C呢,看上邊L28靖榕,只要遍歷A后邊的代碼就可以了茁计,為什么星压?看看前文的mDependencySortedChildren的排序規(guī)則就知道了娜膘,B,C絕對(duì)是在A的后邊竣贪⊙菰酰可以省去找前面的view避乏,這就是mDependencySortedChildren排序的作用拍皮。

再后邊代碼就是,先判斷下b.layoutDependsOn是否返回true,然后執(zhí)行b.onDependentViewChanged

                if (b != null && b.layoutDependsOn(this, checkChild, child)) {
                盗胀。票灰。屑迂。
                //這里調(diào)用了behavior的onDependentViewChanged
                    final boolean handled = b.onDependentViewChanged(this, checkChild, child);
                    ...
                }
            }

好了惹盼,behavior的原理基本分析完了手报。有點(diǎn)繞掩蛤,但不復(fù)雜。
我個(gè)人認(rèn)為兄裂,這個(gè)實(shí)現(xiàn)過程還可以優(yōu)化一下晰奖,比如上文第一個(gè)for循環(huán)匾南,是遍歷了所有的子view蛆楞,實(shí)際上只要遍歷被依賴的子view 就好了。而第二個(gè)for循環(huán)橙数,是遍歷了child(rect變化的view)之后的所有子view灯帮,其實(shí)也沒這個(gè)必要,因?yàn)橐蕾囮P(guān)系是早就定好的迎献,可以建一個(gè)數(shù)組存儲(chǔ)哪些view依賴了child吁恍,這樣只要遍歷這個(gè)數(shù)組就可以了冀瓦。
如果是我來寫翼闽,我會(huì)給每個(gè)view設(shè)計(jì)一個(gè)依賴者數(shù)組感局,比如Aview的依賴者數(shù)組內(nèi)有B,C,就代表B依賴于A崖瞭,C依賴于A读恃。 那第一個(gè)for循環(huán)遍歷依賴者數(shù)組非空的view即可寺惫,而第二個(gè)for循環(huán)遍歷依賴者數(shù)組就好西雀。
還有一點(diǎn)艇肴,view的rect發(fā)生變化肯定在onLayout之后就知道了再悼,如果在onLayout里把發(fā)生變化的view記錄下來冲九,那么第一個(gè)for循環(huán)就可以更簡(jiǎn)單了莺奸,也沒必要在LayoutParam里面設(shè)計(jì)一個(gè)mLastChildRect了。
以上是我的個(gè)人想法温学,如有不對(duì)甚疟,歡迎指正古拴,可能代碼google認(rèn)為反正CoordinatorLayout的子view很小黄痪,所以沒必要搞那么復(fù)雜桅打。

onDependentViewRemoved

我們開篇自定布局的時(shí)候還寫了onDependentViewRemoved,那這個(gè)onDependentViewRemoved是在哪里被調(diào)用的呢挺尾?
ViewGroup內(nèi)有個(gè)mOnHierarchyChangeListener,view結(jié)構(gòu)發(fā)生變化時(shí)會(huì)觸發(fā)OnHierarchyChangeListener回調(diào)丽柿。

protected OnHierarchyChangeListener mOnHierarchyChangeListener;
    public interface OnHierarchyChangeListener {
        /**
         * Called when a new child is added to a parent view.
         *
         * @param parent the view in which a child was added
         * @param child the new child view added in the hierarchy
         */
        void onChildViewAdded(View parent, View child);

        /**
         * Called when a child is removed from a parent view.
         *
         * @param parent the view from which the child was removed
         * @param child the child removed from the hierarchy
         */
        void onChildViewRemoved(View parent, View child);
    }

再看CoordinatorLayout內(nèi)自己定義了一個(gè)HierarchyChangeListener,在onChildViewRemoved的時(shí)候會(huì)調(diào)用dispatchDependentViewRemoved涂召,這個(gè)HierarchyChangeListener在構(gòu)造函數(shù)內(nèi)set果正。所以有view被remove調(diào)的時(shí)候回回調(diào)到
dispatchDependentViewRemoved秋泳。


    private class HierarchyChangeListener implements OnHierarchyChangeListener {
        @Override
        public void onChildViewAdded(View parent, View child) {
            if (mOnHierarchyChangeListener != null) {
                mOnHierarchyChangeListener.onChildViewAdded(parent, child);
            }
        }

        @Override
        public void onChildViewRemoved(View parent, View child) {
            dispatchDependentViewRemoved(child);

            if (mOnHierarchyChangeListener != null) {
                mOnHierarchyChangeListener.onChildViewRemoved(parent, child);
            }
        }
    }

dispatchDependentViewRemoved的代碼也很簡(jiǎn)單歉闰,會(huì)根據(jù)需要觸發(fā)onDependentViewRemoved

   void dispatchDependentViewRemoved(View view) {
        final int childCount = mDependencySortedChildren.size();
        boolean viewSeen = false;
        for (int i = 0; i < childCount; i++) {
            final View child = mDependencySortedChildren.get(i);
            if (child == view) {
                // We've seen our view, which means that any Views after this could be dependent
                viewSeen = true;
                continue;
            }
            if (viewSeen) {
                CoordinatorLayout.LayoutParams lp = (CoordinatorLayout.LayoutParams)
                        child.getLayoutParams();
                CoordinatorLayout.Behavior b = lp.getBehavior();
                if (b != null && lp.dependsOn(this, child, view)) {
                    b.onDependentViewRemoved(this, child, view);
                }
            }
        }
    }

上述代碼都是為了監(jiān)聽某個(gè)view被remove而加的新娜,那為什么增加一個(gè)view的時(shí)候沒這么麻煩概龄,刪除一個(gè)view就這么麻煩呢私杜。因?yàn)樵黾恿艘粋€(gè)view衰粹,那這個(gè)view铝耻,必然在mDependencySortedChildren內(nèi)瓢捉,而刪除了一個(gè)view泡态,這個(gè)view在mDependencySortedChildren就找不到了迂卢,所以加了這一堆代碼

泛型類Behavior

要知道Behavior其實(shí)是個(gè)泛型類

    public static abstract class Behavior<V extends View> 

所以自定義Behavior可以這么寫,這樣更優(yōu)雅而克,免去了強(qiáng)轉(zhuǎn)

    public static class Behavior extends CoordinatorLayout.Behavior<FloatingActionButton> {
        // We only support the FAB <> Snackbar shift movement on Honeycomb and above. This is
        // because we can use view translation properties which greatly simplifies the code.
        private static final boolean SNACKBAR_BEHAVIOR_ENABLED = Build.VERSION.SDK_INT >= 11;

        private ValueAnimatorCompat mFabTranslationYAnimator;
        private float mFabTranslationY;
        private Rect mTmpRect;

        @Override
        public boolean layoutDependsOn(CoordinatorLayout parent,
                FloatingActionButton child, View dependency) {
            // We're dependent on all SnackbarLayouts (if enabled)
            return SNACKBAR_BEHAVIOR_ENABLED && dependency instanceof Snackbar.SnackbarLayout;
        }

        @Override
        public boolean onDependentViewChanged(CoordinatorLayout parent, FloatingActionButton child,
                View dependency) {
            if (dependency instanceof Snackbar.SnackbarLayout) {
                updateFabTranslationForSnackbar(parent, child, dependency);
            } else if (dependency instanceof AppBarLayout) {
                // If we're depending on an AppBarLayout we will show/hide it automatically
                // if the FAB is anchored to the AppBarLayout
                updateFabVisibility(parent, (AppBarLayout) dependency, child);
            }
            return false;
        }

        @Override
        public void onDependentViewRemoved(CoordinatorLayout parent, FloatingActionButton child,
                View dependency) {
            if (dependency instanceof Snackbar.SnackbarLayout) {
                updateFabTranslationForSnackbar(parent, child, dependency);
            }
        }

總結(jié)

1亮钦、view的behavior有2種方式蜂莉,xml內(nèi)指定映穗,或者注解里指定蚁滋,xml優(yōu)先級(jí)高辕录。xml內(nèi)指定的話走诞,是在inflate的時(shí)候?qū)Behavior賦值的,在注解里指定的話碑幅,是在onMeasure內(nèi)賦值的沟涨,稍有不同裹赴。
2篮昧、behavior能夠檢測(cè)到view的尺寸變化以及view被remove
3笋妥、CoordinatorLayout內(nèi)的mDependencySortedChildren里,被依賴的view放前面酵颁,比如我們fab依賴于snackbar躏惋,那么snackbar必然放在fab的前邊簿姨。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末扁位,一起剝皮案震驚了整個(gè)濱河市域仇,隨后出現(xiàn)的幾起案子暇务,更是在濱河造成了極大的恐慌,老刑警劉巖择镇,帶你破解...
    沈念sama閱讀 216,591評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件沐鼠,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡乘盖,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來穿扳,“玉大人矛物,你說我怎么就攤上這事÷臀” “怎么了爱榔?”我有些...
    開封第一講書人閱讀 162,823評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵详幽,是天一觀的道長(zhǎng)浸锨。 經(jīng)常有香客問我柱搜,道長(zhǎng),這世上最難降的妖魔是什么谎亩? 我笑而不...
    開封第一講書人閱讀 58,204評(píng)論 1 292
  • 正文 為了忘掉前任匈庭,我火速辦了婚禮,結(jié)果婚禮上夭拌,老公的妹妹穿的比我還像新娘衷咽。我一直安慰自己,他們只是感情好桶现,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,228評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著相寇,像睡著了一般。 火紅的嫁衣襯著肌膚如雪婆赠。 梳的紋絲不亂的頭發(fā)上页藻,一...
    開封第一講書人閱讀 51,190評(píng)論 1 299
  • 那天份帐,我揣著相機(jī)與錄音废境,去河邊找鬼噩凹。 笑死毡咏,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的堵泽。 我是一名探鬼主播迎罗,決...
    沈念sama閱讀 40,078評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼尤辱!你這毒婦竟也來了光督?” 一聲冷哼從身側(cè)響起塔粒,我...
    開封第一講書人閱讀 38,923評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蓄拣,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體辜昵,經(jīng)...
    沈念sama閱讀 45,334評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,550評(píng)論 2 333
  • 正文 我和宋清朗相戀三年舀锨,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了坎匿。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片替蔬。...
    茶點(diǎn)故事閱讀 39,727評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡承桥,死狀恐怖凶异,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情屯掖,我是刑警寧澤贴铜,帶...
    沈念sama閱讀 35,428評(píng)論 5 343
  • 正文 年R本政府宣布绍坝,位于F島的核電站苔悦,受9級(jí)特大地震影響玖详,放射性物質(zhì)發(fā)生泄漏蟋座。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,022評(píng)論 3 326
  • 文/蒙蒙 一巢墅、第九天 我趴在偏房一處隱蔽的房頂上張望君纫。 院中可真熱鬧蓄髓,春花似錦、人聲如沸会前。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,672評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至斋攀,卻和暖如春梧田,著一層夾襖步出監(jiān)牢的瞬間淳蔼,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,826評(píng)論 1 269
  • 我被黑心中介騙來泰國打工裁眯, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留鹉梨,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,734評(píng)論 2 368
  • 正文 我出身青樓穿稳,卻偏偏與公主長(zhǎng)得像存皂,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子逢艘,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,619評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容