ImageVerifierCode 换一换
格式:DOCX , 页数:17 ,大小:22.31KB ,
资源ID:25560386      下载积分:3 金币
快捷下载
登录下载
邮箱/手机:
温馨提示:
快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。 如填写123,账号就是123,密码也是123。
特别说明:
请自助下载,系统不会自动发送文件的哦; 如果您已付费,想二次下载,请登录后访问:我的下载记录
支付方式: 支付宝    微信支付   
验证码:   换一换

加入VIP,免费下载
 

温馨提示:由于个人手机设置不同,如果发现不能下载,请复制以下地址【https://www.bdocx.com/down/25560386.html】到电脑端继续下载(重复下载不扣费)。

已注册用户请登录:
账号:
密码:
验证码:   换一换
  忘记密码?
三方登录: 微信登录   QQ登录  

下载须知

1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。
2: 试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。
3: 文件的所有权益归上传用户所有。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 本站仅提供交流平台,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

版权提示 | 免责声明

本文(ViewPager源码分析.docx)为本站会员(b****9)主动上传,冰豆网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知冰豆网(发送邮件至service@bdocx.com或直接QQ联系客服),我们立即给予删除!

ViewPager源码分析.docx

1、ViewPager源码分析ViewPager源码分析1.问题由于Android Framework源码很庞大,所以读源码必须带着问题来读!没有问题,创造问题再来读!否则很容易迷失在无数的方法与属性之中,最后无功而返。 那么,关于ViewPager有什么问题呢? 1. setOffsreenPageLimit()方法是如何实现页面缓存的? 2. 在布局文件中,ViewPager布局内部能否添加其他View? 3. 为什么ViewPager初始化时,显示了一个页面却不会触发onPageSelected回调?问题肯定不止这三个,但是有这三个问题基本可以找到本次分析的重点了。读者朋友也可以自己先提出一

2、些问题,再看下面的分析,看看是否可以从分析过程中找到答案。2.从onMeasure()下手ViewPager继承自ViewGroup,是Android Framework提供的一个控件,而Android系统显示控件的流程就是: Activity加载布局实例化所有控件 rootView遍历所以控件 对需要重绘的控件执行测量,布局,绘制的操作。 而转化到某个控件来说,它的流程就是:构造方法 onMeasure onLayout onDraw 由于ViewPager的构造方法中只是初始化了一些与本文主题无关的属性就略过不讲,那么自然而然onMeasure方法就来到了我们眼前。 那么在onMeasur

3、e中ViewPager做了些什么呢?先把源码摆出来,我进行了一些删减。Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) /测量ViewPager自身大小 setMeasuredDimension(getDefaultSize(0, widthMeasureSpec), getDefaultSize(0, heightMeasureSpec); final int measuredWidth = getMeasuredWidth(); / child的宽高,占满父控件 int childW

4、idthSize = measuredWidth - getPaddingLeft() - getPaddingRight(); int childHeightSize = getMeasuredHeight() - getPaddingTop() - getPaddingBottom(); /1.测量Decor int size = getChildCount(); for (int i = 0; i size; +i) final View child = getChildAt(i); if (child.getVisibility() != GONE) final LayoutParam

5、s lp = (LayoutParams) child.getLayoutParams(); if (lp != null & lp.isDecor) /仅对Decor进行测量 /省略若干代码,主要负责对Decor控件的测量 . mChildWidthMeasureSpec = MeasureSpec.makeMeasureSpec(childWidthSize, MeasureSpec.EXACTLY); mChildHeightMeasureSpec = MeasureSpec.makeMeasureSpec(childHeightSize, MeasureSpec.EXACTLY); /

6、 2.从Adapter中获取childView mInLayout = true; populate(); mInLayout = false; / 3.测量非Decor的childView size = getChildCount(); for (int i = 0; i size; +i) final View child = getChildAt(i); if (child.getVisibility() != GONE) final LayoutParams lp = (LayoutParams) child.getLayoutParams(); if (lp = null | !lp

7、.isDecor) final int widthSpec = MeasureSpec.makeMeasureSpec( (int) (childWidthSize * lp.widthFactor), MeasureSpec.EXACTLY); child.measure(widthSpec, mChildHeightMeasureSpec); 简单总结就是三件事情。2.1 测量Decor控件可能很多人有些懵x了,Decor是个啥? 其实Decor是一个接口,在ViewPager内部定义的,并且该接口是没有定义任何内容的。唯一的作用就是如果你的控件实现了Decor接口,那么你的控件就属于De

8、corView了。 我们知道ViewPager的数据是通过Adapter管理的,但其实还有一种方式给ViewPager添加childView.#layout.xml 上面这种直接在ViewPager布局内部添加控件也是可以的,但是要求DecorView必须实现Decor接口,否则将不予显示。 在ViewPager的addView方法中会对childView进行判断,也看一下代码吧! Override public void addView(View child, int index, ViewGroup.LayoutParams params) if (!checkLayoutParams(p

9、arams) params = generateLayoutParams(params); final LayoutParams lp = (LayoutParams) params; lp.isDecor |= child instanceof Decor; /在此处给isDecor赋值 /省略无关代码 . 至于addView()方法是如何调用,可以参考本人博客 ViewGroup如何加载布局中的View? 而上面的代码我们要注意的是lp.isDecor,这是ViewPager为它的childView准备的LayoutParams,在onMeasure的第一步中就是根据lp.isDecor来

