Android从源码分析ScrollView自动滑动的焦点问题以及解决方案

当Android应用中切换含有ScrollView的Fragment时,ScrollView会自动滚动到顶部。本文通过源码分析,揭示了问题根源在于View显示状态变化导致焦点转移。文章提供了两种解决方案:方案一是设置根布局属性,但首次切换时仍存在问题;方案二是直接在根布局使用`android:descendantFocusability="blocksDescendants"`,阻止焦点传递,有效防止ScrollView自动滑动。
摘要由CSDN通过智能技术生成

大家做项目开放应该都碰到过类似于这种界面
这里写图片描述
这时候我们做Fragment切换的时候,如果Fragment带有像ListView和RecyclerView之类的列表,在切换的时候ScrollView会自动滑动到列表的顶部。虽然做手机app开发的时候,焦点处理比较少,但是我们可以从源码来分析一下焦点是如何传递的。

源码分析

我们两个Fragment切换的时候,最终其实还是一个View隐藏,一个View显示而已。在两个页面都加载进去了之后,我们做切换的时候一个View会GONE,一个View会VISIBLE。GONE我们后面分析,其实就是会在有焦点的时候会清空焦点。设置VISIBLE之后 。

 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;

    .....

        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);
                }
            }
        }
        .....
 }

从上面可以看到,我们会执行mParent.focusableViewAvailable(this);我们看看这个方法做了什么

public void focusableViewAvailable(View v) {
        if (mParent != null
                // shortcut: don't report a new focusable view if we block our descendants from
                // getting focus
                && (getDescendantFocusability() != FOCUS_BLOCK_DESCENDANTS)
                && (isFocusableInTouchMode() || !shouldBlockFocusForTouchscreen())
                // shortcut: don't report a new focusable view if we already are focused
                // (and we don't prefer our descendants)
                //
                // note: knowing that mFocused is non-null is not a good enough reason
                // to break the traversal since in that case we'd actually have to find
                // the f
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值