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