ScrollView(RecyclerView等)为什么会自动滚动原理分析,还有阻止自动滑动的解决方案(1)

如何成为Android高级架构师!

架构师必须具备抽象思维和分析的能力,这是你进行系统分析和系统分解的基本素质。只有具备这样的能力,架构师才能看清系统的整体,掌控全局,这也是架构师大局观的形成基础。 你如何具备这种能力呢?一是来自于经验,二是来自于学习。

架构师不仅要具备在问题领域上的经验,也需要具备在软件工程领域内的经验。也就是说,架构师必须能够准确得理解需求,然后用软件工程的思想,把需求转化和分解成可用计算机语言实现的程度。经验的积累是需要一个时间过程的,这个过程谁也帮不了你,是需要你去经历的。

但是,如果你有意识地去培养,不断吸取前人的经验的话,还是可以缩短这个周期的。这也是我整理架构师进阶此系列的始动力之一。


成为Android架构师必备知识技能

对应导图的学习笔记(由阿里P8大牛手写,我负责整理成PDF笔记)

部分内容展示

《设计思想解读开源框架》

  • 目录
  • 热修复设计
  • 插件化框架设计

    《360°全方面性能优化》
  • 设计思想与代码质量优化
  • 程序性能优化

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

addView的方法内部是调用了ViewGroup的addViewInner(View child, int index, LayoutParams params,boolean preventRequestLayout)方法:

android.view.ViewGroup{

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

if (child.hasFocus()) {
requestChildFocus(child, child.findFocus());
}

}
}

这里我们看到,我们在添加一个hasFocus的子view的时候,是会调用requestChildFocus方法,在这里我们需要明白view的绘制原理,是view树的层级绘制,是绘制树的最顶端,也就是子view,然后父view的机制。明白这个的话,我们再继续看ViewGroup的requestChildFocus方法,

@Override
public void requestChildFocus(View child, View focused) {
if (DBG) {
System.out.println(this + " requestChildFocus()");
}
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);
}

mFocused = child;
}
if (mParent != null) {
mParent.requestChildFocus(this, focused);
}
}

在上面会看到 mParent.requestChildFocus(this, focused);的调用,这是Android中典型的也是24种设计模式的一种(责任链模式),会一直调用,就这样,我们肯定会调用到ScrollView的requestChidlFocus方法,然后Android的ScrollView控件,重写了requestChildFocus方法:

@Override
public void requestChildFocus(View child, View focused) {
if (!mIsLayoutDirty) {
scrollToChild(focused);
} else {
mChildToScrollTo = focused;
}
super.requestChildFocus(child, focused);
}

因为在addViewInner之前调用了requestLayout()方法:

@Override
public void requestLayout() {
mIsLayoutDirty = true;
super.requestLayout();
}

所以我们在执行requestChildFocus的时候,会进入else的判断,mChildToScrollTo = focused。

2.接下来我们继续分析下mParent.requestChildFocus(this, focused)方法?

android.view.ViewGroup{
@Override
public void requestChildFocus(View child, View focused) {
if (DBG) {
System.out.println(this + " requestChildFocus()");
}
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);
}

mFocused = child;
}
if (mParent != null) {
mParent.requestChildFocus(this, focused);
}
}
}

首先,我们会判断ViewGroup的descendantFocusability属性,如果是FOCUS_BLOCK_DESCENDANTS值的话,直接就返回了(这部分后面会解释,也是android:descendantFocusability="blocksDescendants"属性能解决自动滑动的原因),我们先来看看if (mParent != null)mParent.requestChildFocus(this, focused)}成立的情况,这里会一直调用,直到调用到ViewRootImpl的requestChildFocus方法

@Override
public void requestChildFocus(View child, View focused) {
if (DEBUG_INPUT_RESIZE) {
Log.v(mTag, "Request child focus: focus now " + focused);
}
checkThread();
scheduleTraversals();
}

scheduleTraversals()会启动一个runnable,执行performTraversals方法进行view树的重绘制。

3.那么ScrollView为什么会滑到获取焦点的子view的位置了?

答:通过上面的分析,我们可以看到当Scrollview中包含有焦点的view的时候,最终会执行view树的重绘制,所以会调用view的onLayout方法,我们看下ScrollView的onLayout方法

android.view.ScrollView{
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);

if (mChildToScrollTo != null && isViewDescendantOf(mChildToScrollTo, this)) {
scrollToChild(mChildToScrollTo);
}
mChildToScrollTo = null;

}
}

从第一步我们可以看到,我们在requestChildFocus方法中,是对mChildToScrollTo进行赋值了,所以这个时候,我们会进入到if判断的执行,调用scrollToChild(mChildToScrollTo)方法:

private void scrollToChild(View child) {
child.getDrawingRect(mTempRect);
offsetDescendantRectToMyCoords(child, mTempRect);

int scrollDelta = computeScrollDeltaToGetChildRectOnScreen(mTempRect);

if (scrollDelta != 0) {
scrollBy(0, scrollDelta);
}
}

很明显,当前的方法就是将ScrollView移动到获取制定的view当中,在这里我们可以明白了,为什么ScrollView会自动滑到获取焦点的子view的位置了。

4.为什么在ScrollView的子viewGroup中增加android:descendantFocusability=”blocksDescendants”属性能阻止ScrollView的自动滑动呢?

答:如第一步所说的,view的绘制原理:是view树的层级绘制,是绘制树的最顶端,也就是子view,然后父view绘制的机制,所以我们在ScrollView的直接子view设置android:descendantFocusability=”blocksDescendants”属性的时候,这个时候直接return了,就不会再继续执行父view也就是ScrollView的requestChildFocus(View child, View focused)方法了,导致下面的自动滑动就不会触发了。

@Override
public void requestChildFocus(View child, View focused) {

if (getDescendantFocusability() == FOCUS_BLOCK_DESCENDANTS) {
return;
}


if (mParent != null) {

最后

我一直以来都有整理练习大厂面试题的习惯,有随时跳出舒服圈的准备,也许求职者已经很满意现在的工作,薪酬,觉得习惯而且安逸。

不过如果公司突然倒闭,或者部门被裁减,还能找到这样或者更好的工作吗?

我建议各位,多刷刷面试题,知道最新的技术,每三个月可以去面试一两家公司,因为你已经有不错的工作了,所以可以带着轻松的心态去面试,同时也可以增加面试的经验。

我可以将最近整理的一线互联网公司面试真题+解析分享给大家,大概花了三个月的时间整理2246页,帮助大家学习进步。

由于篇幅限制,文档的详解资料太全面,细节内容太多,所以只把部分知识点截图出来粗略的介绍,每个小节点里面都有更细化的内容!以下是部分内容截图:

部分目录截图

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 11
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值