PullToRefreshListView原理解析(一)

PullToRefreshListView是PullToRefreshBase的子类,而PullToRefreshBase继承LinearLayout。先分析父类PullToRefreshBase。在PullToRefreshBase的init()方法中

private void init(Context context, AttributeSet attrs) {
        switch (getPullToRefreshScrollDirection()) {
        case HORIZONTAL:
            setOrientation(LinearLayout.HORIZONTAL);
            break;
        case VERTICAL:
        default:
            setOrientation(LinearLayout.VERTICAL);
            break;
        }
        setGravity(Gravity.CENTER);
        ViewConfiguration config = ViewConfiguration.get(context);
        mTouchSlop = config.getScaledTouchSlop();
        // Styleables from XML
        TypedArray a = context.obtainStyledAttributes(attrs,
                R.styleable.PullToRefresh);
        if (a.hasValue(R.styleable.PullToRefresh_ptrMode)) {
            mMode = Mode.mapIntToValue(a.getInteger(
                    R.styleable.PullToRefresh_ptrMode, 0));
        }
        if (a.hasValue(R.styleable.PullToRefresh_ptrAnimationStyle)) {
            mLoadingAnimationStyle = AnimationStyle.mapIntToValue(a.getInteger(
                    R.styleable.PullToRefresh_ptrAnimationStyle, 0));
        }
        // Refreshable View
        // By passing the attrs, we can add ListView/GridView params via XML
        mRefreshableView = createRefreshableView(context, attrs);
        addRefreshableView(context, mRefreshableView);
        // We need to create now layouts now
        createDefaultLoadingLayout(context, mMode, a);
        /**
         * Styleables from XML
         */
        if (a.hasValue(R.styleable.PullToRefresh_ptrRefreshableViewBackground)) {
            Drawable background = a
                    .getDrawable(R.styleable.PullToRefresh_ptrRefreshableViewBackground);
            if (null != background) {
                mRefreshableView.setBackgroundDrawable(background);
            }
        } else if (a
                .hasValue(R.styleable.PullToRefresh_ptrAdapterViewBackground)) {
            Utils.warnDeprecation("ptrAdapterViewBackground",
                    "ptrRefreshableViewBackground");
            Drawable background = a
                    .getDrawable(R.styleable.PullToRefresh_ptrAdapterViewBackground);
            if (null != background) {
                mRefreshableView.setBackgroundDrawable(background);
            }
        }
        if (a.hasValue(R.styleable.PullToRefresh_ptrOverScroll)) {
            mOverScrollEnabled = a.getBoolean(
                    R.styleable.PullToRefresh_ptrOverScroll, true);
        }
        if (a.hasValue(R.styleable.PullToRefresh_ptrScrollingWhileRefreshingEnabled)) {
            mScrollingWhileRefreshingEnabled = a
                    .getBoolean(
                            R.styleable.PullToRefresh_ptrScrollingWhileRefreshingEnabled,
                            false);
        }
        // Let the derivative classes have a go at handling attributes, then
        // recycle them...
        handleStyledAttributes(a);
        a.recycle();
        // Finally update the UI for the modes
        updateUIForMode();
    }  
(1)  getPullToRefreshScrollDirection在子类 PullToRefreshListView被重写

public final Orientation getPullToRefreshScrollDirection() {
        return Orientation.VERTICAL;
 }  
所以 PullToRefreshListView就是一个 Orientation为 VERTICAL的 LinearLayout
(2) mRefreshableView  = createRefreshableView(context, attrs);  mRefreshableView是泛型T,在子类 PullToRefreshListView

public class PullToRefreshListView extends PullToRefreshAdapterViewBase<ListView> {
 ...... 
       @Override
    protected ListView createRefreshableView(Context context, AttributeSet attrs) {
        ListView lv = createListView(context, attrs);
        // Set it to this so it can be used in ListActivity/ListFragment
        lv.setId(android.R.id.list);
        return lv;
    }
 ...... 
} 
所以使用 PullToRefreshListView 时 mRefreshableView  实际上就是 ListView。当然,我们也可以改写此泛型,替换成我们自定义的ListView。比如

其中 SwipeMenuListView是可以实现侧拉删除的ListView,改写后的 PullToRefreshSwipeListView就能实现 侧拉删除功能同时,拥有 PullToRefreshListView的上拉加载下拉刷新功能。
(3)addRefreshableView(context, mRefreshableView)方法
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));
    } 
