parameter must be a descendant of this view 報(bào)錯(cuò)解決方案及Android 獲取View焦點(diǎn)源碼分析

前言

最近的一系列源碼分析,都是基于一個(gè)錯(cuò)誤昧港,逐步深入源碼。這樣更有目的性的看源碼支子,思路會(huì)更清楚一點(diǎn)创肥。
網(wǎng)絡(luò)上有文章給出了有針對性的解決方案。我通過源碼給出更普通的解決思路值朋,這個(gè)問題叹侄,沒有特定的解決方案,所以只能領(lǐng)會(huì)精髓后昨登,隨機(jī)應(yīng)變趾代。

下面通過我遇到的具體問題,展開源碼的分析丰辣,所以不必太在意業(yè)務(wù)場景的相似撒强,重在領(lǐng)會(huì)精髓

報(bào)錯(cuò)

我的具體場景是禽捆,在從某一個(gè)界面跳轉(zhuǎn)到登錄界面時(shí),點(diǎn)擊輸入框EditText 時(shí)飘哨,出現(xiàn)的崩潰胚想。

java.lang.IllegalArgumentException: parameter must be a descendant of this view
        at android.view.ViewGroup.offsetRectBetweenParentAndChild(ViewGroup.java:6078)
        at android.view.ViewGroup.offsetDescendantRectToMyCoords(ViewGroup.java:6007)
        at android.view.FocusFinder.findNextFocusInAbsoluteDirection(FocusFinder.java:365)
        at android.view.FocusFinder.findNextFocus(FocusFinder.java:268)
        at android.view.FocusFinder.findNextFocus(FocusFinder.java:110)
        at android.view.FocusFinder.findNextFocus(FocusFinder.java:80)
        at android.view.ViewGroup.focusSearch(ViewGroup.java:1027)
        at android.view.ViewGroup.focusSearch(ViewGroup.java:1029)
        at android.view.ViewGroup.focusSearch(ViewGroup.java:1029)
        at android.view.ViewGroup.focusSearch(ViewGroup.java:1029)
        at android.view.ViewGroup.focusSearch(ViewGroup.java:1029)
        at android.view.ViewGroup.focusSearch(ViewGroup.java:1029)
        at android.view.ViewGroup.focusSearch(ViewGroup.java:1029)
        at android.view.ViewGroup.focusSearch(ViewGroup.java:1029)
        at android.view.ViewGroup.focusSearch(ViewGroup.java:1029)
        at android.view.ViewGroup.focusSearch(ViewGroup.java:1029)
        at android.view.View.focusSearch(View.java:10843)
        at android.widget.TextView.onCreateInputConnection(TextView.java:7862)
        at androidx.appcompat.widget.AppCompatEditText.onCreateInputConnection(AppCompatEditText.java:186)
        at android.view.inputmethod.InputMethodManager.startInputInner(InputMethodManager.java:1290)
        at android.view.inputmethod.InputMethodManager.checkFocus(InputMethodManager.java:1485)
        at android.view.inputmethod.InputMethodManager.viewClicked(InputMethodManager.java:1667)
        at android.widget.TextView.viewClicked(TextView.java:12009)
        at android.widget.TextView.onTouchEvent(TextView.java:10109)
        at android.view.View.dispatchTouchEvent(View.java:12513)
        at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3030)
        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2719)
        at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3030)
        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2719)
        at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3030)
        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2719)
        at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3030)
        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2719)
        at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3030)
        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2719)
        at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3030)
        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2719)
        at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3030)
        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2719)
        at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3030)
        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2719)
        at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3030)
        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2719)
        at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3030)
        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2719)
        at com.android.internal.policy.DecorView.superDispatchTouchEvent(DecorView.java:440)
        at com.android.internal.policy.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1830)
        at android.app.Activity.dispatchTouchEvent(Activity.java:3400)

源碼分析 Android 獲取View焦點(diǎn)的流程

深入剖析崩潰的原因,涉及到Android其他方面的知識(shí)芽隆,所以這里只分析到引出這個(gè)異常的地方

下面是跳轉(zhuǎn)到登錄界面后浊服,點(diǎn)擊輸入框EditText 時(shí),點(diǎn)擊事件層層分發(fā)胚吁,到focusSearch 在指定方向上牙躺,搜索下一個(gè)可以獲取焦點(diǎn)的View

