(二) 以PullToRefreshListView为例,讲解PullToRefresh中的View初始化操作

     初始化Views,是PullToRefresh中很重要的一环,这个初始化过程,定义好了需要下拉刷新的View,刷新过程中显示的View,并完成了对PullToRefreshBase这个LinearLayout的Wrap工作,是后续各种事件监听的基础;

一、PullToRefreshBase的init方法;

PullToRefreshBase.java:
private void init(Context context, AttributeSet attrs) {
        //获取pull的方向来确定LinearLayout中orientation,因为后续涉及到动态添加各种View;
		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
		// 获取xml中一些属性,pull-View是支持xml配置的,具体的配置内容去看styeable文件,这里不细讲
		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
		// 1、通过createRefreshableView实例化要刷新的View
		mRefreshableView = createRefreshableView(context, attrs);
		addRefreshableView(context, mRefreshableView);

		// We need to create now layouts now
		//2、通过createLoadingLayout实例化下拉的时需要显示的Layout:mHeaderLayout,实例化上拉时需要显示的View:mFooterLayout;
		mHeaderLayout = createLoadingLayout(context, Mode.PULL_FROM_START, a);
		mFooterLayout = createLoadingLayout(context, Mode.PULL_FROM_END, 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...
		//3、拼接带有LoadingLayout的refreshView
		handleStyledAttributes(a);
		a.recycle();

		// Finally update the UI for the modes
		//4、拼接LinearLayout中的LoadingLayout
		updateUIForMode();
	}

      init方法很简洁,做了一些初始化工作,代码中加了很详细的注释,有问题可以沿着注释看;这里主要讲解4个方法,在代码中分别用需要1-4标出来了;

1、createRefreshableView方法:

PullToRefreshBase.java:
protected abstract T createRefreshableView(Context context, AttributeSet attrs);

        这个方法是个抽象方法,这个refreshView的实现在子类中;同时该方法返回T,即任意类型,这样很好地支持了各种refreshView,包括ListView,GrdiView,ScrollView等,只需要在子类实现这个方法即可实现相应的下拉刷新列表;

        既然基类为抽象方法,我们看子类的实现;以PullToRefreshListView为例,简单说明下,其他的实现都类似;

PullToRefreshListView.java:
@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.java:
protected ListView createListView(Context context, AttributeSet attrs) {
		final ListView lv;
		if (VERSION.SDK_INT >= VERSION_CODES.GINGERBREAD) {
  			lv = new InternalListViewSDK9(context, attrs);
		} else {
			lv = new InternalListView(context, attrs);
		}
		return lv;
	}

         PullToRefreshListView中通过createListView实现了ListView,并且将ListView返回;其中InternalListView,主要实现了ListView为空的时的一些基础设置及setAdapter操作;而InternalListViewSDK9则是在InternalListView的基础上增加了下拉回弹的处理,2.3以前的版本不支持这个操作,在这里自定义实现了;说白了,很简单,返回了实例化好的ListView;

此时的整个框架的效果图如下:


2、createLoadingLayout方法;
     这个方法主要用来创建LoadingLayout的对象,这个对象是显示在refreshView的Header或者Footer的layout;

PullToRefreshListView.java:
protected LoadingLayout createLoadingLayout(Context context, Mode mode, TypedArray attrs) {
		LoadingLayout layout = mLoadingAnimationStyle.createLoadingLayout(context, mode,
				getPullToRefreshScrollDirection(), attrs);
		layout.setVisibility(View.INVISIBLE);
		return layout;
	}

PullToRefreshListView.java:
LoadingLayout createLoadingLayout(Context context, Mode mode, Orientation scrollDirection, TypedArray attrs) {
            switch (this) {
                case ROTATE:
                default:
                    return new RotateLoadingLayout(context, mode, scrollDirection, attrs);
                case FLIP:
                    return new FlipLoadingLayout(context, mode, scrollDirection, attrs);
            }
        }

private AnimationStyle mLoadingAnimationStyle = AnimationStyle.getDefault();

static AnimationStyle getDefault() {
			return ROTATE;
		}

        上面是这个方法的调用链,写的这么绕,当然是为了可扩展性和易用了,不过这里我们先考虑原理;说白了,这个方法最后就是返回了LoadingLayout的实例,这里,你只要知道LoadingLayout实现了ILoadingLayout,是上拉或者下拉的时候refreshView显示的footerView或者headerView就行了,后面我会讲到这个类;通过这个方法,实例化了refreshView的要显示的footerView和headerView;

3、handleStypeAtrributes(a)方法:

      这个方法是对xml配置的内容及前面实例化好的内容的组合的过程;基类实现是空;我还要看子类,这里还以PullToRefreshListView为例来分析;

PullToRefreshBase.java:
protected void handleStyledAttributes(TypedArray a) {
	}
PullToRefreshListView.java:
@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);
			}
		}
	}
      注意:这里重新实例化两个LoadingLayout:mHeaderLoadingView和mFooterLoadingView,这两个View都是在PullToRefreshListView中定义;实例化完成之后,将它们分别addHeader和addFooter;此时的列表有有有尾了;但是在这个时候,上拉、下拉都还是没有视图的,因为它们都被gone掉了;

      此时的整个框架的效果图如下:


3、updateUIForMode方法:

PullToRefreshBase.java:
protected void updateUIForMode() {
		// We need to use the correct LayoutParam values, based on scroll
		// direction
		final LinearLayout.LayoutParams lp = getLoadingLayoutLayoutParams();

		// Remove Header, and then add Header Loading View again if needed
		if (this == mHeaderLayout.getParent()) {
			removeView(mHeaderLayout);
		}
		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.PULL_FROM_START;
	}

     在这里,将PullToRefreshBase类里面通过createLoadingLayout得到的Header和Footer再分别添加到LieanerLayout中,就完成了整个refreshView的Wrapper(PullToRefreshBase)的view初始化过程;注意,这里的两个LoadingLayout都是VISIBLE状态的;

此时的整个框架的效果图如下:


     以PullToRefreshListView为例,这里一共实例化5个View,ListView及4个LoadingLayout,这四个Loadinglayout分别代表了在下拉过程中,下拉松手后正在刷新的时候,上拉过程中,上拉松手正在刷新的时候显示的View,其实他们可以长得不一样,只是在这里把他们都写成一样了;显然,如果LoadingLayout的Layout不复杂,写在一起是没问题的,如果LoadingLayout的Layout很复杂,涉及到的View很多,建议还是分开写,针对每一个过程的View约精简越好,避免View数爆炸;

      好了,关于初始化的内容就讲这些,代码都很简单,有这个思路的引导看起来还是很容易的。下一篇再讲解滑动过程中事件的监听、处理,及LoadingLayout的显示;

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章