android:textIsSelectable="true"引起的RecyclerView自動(dòng)滾動(dòng)問(wèn)題

從來(lái)都沒(méi)想過(guò)這個(gè)屬性會(huì)引起bug

問(wèn)題描述

可以看這里http://www.reibang.com/p/ff9df7c392e9
是在寫(xiě)上邊的功能的時(shí)候碰到的辉哥。最早我是沒(méi)問(wèn)題的,好像是我把狀態(tài)欄弄成透明以后就發(fā)現(xiàn)出問(wèn)題了抹锄。每次進(jìn)入頁(yè)面recyclerview會(huì)自動(dòng)往上滾動(dòng)一段距離,

奇怪,而且監(jiān)聽(tīng)onScrollStateChanged可以發(fā)現(xiàn)狀態(tài)進(jìn)去就成了2,也就是setting狀態(tài)曹货,可我根本沒(méi)進(jìn)行任何操作啊,我就設(shè)置了下數(shù)據(jù)讳推。
然后就開(kāi)始瞎折騰顶籽,想著把監(jiān)聽(tīng)刪了,額银觅,監(jiān)聽(tīng)是在滾動(dòng)以后觸發(fā)的礼饱,所以其實(shí)不設(shè)置監(jiān)聽(tīng)它也滾動(dòng)。想著是不是我給第一個(gè)item設(shè)置的addItemDecoration那個(gè)top 300的問(wèn)題?我中間有次改成200發(fā)現(xiàn)好了镊绪≡确可我改回300又不行了,反正不知道是為啥蝴韭,就放那不管了够颠。
今早想著再試試,我就找了別的類榄鉴,完事把那個(gè)類的item也改成這個(gè)類的item發(fā)現(xiàn)那個(gè)類的recyclerview也會(huì)自動(dòng)滾上去履磨。。
終于找到 是item的問(wèn)題庆尘。
那下邊就開(kāi)始把item里的東西一點(diǎn)一點(diǎn)的隱藏看看剃诅。
如下圖,就一個(gè)相對(duì)布局减余,一個(gè)textview综苔,我把相對(duì)布局invisible惩系,完事發(fā)現(xiàn)問(wèn)題還在位岔,奇怪。
我另一個(gè)item本來(lái)復(fù)制的是這個(gè)堡牡,我這次就仿這個(gè)里邊就寫(xiě)個(gè)textview抒抬,高度也一樣,結(jié)果發(fā)下那邊的沒(méi)問(wèn)題啊晤柄,然后我對(duì)比了一下擦剑,兩個(gè)好像就差了個(gè)android:textIsSelectable="true",我就把這個(gè)加上芥颈,結(jié)果發(fā)現(xiàn)那邊的也自動(dòng)滾上去了惠勒,至此終于發(fā)現(xiàn)問(wèn)題根源了。

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="wrap_content">
    <RelativeLayout
        android:id="@+id/layout_float_top"
        android:layout_width="match_parent"
        android:layout_height="150dp">
//省略
    </RelativeLayout>
    <TextView
        android:id="@+id/tv_placeholder"
        android:text="place holder"
        android:gravity="center"
        android:background="#ffffff"
android:textIsSelectable="true"
        android:layout_width="match_parent"
        android:layout_height="530dp" />
</LinearLayout>

測(cè)試

發(fā)現(xiàn)問(wèn)題根源爬坑,測(cè)試的時(shí)候我就仔細(xì)看了下纠屋,我發(fā)現(xiàn)它自動(dòng)滾上去的地方就是textview的底部,也就是本來(lái)正常狀態(tài)textview是有一部分在屏幕外邊看不見(jiàn)的盾计,它會(huì)自動(dòng)往上滾使textview完全可見(jiàn)售担。
然后我把textview高度改小,使得不滾動(dòng)也能全部可見(jiàn)署辉,果然它就不會(huì)自動(dòng)滾動(dòng)了族铆。
完事我又想了下,那我textview的高度比屏幕高度還大的情況下哭尝,會(huì)是啥效果哥攘?
測(cè)試結(jié)果,這次是textview的頂部滾動(dòng)動(dòng)屏幕最上方為止。

結(jié)論