在這里插入圖片描述
mParent.focusSearch(this, direction)有兩處實(shí)現(xiàn),分別是:RecyclerView 囤采、ViewGroup

下面分析ViewGroup中的focusSearch(this, direction)

代碼段1
    /**
     * Find the nearest view in the specified direction that wants to take
     * focus.
     *
     * @param focused The view that currently has focus
     * @param direction One of FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, and
     *        FOCUS_RIGHT, or 0 for not applicable.
     */
    @Override
    public View focusSearch(View focused, int direction) {
       
        if (isRootNamespace()) {
           //如果是根布局也就是 DecorView    
            // root namespace means we should consider ourselves the top of the
            // tree for focus searching; otherwise we could be focus searching
            // into other tabs.  see LocalActivityManager and TabHost for more info.
            return FocusFinder.getInstance().findNextFocus(this, focused, direction);
        } else if (mParent != null) {
            //不斷調(diào)用父視圖的focusSearch
            return mParent.focusSearch(focused, direction);
        }
        return null;
    }

接著調(diào)用了FocusFinder 里面的函數(shù) findNextFocus(ViewGroup root, View focused, int direction)

代碼段2
    /**
     * Find the next view to take focus in root's descendants, starting from the view
     * that currently is focused.
     * @param root Contains focused. Cannot be null.
     * @param focused Has focus now.
     * @param direction Direction to look.
     * @return The next focusable view, or null if none exists.
     */
    public final View findNextFocus(ViewGroup root, View focused, int direction) {
        return findNextFocus(root, focused, null, direction);
    }
代碼段3
    private View findNextFocus(ViewGroup root, View focused, Rect focusedRect, int direction) {
        View next = null;
        ViewGroup effectiveRoot = getEffectiveRoot(root, focused);
        if (focused != null) {
            //查找下一個(gè)由用戶指定的可獲取焦點(diǎn)的View
            //那么如何指定下一個(gè)獲取焦點(diǎn)的view呢述呐?通過View 中的一系列函數(shù) setNextFocusLeftId setNextFocusRightId setNextFocusUpId setNextFocusDownId setNextFocusForwardId 來設(shè)置
            next = findNextUserSpecifiedFocus(effectiveRoot, focused, direction);
        }
        if (next != null) {
            //如果找到符合條件的view,則返回
            return next;
        }

        // 如果沒有找到蕉毯,則通過遍歷root(也就是DecorView)下所有的可獲取焦點(diǎn)的非touch_mode的 view 
        ArrayList<View> focusables = mTempList;
        try {
            focusables.clear();
            //遍歷是從這個(gè)函數(shù)開始的乓搬,所有符合條件的view被添加到focusables
            effectiveRoot.addFocusables(focusables, direction);
            if (!focusables.isEmpty()) {
               //在focusables 中查詢,下一個(gè)可以獲取焦點(diǎn)的view
                next = findNextFocus(effectiveRoot, focused, focusedRect, direction, focusables);
            }
        } finally {
            focusables.clear();
        }
        return next;
    }

下面詳細(xì)的分析一下 effectiveRoot.addFocusables(focusables, direction);

代碼段4
    /**
     * Add any focusable views that are descendants of this view (possibly
     * including this view if it is focusable itself) to views.  If we are in touch mode,
     * only add views that are also focusable in touch mode.
     *
     * @param views Focusable views found so far
     * @param direction The direction of the focus
     */
    public void addFocusables(ArrayList<View> views, @FocusDirection int direction) {

        //這個(gè)方法有5個(gè)地方實(shí)現(xiàn)了它:DrawerLayout代虾、RecyclerView进肯、View、ViewGroup棉磨、ViewPage
        addFocusables(views, direction, isInTouchMode() ? FOCUSABLES_TOUCH_MODE : FOCUSABLES_ALL);
    }

下面主要對ViewGroup和View 中的addFocusables 進(jìn)行分析

View 中的addFocusables 函數(shù)

代碼段5
    public void addFocusables(ArrayList<View> views, @FocusDirection int direction,
            @FocusableMode int focusableMode) {
        if (views == null) {
            return;
        }
        //如果不能獲取焦點(diǎn)江掩,就不添加到views中,直接返回
        if (!canTakeFocus()) {
            return;
        }
        //如果是觸摸模式乘瓤,并且在觸摸模式下不能獲取焦點(diǎn)环形,直接返回
        //也就是說,如果不是觸摸模式或者觸摸模式下可獲取焦點(diǎn)衙傀,就添加到views
        if ((focusableMode & FOCUSABLES_TOUCH_MODE) == FOCUSABLES_TOUCH_MODE
                && !isFocusableInTouchMode()) {
            return;
        }
        views.add(this);
    }

