PullToRefresh源码分析(I)
来源:互联网 发布:java如何制作图片上传 编辑:程序博客网 时间:2024/06/12 21:40
最近提测期,开发任务相对轻松。作为一个Android菜鸟,就要利用好这些时间来提升自己。
虽然博客写得很烂,但是也得写。不为给多少人带去帮助,只为重新整理一下自己的思路。如果有人看且觉得有益的话,我很高兴的。
恩。那么开始。拿到PullToRefresh这么大一坨代码的时候, 我先做的是找到其核心类,然后找出其关系(集成、实现等),作为一条主线。把核心类的成员变量和函数大致列出来。
然后它是下面这样的:
PullToRefreshBase 总共1600多行,而PullToRefreshAdapterView 400多行,于是我决定从后者看起。
先看看它集成OnScrollListener都干了些什么。OnScrollListener一共就2个方法,找到对应的实现代码,先看onScroll:
<span style="white-space:pre"></span>if (null != mOnLastItemVisibleListener) {mLastItemVisible = (totalItemCount > 0) && (firstVisibleItem + visibleItemCount >= totalItemCount - 1);}// If we're showing the indicator, check positions...if (getShowIndicatorInternal()) {updateIndicatorViewsVisibility();}// Finally call OnScrollListener if we have oneif (null != mOnScrollListener) {mOnScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);}很清楚,一共干了3件事情:
第1件:给mLastItemVisible赋值(有没有滑到底端)
第2件:如果需要显示mIndicatorIvTop和mIndicatorBottom,更新它们的状态
第3件:如果我们调用过setOnScrollListener的话,这里会执行回调
总结一下就是滑动的时候更新一下mIndicatorIvTop和mIndicatorBottom的状态,然后回调我们自己写的onScroll方法。
再看onScrollStateChanged()方法,它回调了我们set进去(当然也可以不set,那就什么都没做)的2个listener的方法。
<span style="white-space:pre"></span>public final void onScrollStateChanged(final AbsListView view, final int state) {if (state == OnScrollListener.SCROLL_STATE_IDLE && null != mOnLastItemVisibleListener && mLastItemVisible) {mOnLastItemVisibleListener.onLastItemVisible();}if (null != mOnScrollListener) {mOnScrollListener.onScrollStateChanged(view, state);}}到这里可以得出结论,实现OnScrollListener接口,主要是为了给使用者一些扩展的空间,可以去实现一些自己想要的逻辑。
到这里还剩3个关键成员变量没有分析,分别是mIndicatorIvTop、mIndicatorBottom和mEmptyView。
先看mIndicatorIvTop、mIndicatorBottom,找到它们对应的类。
成员变量中有1个ImageView,4个动画。6个成员方法中使用了到了这4个动画。很明显IndicatorLayout是下拉刷新和松开释放时显示的动画效果。
在之前的onScroll方法中调用了updateIndicatorViewsVisibility(),而updateIndicatorViewsVisibility()中正是调用此处的show()和hide()方法来实现状态更新。
而releaseToRefresh()和pullToRefresh()分别在PullToRefreshAdapterViewBase的onPullToRefresh()和onReleaseToRefresh()中调用,选择其中1个在这里分析一下:
<span style="white-space:pre"></span>@Overrideprotected void onPullToRefresh() {super.onPullToRefresh();if (getShowIndicatorInternal()) {switch (getCurrentMode()) {case PULL_FROM_END:mIndicatorIvBottom.pullToRefresh();break;case PULL_FROM_START:mIndicatorIvTop.pullToRefresh();break;default:// NO-OPbreak;}}}先调用了PullToRefreshBase的onPullToRefresh()方法,然后调用IndicatorLayout的pullToRefresh()方法。我们下拉控件时的所有操作都在这里了,先看看PullToRefreshBase的onPullToRefresh()方法干了些什么。
<span style="white-space:pre"></span>protected void onPullToRefresh() {switch (mCurrentMode) {case PULL_FROM_END:mFooterLayout.pullToRefresh();break;case PULL_FROM_START:mHeaderLayout.pullToRefresh();break;default:// NO-OPbreak;}}
这里又调用了LoadingLayout的pullToRefresh()方法。LoadingLayout类如下:
似曾相识,和IndicatorLayout差不多嘛。1个ImageView+1个TextView,这不就是下拉刷新时的刷新头么。看看pullToRefresh()干了什么:
<span style="white-space:pre"></span>public final void pullToRefresh() {if (null != mHeaderText) {mHeaderText.setText(mPullLabel);}// Now call the callbackpullToRefreshImpl();}
设置了TextView的值,这就是为什么下拉刷新时各个阶段显示的字符串不同的原因所在了。此外这里调用了pullToRefreshImpl()方法,这是一个抽象方法,我们可以在子类中去实现它,在里面实现我们想加的逻辑。
回到PullToRefreshAdapterViewBase中,追踪mIndicatorIvTop、mIndicatorBottom。找到addIndicatorViews方法里面的代码:
<span style="white-space:pre"></span>private void addIndicatorViews() {Mode mode = getMode();FrameLayout refreshableViewWrapper = getRefreshableViewWrapper();if (mode.showHeaderLoadingLayout() && null == mIndicatorIvTop) {// If the mode can pull down, and we don't have one set alreadymIndicatorIvTop = new IndicatorLayout(getContext(), Mode.PULL_FROM_START);FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,ViewGroup.LayoutParams.WRAP_CONTENT);params.rightMargin = getResources().getDimensionPixelSize(R.dimen.indicator_right_padding);params.gravity = Gravity.TOP | Gravity.RIGHT;refreshableViewWrapper.addView(mIndicatorIvTop, params);} else if (!mode.showHeaderLoadingLayout() && null != mIndicatorIvTop) {// If we can't pull down, but have a View then remove itrefreshableViewWrapper.removeView(mIndicatorIvTop);mIndicatorIvTop = null;} …………}mIndicatorIvTop、mIndicatorBottom这两个控件分别被放到了FramLayout refreshableViewWrapper的右上角和右下角。先看看refreshableViewWrapper是个什么鬼东西,跟进到getRefreshablViewWrapper()方法中,它在PullToRefreshBase中。
<span style="white-space:pre"></span>protected FrameLayout getRefreshableViewWrapper() {return mRefreshableViewWrapper;}直接返回了PullToRefreshBase的一个成员变量,这个成员变量的初始化是这样的:
<span style="white-space:pre"></span>private void addRefreshableView(Context context, T refreshableView) {mRefreshableViewWrapper = new FrameLayout(context);mRefreshableViewWrapper.addView(refreshableView, ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.MATCH_PARENT);addViewInternal(mRefreshableViewWrapper, new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT,LayoutParams.MATCH_PARENT));}protected final void addViewInternal(View child, ViewGroup.LayoutParams params) {super.addView(child, -1, params);}
FrameLayout中放了一个充满整个FrameLayout的refreshableView(它就是那个被封装的控件,ListView、ScrollView之类的东西)。然后FrameLayout被添加到当前的LinearLayout中。
<span style="white-space:pre"></span>if (mMode.showHeaderLoadingLayout()) {addViewInternal(mHeaderLayout, 0, lp);}if (mMode.showFooterLoadingLayout()) {addViewInternal(mFooterLayout, lp);}
mHeaderLayout和mFooterLayout也被加到了当前LinearLayout中,其中mHeaderLayout在LinearLayout顶层,mFooterLayout在底层。lp的属性是这样的(LinearLayout是Vertical的时候):
<span style="white-space:pre"></span>return new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,LinearLayout.LayoutParams.WRAP_CONTENT);
至此,整个下拉刷新框架的布局比较清楚了,完整的它是这样的:
PullToRefresh是一个很庞大的框架,以我目前的水平很难1天时间就理解透彻,更不要说一篇博文写完。
第一篇的最后,从源码的角度来分析下这个框架的一些API使用。
找到PullToRefreshAdapterViewBase的handleStyleAttributes方法:
<span style="white-space:pre"></span>@Overrideprotected void handleStyledAttributes(TypedArray a) {// Set Show Indicator to the XML value, or default valuemShowIndicator = a.getBoolean(R.styleable.PullToRefresh_ptrShowIndicator, !isPullToRefreshOverScrollEnabled());}这个方法会在各个实现类的handleStyleAttributes中调用,用Xml初始化mShowIndicator的值。因此如果我们想要显示mIndicatorIvTop/mIndicatorIvBottom,只需要在Xml中写1句:ptrShowIndicator="true";否则设置ptrShowIndicator="false"。
@Overridepublic final T getRefreshableView() {return mRefreshableView;}调用getRefreshableView()方法就能得到封装的ListView/ScrollView/GridView等。然后就可以为所欲为了。
PullToRefreshBase中提供了mHeaderLayout和mFooterLayout的代理。通过getLoadingLayoutProxy()方法获取。
<span style="white-space:pre"></span>public final ILoadingLayout getLoadingLayoutProxy() {<span style="white-space:pre"></span>return getLoadingLayoutProxy(true, true);<span style="white-space:pre"></span>}
<span style="white-space:pre"></span>@Overridepublic final ILoadingLayout getLoadingLayoutProxy(boolean includeStart, boolean includeEnd) {return createLoadingLayoutProxy(includeStart, includeEnd);}当includeStart为true时代理包含mHeaderLayout,includeEnd为true时代理包含mFooterLayout。
<span style="white-space:pre"></span>if (includeStart && mMode.showHeaderLoadingLayout()) {proxy.addLayout(mHeaderLayout);}if (includeEnd && mMode.showFooterLoadingLayout()) {proxy.addLayout(mFooterLayout);}
<span style="white-space:pre"></span>/** * Set the Last Updated Text. This displayed under the main label when * Pulling * * @param label - Label to set */public void setLastUpdatedLabel(CharSequence label);/** * Set the drawable used in the loading layout. This is the same as calling * <code>setLoadingDrawable(drawable, Mode.BOTH)</code> * * @param drawable - Drawable to display */public void setLoadingDrawable(Drawable drawable);/** * Set Text to show when the Widget is being Pulled * <code>setPullLabel(releaseLabel, Mode.BOTH)</code> * * @param pullLabel - CharSequence to display */public void setPullLabel(CharSequence pullLabel);/** * Set Text to show when the Widget is refreshing * <code>setRefreshingLabel(releaseLabel, Mode.BOTH)</code> * * @param refreshingLabel - CharSequence to display */public void setRefreshingLabel(CharSequence refreshingLabel);/** * Set Text to show when the Widget is being pulled, and will refresh when * released. This is the same as calling * <code>setReleaseLabel(releaseLabel, Mode.BOTH)</code> * * @param releaseLabel - CharSequence to display */public void setReleaseLabel(CharSequence releaseLabel);/** * Set's the Sets the typeface and style in which the text should be * displayed. Please see * {@link android.widget.TextView#setTypeface(Typeface) * TextView#setTypeface(Typeface)}. */public void setTextTypeface(Typeface tf);ILoadingLayout接口中提供了以上方法设置mHeaderLayout,mFooterLayout的不同状态下显示的动画和字符串值、属性,具体见代码注释。
其实现类LoadingLayoutProxy中用1个HashSet保存着代理对象,其值由getLoadingLayoutProxy()的参数决定。
第一篇就到这里结束。
- PullToRefresh源码分析(I)
- Android PullToRefresh 源码分析
- PullToRefresh 的源码分析
- 开源Android-PullToRefresh下拉刷新源码分析
- 开源Android-PullToRefresh下拉刷新源码分析
- 开源Android-PullToRefresh下拉刷新源码分析
- PullToRefresh框架源码分析(Ⅱ)下拉刷新的过程分析
- Android开发开源项目之-PullToRefresh源码分析
- 5.3.2 开源Android-PullToRefresh下拉刷新源码分析
- PullToRefresh源码分析(Ⅲ)从源码分析PTRListView不足屏时上拉动画有2个的原因
- PullToRefresh源码阅读
- Android-PullToRefresh代码分析
- Android PullToRefresh 分析之一、初识PullToRefresh
- Android PullToRefresh 分析之一、初识PullToRefresh
- PullToRefresh(下拉刷新)源码浅析
- PullToRefresh(下拉刷新)源码浅析
- 源码简读之PullToRefresh
- Android-PullToRefresh控件源码解析
- form 表单提交不刷新页面
- 最详细的JavaScript和事件解读
- 203. Remove Linked List Elements
- .length .length() .size()的使用方法和区别
- Noj Red packet -1651 (二分)
- PullToRefresh源码分析(I)
- android studio 程序退出提示
- Android资源文件夹名称中的修饰语“sw600dp和sw720dp”等是如何对应到具体设备的?
- Maven依赖机制
- 【HDU 5755】Gambler Bo(高斯消元)
- Parcel了解
- UIView非常用方法及属性详解
- TCP、UDP、RTP(RTCP)异同与区别
- 查看linux进程内存占用