(二) 以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的顯示;

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