關(guān)于TOUCH_MODE更詳細(xì)的說明抬吟,參考官方博客

ViewGroup 中的addFocusables 函數(shù)

代碼段6
    @Override
    public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
        final int focusableCount = views.size();
        //自身ViewGroup與它后代view的關(guān)系,是在后代view之前统抬、之后獲取焦點(diǎn)火本,或者不讓后臺(tái)view 獲取焦點(diǎn)
        final int descendantFocusability = getDescendantFocusability();
        final boolean blockFocusForTouchscreen = shouldBlockFocusForTouchscreen();
         //isFocusableInTouchMode() 在touchMode下 是否可以獲取或保持焦點(diǎn)
        final boolean focusSelf = (isFocusableInTouchMode() || !blockFocusForTouchscreen);

        //后代view不能獲取焦點(diǎn)
        if (descendantFocusability == FOCUS_BLOCK_DESCENDANTS) {
            //自己可以獲取焦點(diǎn)
            if (focusSelf) {
                //調(diào)用view 中的addFocusables,把當(dāng)前布局添加到views
                super.addFocusables(views, direction, focusableMode);
            }
            return;
        }

        if (blockFocusForTouchscreen) {
            focusableMode |= FOCUSABLES_TOUCH_MODE;
        }
        //在后代view之前獲取焦點(diǎn)聪建,并且自己可以獲取焦點(diǎn)
        if ((descendantFocusability == FOCUS_BEFORE_DESCENDANTS) && focusSelf) {
            //調(diào)用view 中的addFocusables钙畔,把當(dāng)前布局添加到views
            super.addFocusables(views, direction, focusableMode);
        }

        int count = 0;
        final View[] children = new View[mChildrenCount];
        for (int i = 0; i < mChildrenCount; ++i) {
            View child = mChildren[i];
            if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
               //獲取當(dāng)前view 下可見的子view
                children[count++] = child;
            }
        }
        FocusFinder.sort(children, 0, count, this, isLayoutRtl());
        for (int i = 0; i < count; ++i) {
            //遍歷子view,如果是view就添加到 views金麸,如果是viewGroup就再次調(diào)用addFocusables進(jìn)行判斷
            children[i].addFocusables(views, direction, focusableMode);
        }

        // When set to FOCUS_AFTER_DESCENDANTS, we only add ourselves if
        // there aren't any focusable descendants.  this is
        // to avoid the focus search finding layouts when a more precise search
        // among the focusable children would be more interesting.
        //在后代view之后獲取焦點(diǎn)擎析,并且自己可以獲取焦點(diǎn) 并且僅在沒有可聚焦后代(views的數(shù)量沒有變)的情況下添加自己
        if ((descendantFocusability == FOCUS_AFTER_DESCENDANTS) && focusSelf
                && focusableCount == views.size()) {
            super.addFocusables(views, direction, focusableMode);
        }
    }

這篇文章所給出的解決方法,就是

代碼段3 中effectiveRoot.addFocusables(focusables, direction);調(diào)用后 挥下,focusables 中是所有可獲取焦點(diǎn)的View叔锐,在非空的情況下調(diào)用如下代碼findNextFocus函數(shù)