10、挑选出Decor控件来测量的。 至于Decor的测量过程与本文主题无关,在此就不详述了,有兴趣的可以自己去查看源码。2.2 从Adapter中创建ChildView(populate方法)ViewPager也是采用Observable模式来设计的,数据通过PagerAdapter来管理,并且childView也是通过PagerAdapter来创建的,ViewPager主要负责界面交互相关的工作。 对PagerAdapter并不会做太详细的介绍,直接给一个示例代码吧。public class AutoScrollAdapter extends PagerAdapter /省略构造方法代码 . O

11、verride public void destroyItem(ViewGroup container, int position, Object object) Override public int getCount() return mData.size(); Override public boolean isViewFromObject(View view, Object object) return view = object; Override public Object instantiateItem(ViewGroup container, int position) Vie

12、w itemView = new TextView(mContext); /通过各种方法新建一个childView container.addView(itemView);/将childView添加到ViewPager中 return itemView; 这四个方法是必须要重写的,方法的含义根据方法名就能看出来。这里主要要讲一下最后这个方法instantiateItem()。它负责向ViewPager提供childView,这里调用的addView方法是被ViewPager重写过的,所以会对lp.isDecor赋值,并且我们可以知道,这里的isDecor=false。有些人可能要问,这一步的主

13、角不应该是populate()方法吗?的确应该是populate方法,但是由于这个方法比较复杂,为了阅读的连贯性考虑,博主决定单独提出来,一会儿再讲它。 在这里主要告诉大家,populate()方法内部会调用Adapter.instantiateItem()方法,也就是将Adapter中的childView添加到ViewPager中来,为下一步做准备。2.3 测量ChildView有了上面的分析,这一步的内容就很好理解了。 简单来说就是,遍历所有的childView,挑选出lp.isDecor=false的childView,然后调用view.measure()方法让childView自己去完

14、成测量。 还有一点需要注意,就是childView的宽度 width= childWidthSize * lp.widthFactor。childWidthSize就是ViewPager的宽度,lp.widthFactor代表这个childView占几个页面。lp.widthFactor默认情况下是1.0,可以重写PagerAdapter.getPageWidth(pos)方法来修改这个值。 到此,ViewPager的测量过程就完成了。3.populate()方法可以说这是ViewPager最核心的一个方法,所以单独作为一个小节来分析。 在分析源码之前,必须先介绍一个类ItemInfo3.1

15、ItemInfo是什么?static class ItemInfo Object object; /childView int position; /childView在Adapter中的位置 boolean scrolling; /是否在滚动 float widthFactor; /宽度的倍数,默认情况下是1 float offset; /页面的偏移参数,粗暴的理解就是第几个页面 这是ViewPager内部定义的一个静态类,将childView相关的属性进行了包装,主要是为了方便对childView的管理。 并且在ViewPager内部还维护了一个ArrayList,由ItemInfo对象组

16、成,属性名是mItems。 这个list的长度就是由mOffscreenPageLimit来决定的,这个在后面的代码分析中会看到。 好了,了解了基本对象之后,就可以开始分析populate方法了。 注意:由于代码比较长,为了方便阅读博主打算将populate()方法的代码分段讲解,如过代码中没有方法声明,则表示该段代码属于populate()方法。3.2 获取当前的ItemInfo对象从这里开始,对populate()方法的源码进行分析,分析内容主要在代码的注释中编写。 void populate(int newCurrentItem) ItemInfo oldCurInfo = null;

17、int focusDirection = View.FOCUS_FORWARD; if (mCurItem != newCurrentItem) focusDirection = mCurItem newCurrentItem ? View.FOCUS_RIGHT : View.FOCUS_LEFT; oldCurInfo = infoForPosition(mCurItem); /获取旧的ItemInfo对象 mCurItem = newCurrentItem; /更新mCurItem的值,就是在Adapter中的position /省略无关代码 . /mOffscreenPageLimit

18、就是setOffscreenPageLimit方法设置的值 final int pageLimit = mOffscreenPageLimit; /根据下面三行代码可知:mItems的长度就是 2 * pageLimit + 1 /这里声明的startPos和endPos在后面会起作用,大家注意一下 final int startPos = Math.max(0, mCurItem - pageLimit); final int N = mAdapter.getCount(); final int endPos = Math.min(N-1, mCurItem + pageLimit); /