加上android:textIsSelectable="true"以后逝淹,如果textview的高度比屏幕高度還大末捣,那么會(huì)讓textview的top滾動(dòng)到屏幕的頂部,
如果textview的高度沒(méi)有屏幕高度大【而且textview有一部分不可見(jiàn)创橄,在屏幕外邊】箩做,那么會(huì)讓textview的bottomo滾動(dòng)到屏幕的底部以使得textview完全可見(jiàn)。

其他人會(huì)碰到這問(wèn)題嗎

感覺(jué)不太會(huì)妥畏,一般人也不會(huì)沒(méi)事弄個(gè)這么高的textview邦邦。我是測(cè)試用的,替代一堆復(fù)雜的布局醉蚁,占個(gè)位置才這樣的燃辖,而又為了順手測(cè)試下文字選中功能,才加上這個(gè)网棍。

如果真有人也碰到過(guò)黔龟,可以加個(gè)好友,幾率這么低滥玷,難兄難弟以后多交流氏身。

原因

感覺(jué)是設(shè)置了selectable以后,會(huì)進(jìn)行foucus的處理惑畴,而這里會(huì)進(jìn)行invalidate進(jìn)行l(wèi)ayout的
看寫(xiě)這篇帖子蛋欣,感覺(jué)寫(xiě)的不錯(cuò),原理和scrollview其實(shí)是樣的
https://www.imooc.com/article/23067
看上邊文章就行如贷,下邊只是跟著文章走一遍記錄一下而已陷虎。
第6點(diǎn),測(cè)試發(fā)現(xiàn)是無(wú)效的杠袱,難道姿勢(shì)不對(duì)尚猿?
看下方法,確實(shí)的楣富,設(shè)置了selectable以后凿掂,textview就會(huì)獲取焦點(diǎn)的

    public void setTextIsSelectable(boolean selectable) {
        if (!selectable && mEditor == null) return; // false is default value with no edit data
        setFocusableInTouchMode(selectable);
        setFocusable(FOCUSABLE_AUTO);

        setClickable(selectable);
        setLongClickable(selectable);

    }

布局的加載過(guò)程,是從根root view開(kāi)始一個(gè)一個(gè)add子child的
看下ViewGroup的addView方法

    public void addView(View child, int index, LayoutParams params) {

        // addViewInner() will call child.requestLayout() when setting the new LayoutParams
        // therefore, we call requestLayout() on ourselves before, so that the child's request
        // will be blocked at our level
        requestLayout();
        invalidate(true);
        addViewInner(child, index, params, false);
    }

繼續(xù)

    private void addViewInner(View child, int index, LayoutParams params,
            boolean preventRequestLayout) {

        final boolean childHasFocus = child.hasFocus();
        if (childHasFocus) {
            requestChildFocus(child, child.findFocus());
        }

        if (child.hasDefaultFocus()) {
            // When adding a child that contains default focus, either during inflation or while
            // manually assembling the hierarchy, update the ancestor default-focus chain.
            setDefaultFocus(child);
        }
    }

go on

    public void requestChildFocus(View child, View focused) {
//設(shè)置了FOCUS_BLOCK_DESCENDANTS的屬性菩彬,那么就被中斷了缠劝,不會(huì)往上傳遞了,所以如果我們?cè)O(shè)置了focus的view的任何父類添加了這個(gè)屬性骗灶,就不會(huì)傳遞給scrollview了惨恭。
        if (getDescendantFocusability() == FOCUS_BLOCK_DESCENDANTS) {
            return;
        }

        // Unfocus us, if necessary
        super.unFocus(focused);

        // We had a previous notion of who had focus. Clear it.
        if (mFocused != child) {
            if (mFocused != null) {
                mFocused.unFocus(focused);
            }

//這里也能看到,如果add多個(gè)focus的child的話耙旦,最后的位置其實(shí)是最后一個(gè)child的位置脱羡。
            mFocused = child;
        }
        if (mParent != null) {
            mParent.requestChildFocus(this, focused);
        }
    }