代碼段7
    private View findNextFocus(ViewGroup root, View focused, Rect focusedRect,
            int direction, ArrayList<View> focusables) {
        if (focused != null) {
            //本篇文章分析的流程挪鹏,focused 不為空
            if (focusedRect == null) {
                focusedRect = mFocusedRect;
            }
            // fill in interesting rect from focused
            //獲取focused所在的矩形區(qū)域到mOtherRect中
            focused.getFocusedRect(focusedRect);
            //把focused的坐標(biāo)置侍,轉(zhuǎn)換為相對于root的坐標(biāo)
            root.offsetDescendantRectToMyCoords(focused, focusedRect);
        } else {
           //如果focused  為空惭缰,就在root布局的指定方向添加一個(gè)focusedRect
            if (focusedRect == null) {
                focusedRect = mFocusedRect;
                // make up a rect at top left or bottom right of root
                switch (direction) {
                    case View.FOCUS_RIGHT:
                    case View.FOCUS_DOWN:
                        setFocusTopLeft(root, focusedRect);
                        break;
                    case View.FOCUS_FORWARD:
                        if (root.isLayoutRtl()) {
                            setFocusBottomRight(root, focusedRect);
                        } else {
                            setFocusTopLeft(root, focusedRect);
                        }
                        break;

                    case View.FOCUS_LEFT:
                    case View.FOCUS_UP:
                        setFocusBottomRight(root, focusedRect);
                        break;
                    case View.FOCUS_BACKWARD:
                        if (root.isLayoutRtl()) {
                            setFocusTopLeft(root, focusedRect);
                        } else {
                            setFocusBottomRight(root, focusedRect);
                        break;
                    }
                }
            }
        }

        switch (direction) {
            case View.FOCUS_FORWARD:
            case View.FOCUS_BACKWARD:
                return findNextFocusInRelativeDirection(focusables, root, focused, focusedRect,
                        direction);
            case View.FOCUS_UP:
            case View.FOCUS_DOWN:
            case View.FOCUS_LEFT:
            case View.FOCUS_RIGHT:
                return findNextFocusInAbsoluteDirection(focusables, root, focused,
                        focusedRect, direction);
            default:
                throw new IllegalArgumentException("Unknown direction: " + direction);
        }
    }
  • 如果focused不是null,說明當(dāng)前獲取到焦點(diǎn)的View存在愈案,則獲得繪制焦點(diǎn)的Rect到focusedRect步责,然后根據(jù)rootView遍歷所有ParentView從子View糾正坐標(biāo)到根View坐標(biāo)返顺。
  • 如果focused是null,則說明當(dāng)前沒有View獲取到焦點(diǎn)蔓肯,則把focusedRect根據(jù)不同的direction重置為“一點(diǎn)”遂鹊。

根據(jù)direction調(diào)用FocusFinder::findNextFocusInAbsoluteDirection方法進(jìn)行對比查找“下一個(gè)”View。

代碼段8
    View findNextFocusInAbsoluteDirection(ArrayList<View> focusables, ViewGroup root, View focused,
            Rect focusedRect, int direction) {
        // initialize the best candidate to something impossible
        // (so the first plausible view will become the best choice)
        //先設(shè)置focusedRect 為最佳的候選矩陣
        mBestCandidateRect.set(focusedRect);
        //根據(jù)不同的方向蔗包,偏移一個(gè)像素秉扑,為了方便比較?
        switch(direction) {
            case View.FOCUS_LEFT:
                mBestCandidateRect.offset(focusedRect.width() + 1, 0);
                break;
            case View.FOCUS_RIGHT:
                mBestCandidateRect.offset(-(focusedRect.width() + 1), 0);
                break;
            case View.FOCUS_UP:
                mBestCandidateRect.offset(0, focusedRect.height() + 1);
                break;
            case View.FOCUS_DOWN:
                mBestCandidateRect.offset(0, -(focusedRect.height() + 1));
        }

        View closest = null;

        int numFocusables = focusables.size();
        for (int i = 0; i < numFocusables; i++) {
            View focusable = focusables.get(i);

            // only interested in other non-root views
            if (focusable == focused || focusable == root) continue;

            // get focus bounds of other view in same coordinate system
             //獲取focusable所在的矩形區(qū)域到mOtherRect中调限,這個(gè)focusable是之前獲取的可聚焦的views
            focusable.getFocusedRect(mOtherRect);
            //把focusable的坐標(biāo)(矩陣)舟陆,轉(zhuǎn)換為相對于root的坐標(biāo)(矩陣)
            root.offsetDescendantRectToMyCoords(focusable, mOtherRect);
            //mOtherRect 是否比mBestCandidateRect 更優(yōu)
            if (isBetterCandidate(direction, focusedRect, mOtherRect, mBestCandidateRect)) {
                //如果mOtherRect 更優(yōu),則mBestCandidateRect設(shè)置為mOtherRect 耻矮,for循環(huán)結(jié)束后秦躯,得到最優(yōu)的
                mBestCandidateRect.set(mOtherRect);
                closest = focusable;
            }
        }
        //返回最優(yōu)的view
        return closest;
    }