19、遍历mItems列表,找出mCurItem对应的ItemInfo对象,是根据position来判断的 int curIndex = -1; ItemInfo curItem = null; for (curIndex = 0; curIndex = mCurItem) if (ii.position = mCurItem) curItem = ii; break; / 如果mItems中还未保存该ItemInfo,则创建一个IntemInfo对象 if (curItem = null & N 0) curItem = addNewItem(mCurItem, curIndex); .这里要注意

20、的一点是,在新建ItemInfo对象时,我们是调用的addNewItem方法,它的代码如下所示。ItemInfo addNewItem(int position, int index) ItemInfo ii = new ItemInfo(); /新建一个ItemInfo对象 ii.position = sition; ii.object = mAdapter.instantiateItem(this, position);/用Adapter创建一个childView ii.widthFactor = mAdapter.getPageWidth(position);/默认返回1.0f if (

21、index = mItems.size() /添加到mItems中 mItems.add(ii); else mItems.add(index, ii); return ii; 不管是从mItems中提取还是新建一个ItemInfo对象,总之我们已经得到了curItem,即当前的IntemInfo对象。3.3 管理mItems中的其余对象因为我们的mItems长度是有限的,并且与pageLimit有关,所以很可能出现页面总数大于mItems长度的情况。当显示的页面改变时,我们必须将一些ItemInfo添加进来,将另一些ItemInfo移除。 以保证我们的mItems中的ItemInfo.pos

22、ition是这样的: startPos mCurItem endPos 其中: mCurItem = curItem.position startPos = mCurItem - pagLimit endPos = mCurItem + pagLimit具体如何操作,我们来看代码 if (curItem != null) /1.调整curItem左边的对象 float extraWidthLeft = 0.f; / curIndex是curItem在mItems中的索引 / itemIndex就是curItem左边的ItemInfo的索引 int itemIndex = curIndex -

23、1; /获取左边的ItemInfo对象 ItemInfo ii = itemIndex = 0 ? mItems.get(itemIndex) : null; final int clientWidth = getClientWidth(); /curItem左边需要的宽度,默认情况下为1.0f final float leftWidthNeeded = clientWidth = 0 ? 0 : 2.f - curItem.widthFactor + (float) getPaddingLeft() / (float) clientWidth; /遍历mItems左半部分,即curIndex

24、左边的对象 /只有在pos = 0; pos-) / 建议大家先从下面的else if开始看,因为这里的逻辑是准备退出循环了 if (extraWidthLeft = leftWidthNeeded & pos startPos) /当pos = 0 ? mItems.get(itemIndex) : null; /如果curIndex左边的ItemInfo对象不为null else if (ii != null & pos = ii.position) extraWidthLeft += ii.widthFactor; /累加curItem左边需要的宽度 itemIndex-; /再往cur

25、Index左边移一个位置 ii = itemIndex = 0 ? mItems.get(itemIndex) : null; /取出ItemInfo对象 /如果curIndex左边的ItemInfo为null else /新建一个ItemInfo对象,添加到itemIndex的右边 ii = addNewItem(pos, itemIndex + 1); extraWidthLeft += ii.widthFactor; /累加左边宽度 curIndex+; /由于往mItems中插入了一个对象,故curIndex需要加1 ii = itemIndex = 0 ? mItems.get(it

26、emIndex) : null; /去除ItemInfo /2.调整curItem右边的对象,逻辑与上面类似 /代码省略 . / 3.计算mItems中的偏移参数 calculatePageOffsets(curItem, curIndex, oldCurInfo); 代码主要是一些逻辑,需要大家静下心来读,也不知道讲清除了没有。(发现要把代码翻译成文字真是累,一句代码要用一大段文字来说明) 对于calculatePageOffsets方法,就不贴源码分析了,主要说一下它做了哪些事情吧根据oldItem.position与curItem.position的大小关系,来确定curItem的off

27、set值再分别对curItem的左边和右边的Item写入offset值mPageMargin是页面之间的间隔, marginOffset = mPageMargin / childWidth每个页面的offset = mAdapter.getPageWidth(pos) + marginOffset参照上面的四点提示,大家去读源码应该也没啥难度的,关键是都是一些逻辑处理很难文字化说明。3.4 一些收尾工作 / 将ItemInfo的内容更新到childView的LayoutParams中 final int childCount = getChildCount(); for (int i = 0

28、; i childCount; i+) final View ld = getChildAt(i); final LayoutParams lp = (LayoutParams) child.getLayoutParams(); lp.childIndex = i; if (!lp.isDecor & lp.widthFactor = 0.f) final ItemInfo ii = infoForChild(child); if (ii != null) lp.widthFactor = ii.widthFactor; lp.position = ii.position; /根据lp.pos

29、ition的大小对所有childView进行排序,另外DecorView是排在其他child之前的 sortChildDrawingOrder();OK,populate方法分析到此就结束了。4. onLayout布局也是先布局Decor,再布局Adapter创建的childView,直接上源码吧。 Override protected void onLayout(boolean changed, int l, int t, int r, int b) final int count = getChildCount(); int width = r - l; int height = b - t;

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1