可以看到代碼結(jié)尾又調(diào)用了parent的同名方法,也就是一層一層往上走了,直到中斷
如果沒(méi)有重寫(xiě)這個(gè)方法锉罐,那么方法還是執(zhí)行上邊的代碼
不過(guò)ScrollView帆竹,RecyclerView都重寫(xiě)了這個(gè)方法的,
ScrollView如下,可以看到對(duì)child進(jìn)行了scroll操作。

    public void requestChildFocus(View child, View focused) {
        if (focused != null && focused.getRevealOnFocusHint()) {
            if (!mIsLayoutDirty) {//這個(gè)boolean值標(biāo)記的是布局有沒(méi)有完成脓规,完成的話就是false
                scrollToChild(focused);
            } else {
                // The child may not be laid out yet, we can't compute the scroll yet
                mChildToScrollTo = focused;
            }
        }
//super方法[處理FOCUS_BLOCK_DESCENDANTS情況]是在上邊滾動(dòng)處理之后調(diào)用的栽连,
//所以給ScrollView設(shè)置android:descendantFocusability="blocksDescendants"屬性是無(wú)效的
        super.requestChildFocus(child, focused);
    }

下邊的方法處理了上邊的else情況。

    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        mIsLayoutDirty = false;
        // Give a child focus if it needs it
        if (mChildToScrollTo != null && isViewDescendantOf(mChildToScrollTo, this)) {
            scrollToChild(mChildToScrollTo);
        }
        mChildToScrollTo = null;
}

最后看下view的幾個(gè)focus方法

    public boolean isFocused() {
        return (mPrivateFlags & PFLAG_FOCUSED) != 0;
    }
    public boolean hasFocus() {
        return (mPrivateFlags & PFLAG_FOCUSED) != 0;
    }
    public boolean hasFocus() {
        return (mPrivateFlags & PFLAG_FOCUSED) != 0 || mFocused != null;
    }

ViewGroup的侨舆,先看自己是不是秒紧,是就返回自己,不是就往下找挨下,mFocused 這個(gè)變量上邊代碼有賦值

    public View findFocus() {

        if (isFocused()) {
            return this;
        }

        if (mFocused != null) {
            return mFocused.findFocus();
        }
        return null;
    }

我們看下上邊分析的viewgroup的方法

    public void requestChildFocus(View child, View focused) {


        // Unfocus us, if necessary
        super.unFocus(focused);//child有焦點(diǎn)的話熔恢,會(huì)clear掉自己的focus標(biāo)志

        // We had a previous notion of who had focus. Clear it.
        if (mFocused != child) {
            if (mFocused != null) {
                mFocused.unFocus(focused);//添加了新的focused view,上一個(gè)focused view會(huì)被清除focus標(biāo)志
            }
            mFocused = child;
        }

    }

看下unFocus

    public void clearFocus() {
        clearFocusInternal(null, true, true);
    }

//看下if條件即可臭笆,可以發(fā)現(xiàn)叙淌,如果自身是focused的話,會(huì)執(zhí)行非操作愁铺,也就是把focused屬性給去掉了
    void clearFocusInternal(View focused, boolean propagate, boolean refocus) {
        if ((mPrivateFlags & PFLAG_FOCUSED) != 0) {
            mPrivateFlags &= ~PFLAG_FOCUSED;

            if (propagate && mParent != null) {
                mParent.clearChildFocus(this);
            }

            onFocusChanged(false, 0, null);
            refreshDrawableState();

            if (propagate && (!refocus || !rootViewRequestFocus())) {
                notifyGlobalFocusCleared(this);
            }
        }
    }

早上寫(xiě)的瀏覽器關(guān)閉沒(méi)了鹰霍,懶得寫(xiě)了,看下日志帜讲,可以發(fā)現(xiàn)childFocus的調(diào)用并不是在addView里的衅谷,而且日志里打印的hasFouce都是false,可以看到是在onLayout之后操作的椒拗,而且先調(diào)用的是onFocus方法似将,從root view一層一層的往下