代碼段9
    /**
     * Offset a rectangle that is in a descendant's coordinate
     * space into our coordinate space.
     * @param descendant A descendant of this view
     * @param rect A rectangle defined in descendant's coordinate space.
     */
    public final void offsetDescendantRectToMyCoords(View descendant, Rect rect) {
        offsetRectBetweenParentAndChild(descendant, rect, true, false);
    }
代碼段10
    /**
     * Helper method that offsets a rect either from parent to descendant or
     * descendant to parent.
     */
    void offsetRectBetweenParentAndChild(View descendant, Rect rect,
            boolean offsetFromChildToParent, boolean clipToBounds) {

        // already in the same coord system :)
        if (descendant == this) {
            return;
        }

        ViewParent theParent = descendant.mParent;

        // search and offset up to the parent
        //通過不斷的循環(huán),把descendant的坐標(biāo)裆装,也就是矩陣rect踱承,轉(zhuǎn)換為相對于當(dāng)前view(因?yàn)閛ffsetRectBetweenParentAndChild是view的方法)的坐標(biāo)(矩陣)
        while ((theParent != null)
                && (theParent instanceof View)
                && (theParent != this)) {
            
            if (offsetFromChildToParent) {
               //偏移矩陣,例如:布局viewGoupA 里面有viewGoupB 里面有 view C
               //把view C 相對于父布局viewGoupB的坐標(biāo)轉(zhuǎn)換為相對于viewGoupA的坐標(biāo)
               //這里mLeft 相對于父布局的x坐標(biāo)哨免,-x方向滾動(dòng)的距離mScrollX茎活,才是原來真是的位置
                rect.offset(descendant.mLeft - descendant.mScrollX,
                        descendant.mTop - descendant.mScrollY);
                if (clipToBounds) {
                   //修剪矩陣
                    View p = (View) theParent;
                    //intersect  壓緊到公共區(qū)域
                    boolean intersected = rect.intersect(0, 0, p.mRight - p.mLeft,
                            p.mBottom - p.mTop);
                    if (!intersected) {
                        rect.setEmpty();
                    }
                }
            } else {
                 //上面的反向操作,布局viewGoupA 里面有viewGoupB 里面有 view C
                 //view C 已經(jīng)轉(zhuǎn)為相對viewGoupA的坐標(biāo)了琢唾,下面的操作就是轉(zhuǎn)為相對于viewGoupB的坐標(biāo)
                if (clipToBounds) {
                    View p = (View) theParent;
                    boolean intersected = rect.intersect(0, 0, p.mRight - p.mLeft,
                            p.mBottom - p.mTop);
                    if (!intersected) {
                        rect.setEmpty();
                    }
                }
                //偏移矩陣载荔,從父view 到子view
                rect.offset(descendant.mScrollX - descendant.mLeft,
                        descendant.mScrollY - descendant.mTop);
            }

            descendant = (View) theParent;
            theParent = descendant.mParent;
        }

        // now that we are up to this view, need to offset one more time
        // to get into our coordinate space
        if (theParent == this) {
            if (offsetFromChildToParent) {
                rect.offset(descendant.mLeft - descendant.mScrollX,
                        descendant.mTop - descendant.mScrollY);
            } else {
                rect.offset(descendant.mScrollX - descendant.mLeft,
                        descendant.mScrollY - descendant.mTop);
            }
        } else {
           //經(jīng)過我的分析,這個(gè)錯(cuò)誤只有兩種情況
            throw new IllegalArgumentException("parameter must be a descendant of this view");
        }
    }

