初始化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的顯示;