I: LinearLayoutTemp:addView start=========android.support.v7.widget.AppCompatTextView{5168538 V.ED..... ......ID 0,0-0,0}===false====null
I: LinearLayoutTemp:addView end=========android.support.v7.widget.AppCompatTextView{5168538 V.ED..... ......ID 0,0-0,0}===false====null
I: LinearLayoutTemp:addView start=========com.charliesong.demo0327.reader.LRTextView{1e72513 VFED..CL. ......ID 0,0-0,0 #7f0a024b app:id/tv_test}===false====null
I: LinearLayoutTemp:addView end=========com.charliesong.demo0327.reader.LRTextView{1e72513 VFED..CL. ......ID 0,0-0,0 #7f0a024b app:id/tv_test}===false====null
I: LinearLayoutTemp:addView start=========android.support.v7.widget.AppCompatTextView{c68c350 VFED..CL. ......ID 0,0-0,0 #7f0a024a app:id/tv_temp}===false====null
I: LinearLayoutTemp:addView end=========android.support.v7.widget.AppCompatTextView{c68c350 VFED..CL. ......ID 0,0-0,0 #7f0a024a app:id/tv_temp}===false====null
I: touch slop===========16
I: result0==============false/false======false==false
I: ScrollViewTemp:refreshDrawableState==========================
I: ScrollViewTemp:onMeasure===========start
I: LinearLayoutTemp:onMeasure===========start
I: LinearLayoutTemp:onMeasure==============end
I: ScrollViewTemp:onMeasure==============end
I: ScrollViewTemp:onMeasure===========start
I: LinearLayoutTemp:onMeasure===========start
I: LinearLayoutTemp:onMeasure==============end
I: ScrollViewTemp:onMeasure==============end
I: ScrollViewTemp:onLayout===========start
I: LinearLayoutTemp:onLayout===========start
I: LinearLayoutTemp:onLayout==============end
I: ScrollViewTemp:onLayout==============end
I: 0/4==========0/144
I: 5/9==========144/270
I: 10/13==========270/349
I: ScrollViewTemp:requestFocus start=======262144
I: ScrollViewTemp:onRequestFocusInDescendants start=======direction:2==null
I: ScrollViewTemp:onRequestFocusInDescendants=============com.charliesong.demo0327.reader.LRTextView{1e72513 VFED..CL. ......ID 0,30-750,596 #7f0a024b app:id/tv_test}
I: LRTextView:requestFocus start=======
I: LinearLayoutTemp:requestChildFocus==start=====com.charliesong.demo0327.reader.LRTextView{1e72513 VFED..CL. .F....ID 0,30-750,596 #7f0a024b app:id/tv_test}===========com.charliesong.demo0327.reader.LRTextView{1e72513 VFED..CL. .F....ID 0,30-750,596 #7f0a024b app:id/tv_test}
I: ScrollViewTemp:requestChildFocus==start=====com.charliesong.demo0327.reader.LinearLayoutTemp{c9829b0 V.E...... ......ID 0,0-768,655 #7f0a0132 app:id/layout1}===========com.charliesong.demo0327.reader.LRTextView{1e72513 VFED..CL. .F....ID 0,30-750,596 #7f0a024b app:id/tv_test}
I: ScrollViewTemp:requestChildFocus==end=====com.charliesong.demo0327.reader.LinearLayoutTemp{c9829b0 V.E...... ......ID 0,0-768,655 #7f0a0132 app:id/layout1}===========com.charliesong.demo0327.reader.LRTextView{1e72513 VFED..CL. .F....ID 0,30-750,596 #7f0a024b app:id/tv_test}
I: LinearLayoutTemp:requestChildFocus==end=====com.charliesong.demo0327.reader.LRTextView{1e72513 VFED..CL. .F....ID 0,30-750,596 #7f0a024b app:id/tv_test}===========com.charliesong.demo0327.reader.LRTextView{1e72513 VFED..CL. .F....ID 0,30-750,596 #7f0a024b app:id/tv_test}
I: ScrollViewTemp:onRequestFocusInDescendants end=======true
I: ScrollViewTemp:draw===========start
I: ScrollViewTemp:onDraw===========start
I: ScrollViewTemp:onDraw==============end
I: ScrollViewTemp:dispatchDraw===========start
I: LinearLayoutTemp:dispatchDraw===========start
I: onDraw===========16=========16
I: LinearLayoutTemp:dispatchDraw==============end
I: ScrollViewTemp:dispatchDraw==============end
I: ScrollViewTemp:draw==============end

看下ScrollView

    override fun requestFocus(direction: Int, previouslyFocusedRect: Rect?): Boolean {
        println("${javaClass.simpleName}:requestFocus start=======${descendantFocusability}")
        return super.requestFocus(direction, previouslyFocusedRect)
    }

//super的方法如下,因?yàn)槲覀儧](méi)有設(shè)置蚀苛,所以默認(rèn)走的是第三個(gè)FOCUS_AFTER_DESCENDANTS
    public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
        int descendantFocusability = getDescendantFocusability();

        switch (descendantFocusability) {
            case FOCUS_BLOCK_DESCENDANTS:
                return super.requestFocus(direction, previouslyFocusedRect);
            case FOCUS_BEFORE_DESCENDANTS:{}
            case FOCUS_AFTER_DESCENDANTS: {
                final boolean took = onRequestFocusInDescendants(direction, previouslyFocusedRect);
                return took ? took : super.requestFocus(direction, previouslyFocusedRect);
            }
            default:
        }
    }

然后ScrollView重寫(xiě)的如下的方法,這里會(huì)找到第一個(gè)設(shè)置了focus的view在验,我們例子里的LRTextView

    protected boolean onRequestFocusInDescendants(int direction,
            Rect previouslyFocusedRect) {

        // convert from forward / backward notation to up / down / left / right
        // (ugh).
        if (direction == View.FOCUS_FORWARD) {
            direction = View.FOCUS_DOWN;
        } else if (direction == View.FOCUS_BACKWARD) {
            direction = View.FOCUS_UP;
        }

        final View nextFocus = previouslyFocusedRect == null ?
                FocusFinder.getInstance().findNextFocus(this, null, direction) :
                FocusFinder.getInstance().findNextFocusFromRect(this,
                        previouslyFocusedRect, direction);

        if (nextFocus == null) {
            return false;
        }

        if (isOffScreen(nextFocus)) {
            return false;
        }

        return nextFocus.requestFocus(direction, previouslyFocusedRect);
    }

如何找到這個(gè)view后邊再分析,先看下最后一行代碼干啥了

    public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
        return requestFocusNoSearch(direction, previouslyFocusedRect);
    }

    private boolean requestFocusNoSearch(int direction, Rect previouslyFocusedRect) {
    //省略
        handleFocusGainInternal(direction, previouslyFocusedRect);
        return true;
    }
//下邊方法就找到了我們要的requestChildFocus堵未,然后一層一層往上走腋舌,和我們的日志一樣
    void handleFocusGainInternal(@FocusRealDirection int direction, Rect previouslyFocusedRect) {
        if ((mPrivateFlags & PFLAG_FOCUSED) == 0) {
            mPrivateFlags |= PFLAG_FOCUSED;

            if (mParent != null) {
                mParent.requestChildFocus(this, this);//這里調(diào)用的
                updateFocusedInCluster(oldFocus, direction);
            }
//省略
        }
    }

現(xiàn)在分析下咋找到那個(gè)focus的view的

        final View nextFocus = previouslyFocusedRect == null ?
                FocusFinder.getInstance().findNextFocus(this, null, direction) :
                FocusFinder.getInstance().findNextFocusFromRect(this,
                        previouslyFocusedRect, direction);
//首次加載布局的時(shí)候,那個(gè)rect是空的渗蟹,所以走的findNextFocus块饺,direction = View.FOCUS_DOWN
    public final View findNextFocus(ViewGroup root, View focused, int direction) {
        return findNextFocus(root, focused, null, direction);
    }
    private View findNextFocus(ViewGroup root, View focused, Rect focusedRect, int direction) {
        View next = null;
        ViewGroup effectiveRoot = getEffectiveRoot(root, focused);//focused為空,返回的還是root
        if (focused != null) {
            next = findNextUserSpecifiedFocus(effectiveRoot, focused, direction);
        }
        if (next != null) {
            return next;
        }
        ArrayList<View> focusables = mTempList;
        try {
            focusables.clear();
            effectiveRoot.addFocusables(focusables, direction);
//這個(gè)看viewgroup里的方法雌芽,就是找到所有focus的View添加進(jìn)來(lái)授艰,我們例子中有2個(gè),
//具體實(shí)現(xiàn)在ViewGroup方法里同名方法世落,如果child是ViewGroup淮腾,會(huì)繼續(xù)調(diào)用同名方法,添加它的子view
            if (!focusables.isEmpty()) {//最后執(zhí)行的是這句
                next = findNextFocus(effectiveRoot, focused, focusedRect, direction, focusables);
            }
        } finally {
            focusables.clear();
        }
        return next;
    }

繼續(xù)上邊的findNextFocus方法,第二個(gè)第三個(gè)參數(shù)都是null

    private View findNextFocus(ViewGroup root, View focused, Rect focused,
            int direction, ArrayList<View> focusables) {
        if (focused != null) {
        } else {
            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://是這個(gè)
                        setFocusTopLeft(root, focusedRect);
                        break;
                    }
                }
            }
        }

        switch (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);
        }
    }

//繼續(xù)往下。谷朝。洲押。。圆凰。杈帐。。专钉。娘荡。。驶沼。炮沐。。回怜。大年。
    View findNextFocusInAbsoluteDirection(ArrayList<View> focusables, ViewGroup root, View focused,
            Rect focusedRect, int direction) {
        mBestCandidateRect.set(focusedRect);
        switch(direction) {
            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.getFocusedRect(mOtherRect);//獲取的就是view的布局rect
            root.offsetDescendantRectToMyCoords(focusable, mOtherRect);//

//下邊的if里方法條件判斷太復(fù)雜了,看的腦袋大玉雾,就不研究了翔试。
            if (isBetterCandidate(direction, focusedRect, mOtherRect, mBestCandidateRect)) {
//找到第一個(gè)就把rect賦值給mBestCandidateRect,參與下一個(gè)view的比較复旬,看誰(shuí)更合適垦缅。
                mBestCandidateRect.set(mOtherRect);
                closest = focusable;
            }
        }
        return closest;
    }


判斷誰(shuí)更靠譜好像最后應(yīng)該走的這里,我們例子是FOCUS_DOWN,換句話說(shuō)驹碍,看誰(shuí)的top更接小誰(shuí)就更靠譜

    static int majorAxisDistanceRaw(int direction, Rect source, Rect dest) {
        switch (direction) {
            case View.FOCUS_LEFT:
                return source.left - dest.right;
            case View.FOCUS_RIGHT:
                return dest.left - source.right;
            case View.FOCUS_UP:
                return source.top - dest.bottom;
            case View.FOCUS_DOWN:
                return dest.top - source.bottom;
        }
        throw new IllegalArgumentException("direction must be one of "
                + "{FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, FOCUS_RIGHT}.");
    }

簡(jiǎn)單結(jié)論

從最底層的root view開(kāi)始執(zhí)行requestFocus
ViewGroup,分了3種情況壁涎,第三種是默認(rèn)的
第一種,會(huì)執(zhí)行super志秃,也就是view的同名方法怔球,換句話說(shuō)就是直接判斷自己是否可以獲取焦點(diǎn)
第二種,先super浮还,就是先判斷自己能不能獲取焦點(diǎn)竟坛,能那就自己處理,不能再往下找
第三種钧舌,相反担汤,先找最里邊的。

    public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
        int descendantFocusability = getDescendantFocusability();

        switch (descendantFocusability) {
            case FOCUS_BLOCK_DESCENDANTS:
                return super.requestFocus(direction, previouslyFocusedRect);
            case FOCUS_BEFORE_DESCENDANTS: {
                final boolean took = super.requestFocus(direction, previouslyFocusedRect);
                return took ? took : onRequestFocusInDescendants(direction, previouslyFocusedRect);
            }
            case FOCUS_AFTER_DESCENDANTS: {
                final boolean took = onRequestFocusInDescendants(direction, previouslyFocusedRect);
                return took ? took : super.requestFocus(direction, previouslyFocusedRect);
            }
            default:
        }
    }

看下第三種默認(rèn)的洼冻,ViewGroup的,默認(rèn)就是一層一層的去找的崭歧,而ScrollView是重寫(xiě)了這個(gè)方法的,上邊有分析

    protected boolean onRequestFocusInDescendants(int direction,
            Rect previouslyFocusedRect) {
        int index;
        int increment;
        int end;
        int count = mChildrenCount;
        if ((direction & FOCUS_FORWARD) != 0) {
            index = 0;
            increment = 1;
            end = count;
        } else {
            index = count - 1;
            increment = -1;
            end = -1;
        }
        final View[] children = mChildren;
        for (int i = index; i != end; i += increment) {
            View child = children[i];
            if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
                if (child.requestFocus(direction, previouslyFocusedRect)) {
                    return true;
                }
            }
        }
        return false;
    }

其他

RecyclerView滾動(dòng)是一樣的道理,最后能看到scroll的字段的

    public void requestChildFocus(View child, View focused) {
        if (!mLayout.onRequestChildFocus(this, mState, child, focused) && focused != null) {
            requestChildOnScreen(child, focused);
        }
        super.requestChildFocus(child, focused);
    }


    private void requestChildOnScreen(@NonNull View child, @Nullable View focused) {

        mLayout.requestChildRectangleOnScreen(this, child, mTempRect, !mFirstLayoutComplete,
                (focused == null));
    }



        public boolean requestChildRectangleOnScreen(RecyclerView parent, View child, Rect rect,
                boolean immediate,
                boolean focusedChildVisible) {
            int[] scrollAmount = getChildRectangleOnScreenScrollAmount(parent, child, rect,
                    immediate);
            int dx = scrollAmount[0];
            int dy = scrollAmount[1];
            if (!focusedChildVisible || isFocusedChildVisibleAfterScrolling(parent, dx, dy)) {
                if (dx != 0 || dy != 0) {
                    if (immediate) {
                        parent.scrollBy(dx, dy);
                    } else {
                        parent.smoothScrollBy(dx, dy);
                    }
                    return true;
                }
            }
            return false;
        }

RecyclerView是一樣的道理碘赖,解決辦法也都一樣驾荣,在這個(gè)view的子view里添加
android:descendantFocusability="blocksDescendants"

ListView看了下外构,并沒(méi)有處理requestChildFocus,所以沒(méi)有這個(gè)問(wèn)題播掷。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末审编,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子歧匈,更是在濱河造成了極大的恐慌垒酬,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,657評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件件炉,死亡現(xiàn)場(chǎng)離奇詭異勘究,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)斟冕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門口糕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人磕蛇,你說(shuō)我怎么就攤上這事景描。” “怎么了秀撇?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,057評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵超棺,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我呵燕,道長(zhǎng)棠绘,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,509評(píng)論 1 293
  • 正文 為了忘掉前任再扭,我火速辦了婚禮氧苍,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘霍衫。我一直安慰自己候引,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,562評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布敦跌。 她就那樣靜靜地躺著,像睡著了一般逛揩。 火紅的嫁衣襯著肌膚如雪柠傍。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,443評(píng)論 1 302
  • 那天辩稽,我揣著相機(jī)與錄音惧笛,去河邊找鬼。 笑死逞泄,一個(gè)胖子當(dāng)著我的面吹牛患整,可吹牛的內(nèi)容都是我干的拜效。 我是一名探鬼主播,決...
    沈念sama閱讀 40,251評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼各谚,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼紧憾!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起昌渤,我...
    開(kāi)封第一講書(shū)人閱讀 39,129評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤赴穗,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后膀息,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體般眉,經(jīng)...
    沈念sama閱讀 45,561評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,779評(píng)論 3 335
  • 正文 我和宋清朗相戀三年潜支,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了甸赃。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,902評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡冗酿,死狀恐怖辑奈,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情已烤,我是刑警寧澤鸠窗,帶...
    沈念sama閱讀 35,621評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站胯究,受9級(jí)特大地震影響稍计,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜裕循,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,220評(píng)論 3 328
  • 文/蒙蒙 一臣嚣、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧剥哑,春花似錦硅则、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,838評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至困介,卻和暖如春大审,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背座哩。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,971評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工徒扶, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人根穷。 一個(gè)月前我還...
    沈念sama閱讀 48,025評(píng)論 2 370
  • 正文 我出身青樓姜骡,卻偏偏與公主長(zhǎng)得像导坟,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子圈澈,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,843評(píng)論 2 354

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

  • 1惫周、通過(guò)CocoaPods安裝項(xiàng)目名稱項(xiàng)目信息 AFNetworking網(wǎng)絡(luò)請(qǐng)求組件 FMDB本地?cái)?shù)據(jù)庫(kù)組件 SD...
    陽(yáng)明先生_X自主閱讀 15,980評(píng)論 3 119
  • 我只想一直陪著你而已。 我的不知所措士败,是因?yàn)橐徊揭徊娇拷銜r(shí)闯两,你卻不理不睬。 你總是那么美谅将,笑容里總是有一種...
    隨風(fēng)而鈴閱讀 129評(píng)論 0 0
  • 10月下旬換了新個(gè)工作漾狼,上班也有一個(gè)多月了,每天依舊庸庸碌碌饥臂,毫無(wú)新意逊躁。部門里我算一個(gè)比較奇特的存在,我的工作與部...
    胡未知閱讀 158評(píng)論 0 0
  • #口語(yǔ)粉碎機(jī)#L3Day9【打卡序列03】 1.練習(xí)內(nèi)容及時(shí)長(zhǎng)1h VOA與食物相關(guān)的表達(dá)2 8-10句 聽(tīng)寫(xiě) 音...
    安墨一生Ivy閱讀 414評(píng)論 0 0