何时调用getView?——从源码的角度给出解答
先来看ListView类中的makeAndAddView方法:
没有数据变化:从mRecycler中取得可视的view
数据有变化:obtainView
/**
* 获取视图填充到列表的item中去,视图可以是从未使用过的视图转换过来,也可以是从回收站复用的视图。
* 在该方法中,先查找是否有可重用视图,如果有,使用可重用视图。
* 然后通过obtainView方法获取一个view(有可能是从未使用视图转换过来
* (obtainView方法是在AbsListView方法中定义)),再重新测量和定位View。
*/
private View makeAndAddView(int position, int y, boolean flow, int childrenLeft,
boolean selected) {
View child;
// 没有数据变化:从mRecycler中取得可视的view
if (!mDataChanged) {
// Try to use an existing view for this position
child = mRecycler.getActiveView(position);
...
}
// 生成view,回收旧view和调用mApapter.getView的地方(AbsListView)
child = obtainView(position, mIsScrap);
...
return child;
}
第11行调用了obtainView方法,该方法的实现是在package android.widget;的AbsListView类中
View obtainView(int position, boolean[] isScrap) {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "obtainView"); isScrap[0] = false;
View scrapView; scrapView = mRecycler.getTransientStateView(position);
if (scrapView == null) {
// 从回收站回收一个View
scrapView = mRecycler.getScrapView(position);
} View child;
if (scrapView != null) {
// 这里调用了getView!注意,第二个参数也就是convertView,传入的是刚才从回收站中回收的View(如果有的话)
child = mAdapter.getView(position, scrapView, this);
...
return child;
}
第16行调用了getView!根据Java多态的特性,实际执行的getView将会是我们自定义BaseAdapter中的那个getView方法。
好,现在虽然找到getView的直接调用者了,问题来了,何时去触发makeAndAddView并调用getView呢?
我们首先来看ListView中的fillDown:自顶至底去填充ListView
/**
填充从pos到list底部所有的item。里面调用到了makeAndAddView方法:
*/
private View fillDown(int pos, int nextTop) {
...
6 // 这里调用到了makeAndAddView方法
View child = makeAndAddView(pos, nextTop, true, mListPadding.left, selected);
...
return selectedView;
}
有fillDown自然就有fillUp:
private View fillUp(int pos, int nextBottom) {
View selectedView = null;
...
// 调用makeAndAddView
View child = makeAndAddView(pos, nextBottom, false, mListPadding.left, selected);
...
return selectedView;
}
还有fillFromTop、fillFromMiddle、fillAboveAndBelow、fillFromSelection等,这些方法都是用来进行子元素布局的,区别是布局模式不同而已。
好了,现在布局子元素的方法有了,那么谁来触发这些方法呢?
通过查找ListView源码,发现刚才的那些方法在layoutChildren()中基本上都出现了。
@Override
protected void layoutChildren() {
...
// 根据mLayoutMode的值来决定布局模式
switch (mLayoutMode) {
case LAYOUT_SET_SELECTION:
if (newSel != null) {
sel = fillFromSelection(newSel.getTop(), childrenTop, childrenBottom);
} else {
sel = fillFromMiddle(childrenTop, childrenBottom);
}
break;
case LAYOUT_SYNC:
sel = fillSpecific(mSyncPosition, mSpecificTop);
break;
case LAYOUT_FORCE_BOTTOM:
sel = fillUp(mItemCount - 1, childrenBottom);
adjustViewsUpOrDown();
break;
case LAYOUT_FORCE_TOP:
mFirstPosition = 0;
sel = fillFromTop(childrenTop);
adjustViewsUpOrDown();
break;
case LAYOUT_SPECIFIC:
sel = fillSpecific(reconcileSelectedPosition(), mSpecificTop);
break;
case LAYOUT_MOVE_SELECTION:
sel = moveSelection(oldSel, newSel, delta, childrenTop, childrenBottom);
break;
default:// 默认的布局顺序是从上往下
if (childCount == 0) {
if (!mStackFromBottom) {
final int position = lookForSelectablePosition(0, true);
setSelectedPositionInt(position);
sel = fillFromTop(childrenTop);
} else {
final int position = lookForSelectablePosition(mItemCount - 1, false);
setSelectedPositionInt(position);
sel = fillUp(mItemCount - 1, childrenBottom);
}
} else {
if (mSelectedPosition >= 0 && mSelectedPosition < mItemCount) {
sel = fillSpecific(mSelectedPosition,
oldSel == null ? childrenTop : oldSel.getTop());
} else if (mFirstPosition < mItemCount) {
sel = fillSpecific(mFirstPosition,
oldFirst == null ? childrenTop : oldFirst.getTop());
} else {
sel = fillSpecific(0, childrenTop);
}
}
break;
} ...
}
继续查找,我们发现layoutChildren的调用者是onFocusChanged、setSelectionInt、父类AbsListView中的onTouchMove、onLayout(这个比较特殊,后文会说明)等,说明当ListView的焦点发生变化时、选中某一项、或者滑动ListView时都会触发ListView的layoutChildren()去布局子元素。
到此为止我们已经很清楚getView的调用时机了,根据掌握的知识点,我们很自然能想到,当初始化一个ListView时,getView的调用也是避免不了的。这是因为ListView在初始化时肯定会绑定一个adapter,即调用语句listview.setAdapter(adapter),我们看一下setAdapter的源码:
@Override
public void setAdapter(ListAdapter adapter) {
if (mAdapter != null && mDataSetObserver != null) {
mAdapter.unregisterDataSetObserver(mDataSetObserver);
}
// 去除原有adapter、观察者、选中项等信息
resetList();
mRecycler.clear();
// 包装adapter,加header或footer,并绑定到当前ListView
if (mHeaderViewInfos.size() > 0|| mFooterViewInfos.size() > 0) {
mAdapter = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, adapter);
} else {
mAdapter = adapter;
}
// 重置选中项信息
mOldSelectedPosition = INVALID_POSITION;
mOldSelectedRowId = INVALID_ROW_ID; // AbsListView#setAdapter will update choice mode states.
super.setAdapter(adapter); if (mAdapter != null) {
mAreAllItemsSelectable = mAdapter.areAllItemsEnabled();
mOldItemCount = mItemCount;
mItemCount = mAdapter.getCount();
checkFocus();
// 重新注册观察者
mDataSetObserver = new AdapterDataSetObserver();
mAdapter.registerDataSetObserver(mDataSetObserver);
// 设置回收器中类型不同的View数目,这里与getView的回收机制紧密相关,值得深究
mRecycler.setViewTypeCount(mAdapter.getViewTypeCount());
// 设置初始选中项
int position;
if (mStackFromBottom) {
position = lookForSelectablePosition(mItemCount - 1, false);
} else {
position = lookForSelectablePosition(0, true);
}
setSelectedPositionInt(position);
setNextSelectedPositionInt(position); if (mItemCount == 0) {
// Nothing selected
checkSelectionChanged();
}
} else {
mAreAllItemsSelectable = true;
checkFocus();
// Nothing selected
checkSelectionChanged();
}
// 请求布局重绘
requestLayout();
}
通读setAdapter源码,我们发现其中并未出现生成新子视图,即调用mAdapter.getView的语句或相关方法,说明此时ListView并未包含子视图。那么疑问来了,ListView是如何在初始化的时候生成子视图的,也就是说第一屏的视图是如何加载到屏幕上的?往后看,我们发现在第53行调用了requestLayout请求布局重绘,我们知道requestLayout最终会去调用onMeasure、onLayout、onDraw方法,因此我们猜测会不会是在onMeasure、onLayout、onDraw某个方法中生成了子视图?
答案是肯定的,AbsListVIew.onLayout过程与普通视图的layout过程完全不同,如下:
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
...
layoutChildren();
...
}
该方法调用了layoutChildren();,即重新布局ListView列表视图。
由此说明调用requestLayout可以实现ListView列表视图的重新布局,这里联想到adapter.notifyDataSetChanged也会调用requestLayout,从而都能实现ListView的刷新。
以上过程只是个人探索,并非绝对正确,如有差错敬请批评指正,谢谢。
参考文献:
Android ListView工作原理完全解析,带你从源码的角度彻底理解
何时调用getView?——从源码的角度给出解答的更多相关文章
- Android AsyncTask完全解析,带你从源码的角度彻底理解
转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/11711405 我们都知道,Android UI是线程不安全的,如果想要在子线程里进 ...
- [转]Android事件分发机制完全解析,带你从源码的角度彻底理解(上)
Android事件分发机制 该篇文章出处:http://blog.csdn.net/guolin_blog/article/details/9097463 其实我一直准备写一篇关于Android事件分 ...
- 从源码的角度分析ViewGruop的事件分发
从源码的角度分析ViewGruop的事件分发. 首先我们来探讨一下,什么是ViewGroup?它和普通的View有什么区别? 顾名思义,ViewGroup就是一组View的集合,它包含很多的子View ...
- 从源码的角度解析View的事件分发
有好多朋友问过我各种问题,比如:onTouch和onTouchEvent有什么区别,又该如何使用?为什么给ListView引入了一个滑动菜单的功能,ListView就不能滚动了?为什么图片轮播器里的图 ...
- 从源码的角度看Activity是如何启动的
欢迎访问我的个人博客,原文链接:http://wensibo.top/2017/07/03/Binder/ ,未经允许不得转载! 大家好,今天想与大家一起分享的是Activity.我们平时接触的最多的 ...
- 第九节:从源码的角度分析MVC中的一些特性及其用法
一. 前世今生 乍眼一看,该标题写的有点煽情,最近也是在不断反思,怎么能把博客写好,让人能读下去,通俗易懂,深入浅出. 接下来几个章节都是围绕框架本身提供特性展开,有MVC程序集提供的,也有其它程序集 ...
- 从源码的角度看 React JS 中批量更新 State 的策略(下)
这篇文章我们继续从源码的角度学习 React JS 中的批量更新 State 的策略,供我们继续深入学习研究 React 之用. 前置文章列表 深入理解 React JS 中的 setState 从源 ...
- 从源码的角度看 React JS 中批量更新 State 的策略(上)
在之前的文章「深入理解 React JS 中的 setState」与 「从源码的角度再看 React JS 中的 setState」 中,我们分别看到了 React JS 中 setState 的异步 ...
- 从源码的角度再学「Thread」
前言 Java中的线程是使用Thread类实现的,Thread在初学Java的时候就学过了,也在实践中用过,不过一直没从源码的角度去看过它的实现,今天从源码的角度出发,再次学习Java Thread, ...
随机推荐
- Vue组件穿透
1. element ui怎么修改单独一个label的颜色 用 >>> 进行组件的穿透
- bzoj千题计划127:bzoj1041: [HAOI2008]圆上的整点
http://www.lydsy.com/JudgeOnline/problem.php?id=1041 设 X>0 ,Y>0 X^2 + Y^2 = R^2 X^2 = R^2-Y^2 ...
- Mockserver -MOCO的使用
转自: http://blog.csdn.net/shensky711/article/details/52770686
- Java并发编程原理与实战四十:JDK8新增LongAdder详解
传统的原子锁AtomicLong/AtomicInt虽然也可以处理大量并发情况下的计数器,但是由于使用了自旋等待,当存在大量竞争时,会存在大量自旋等待,而导致CPU浪费,而有效计算很少,降低了计算效率 ...
- python核心编程笔记——Chapter5
好吧,开始第五章习题: 5-2.又是老调重谈,raw_input这个函数真是非常麻烦,返回是str类型,没办法,只能在函数里面使用int内建.(就只是一道简单的两数相乘,哭了) #!/usr/bin/ ...
- c++刷题(15/100)矩阵转置,最深子树
题目一:矩阵转置 给定一个矩阵 A, 返回 A 的转置矩阵. 矩阵的转置是指将矩阵的主对角线翻转,交换矩阵的行索引与列索引. 示例 1: 输入:[[1,2,3],[4,5,6],[7,8,9]] 输出 ...
- JavaScript事件冒泡与捕获
event.preventDefault(); 如果event.cancelable的值为true,可以取消默认事件 event.cancelable; 元素是否可以取消 ...
- sqlplus设置长度
1.set linesize 100 2.col XX format a30 3.col XXX format 9,999,999,999 3.set heading off 表头不显示
- 【比赛游记】THUWC2019酱油记
往期回顾:THUSC2018酱油记 day 0 早上 7 点的动车,不知道是从哪儿到哪儿的(雾),只知道从福建到广东 233333 一个值得思考的问题:福建人会不会被广东人吃啊? 动车上玩空洞骑士,可 ...
- 邮件伪造测试-Swaks
1. 前言 在kali中自带一个邮件伪造工具Swaks,工具项目主页为 http://jetmore.org/john/code/swaks 2.基本用法: swaks --to --from --e ...