<font color =blue>經(jīng)過我的分析慧耍,這個(gè)錯(cuò)誤只有兩種情況:

  1. descendant 不是當(dāng)前view 的后代 ,(theParent instanceof View) == false 退出循環(huán) ,descendant 為ViewRootImpl (就是這個(gè)解決方法[Another java.lang.IllegalArgumentException: parameter must be a descendant of this view](https://stackoverflow.com/questions/30585561/another-java-lang-illegalargumentexception-parameter-must -be-a-descendant-of-th))
  2. descendant 的mParent為空 (theParent != null) == false 退出循環(huán)丐谋。我遇到的就是這個(gè)問題


    在這里插入圖片描述

下面繼續(xù)回到代碼段8芍碧,來看看函數(shù)isBetterCandidate是怎么比較出更優(yōu)的rect

代碼段11
    /**
     * Is rect1 a better candidate than rect2 for a focus search in a particular
     * direction from a source rect?  This is the core routine that determines
     * the order of focus searching.
     * @param direction the direction (up, down, left, right)
     * @param source The source we are searching from
     * @param rect1 The candidate rectangle
     * @param rect2 The current best candidate.
     * @return Whether the candidate is the new best.
     */
    boolean isBetterCandidate(int direction, Rect source, Rect rect1, Rect rect2) {

        // to be a better candidate, need to at least be a candidate in the first
        // place :)
        //rect1是否在source的指定方向direction的下一個(gè)可獲得焦點(diǎn)的矩陣
        if (!isCandidate(source, rect1, direction)) {
           //rect1不是候選的,說明rect1 沒有rect2 更優(yōu)号俐,返回false
            return false;
        }

        // we know that rect1 is a candidate.. if rect2 is not a candidate,
        // rect1 is better
        if (!isCandidate(source, rect2, direction)) {
          //rect2不是候選的泌豆,說明rect1 比rect2 更優(yōu),返回true
            return true;
        }


        //如果都是候選的吏饿,比較rect1 和rect2 哪個(gè)更優(yōu)踪危,比較的方法大概是:兩個(gè)候選rect分表與source比較蔬浙,是否重疊,是否在希望的方向上等
        // if rect1 is better by beam, it wins
        if (beamBeats(direction, source, rect1, rect2)) {
            return true;
        }

        // if rect2 is better, then rect1 cant' be :)
        if (beamBeats(direction, source, rect2, rect1)) {
            return false;
        }

        // otherwise, do fudge-tastic comparison of the major and minor axis
        return (getWeightedDistanceFor(
                        majorAxisDistance(direction, source, rect1),
                        minorAxisDistance(direction, source, rect1))
                < getWeightedDistanceFor(
                        majorAxisDistance(direction, source, rect2),
                        minorAxisDistance(direction, source, rect2)));
    }

解決方法:

根據(jù)上面的原因贞远,對應(yīng)兩種解決方法:
1畴博、就是可獲取焦點(diǎn)的view是在報(bào)錯(cuò)View的后代
2、保證可獲取焦點(diǎn)view的mParent 不為null

這篇文章蓝仲,與我分析的第二種解決方案一樣俱病,他給出的解決方案更為具體,可以參考
【原創(chuàng)】【ViewFlow+GridView】Parameter must be a descendant of this view問題分析

這篇文章袱结,分析了本篇文章中亮隙,未涉及到的其他的幾處代碼實(shí)現(xiàn)
Android焦點(diǎn)流程代碼分析

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市垢夹,隨后出現(xiàn)的幾起案子溢吻,更是在濱河造成了極大的恐慌,老刑警劉巖果元,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件促王,死亡現(xiàn)場離奇詭異,居然都是意外死亡噪漾,警方通過查閱死者的電腦和手機(jī)硼砰,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來欣硼,“玉大人题翰,你說我怎么就攤上這事≌┦ぃ” “怎么了豹障?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長焦匈。 經(jīng)常有香客問我血公,道長,這世上最難降的妖魔是什么缓熟? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任累魔,我火速辦了婚禮,結(jié)果婚禮上够滑,老公的妹妹穿的比我還像新娘垦写。我一直安慰自己,他們只是感情好彰触,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布梯投。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪分蓖。 梳的紋絲不亂的頭發(fā)上尔艇,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天,我揣著相機(jī)與錄音么鹤,去河邊找鬼终娃。 笑死,一個(gè)胖子當(dāng)著我的面吹牛午磁,可吹牛的內(nèi)容都是我干的尝抖。 我是一名探鬼主播,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼迅皇,長吁一口氣:“原來是場噩夢啊……” “哼昧辽!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起登颓,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤搅荞,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后框咙,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體咕痛,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年喇嘱,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了茉贡。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,059評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡者铜,死狀恐怖腔丧,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情作烟,我是刑警寧澤愉粤,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站拿撩,受9級特大地震影響衣厘,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜压恒,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一影暴、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧探赫,春花似錦型宙、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至讨勤,卻和暖如春箭跳,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背潭千。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工谱姓, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人刨晴。 一個(gè)月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓屉来,卻偏偏與公主長得像,于是被迫代替她去往敵國和親狈癞。 傳聞我的和親對象是個(gè)殘疾皇子茄靠,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評論 2 345

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