PullToRefreshListView添加了一个FrameLayout,FrameLayout里面又添加了mRefreshableView,即添加了ListView。addViewInternal()方法作用和addView ()方法类似
(4)createDefaultLoadingLayout(context, mMode, a)方法最后会通过
LoadingLayout createTopLoadingLayout(Context context, Mode mode,
      Orientation scrollDirection, TypedArray attrs) {
   if (this == FLIP_NOROTATE) {
      return new FlipLoadingLayout(context, mode, scrollDirection,
            attrs);
   }
   return createLoadingLayout(context, mode, scrollDirection, attrs);
}
生成mHeaderLayout
LoadingLayout createBootomLoadingLayout(Context context, Mode mode,
      Orientation scrollDirection, TypedArray attrs) {
   if (this == NOROTATE || this == FLIP_NOROTATE) {
      return new RefRotateLoadingLayout(context, mode,
            scrollDirection, attrs);
   }
   return createLoadingLayout(context, mode, scrollDirection, attrs);
}
生成mFooterLayout。
private AnimationStyle mLoadingAnimationStyle = AnimationStyle.getDefault();知默认this是FLIP_NOROTATE。FlipLoadingLayout 和RefRotateLoadingLayout都是FrameLayout的子类,用于实现头布局和尾布局
(5)handleStyledAttributes(a);PullToRefreshListView会对这个方法进行重写
@Override
protected void handleStyledAttributes(TypedArray a) {
   super.handleStyledAttributes(a);

   mListViewExtrasEnabled = a.getBoolean(R.styleable.PullToRefresh_ptrListViewExtrasEnabled, true);

   if (mListViewExtrasEnabled) {
      final FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT,
            FrameLayout.LayoutParams.WRAP_CONTENT, Gravity.CENTER_HORIZONTAL);

      // Create Loading Views ready for use later
      FrameLayout frame = new FrameLayout(getContext());
      mHeaderLoadingView = createLoadingLayout(getContext(), Mode.PULL_FROM_START, a);
      mHeaderLoadingView.setVisibility(View.GONE);
      frame.addView(mHeaderLoadingView, lp);
      mRefreshableView.addHeaderView(frame, null, false);

      mLvFooterLoadingFrame = new FrameLayout(getContext());
      mFooterLoadingView = createLoadingLayout(getContext(), Mode.PULL_FROM_END, a);
      mFooterLoadingView.setVisibility(View.GONE);
      mLvFooterLoadingFrame.addView(mFooterLoadingView, lp);

      /**
       * If the value for Scrolling While Refreshing hasn't been
       * explicitly set via XML, enable Scrolling While Refreshing.
       */
      if (!a.hasValue(R.styleable.PullToRefresh_ptrScrollingWhileRefreshingEnabled)) {
         setScrollingWhileRefreshingEnabled(true);
      }
   }
}
此方法主要是给PullToRefreshListView中ListView增加HeaderView和FooterLoadingView。此时可以得出PullToRefreshListView的嵌套关系


 
 
(6)updateUIForMode()方法,此方法是通过我们设置的Mode来确定是否显示headView和foodView
protected void updateUIForMode() {
   // We need to use the correct LayoutParam values, based on scroll
   // direction
   final LayoutParams lp = getLoadingLayoutLayoutParams();

   // 移除headView,防止重复addViewInternal(mHeaderLayout, 0, lp);
   if (this == mHeaderLayout.getParent()) {
      removeView(mHeaderLayout);
   }
//当我们设置Mode为PULL_FROM_START或BOTH时showHeaderLoadingLayout()///方法返回为true
   if (mMode.showHeaderLoadingLayout()) {
      addViewInternal(mHeaderLayout, 0, lp);
   }

   // Remove Footer, and then add Footer Loading View again if needed
   if (this == mFooterLayout.getParent()) {
      removeView(mFooterLayout);
   }
   if (mMode.showFooterLoadingLayout()) {
      addViewInternal(mFooterLayout, lp);
   }

   // Hide Loading Views
   refreshLoadingViewsSize();

   // If we're not using Mode.BOTH, set mCurrentMode to mMode, otherwise
   // set it to pull down
   mCurrentMode = (mMode != Mode.BOTH && mMode != Mode.REFROTATE) ? mMode
         : Mode.PULL_FROM_START;
}
至此,PullToRefreshListView的基本结构应该有个大体了解了.个人认为PullToRefreshListView嵌套的有点多了,我在项目中就有因为这个出现过问题:点击打开链接







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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值