前段时间项目中用到了下拉刷新功能,之前在网上也找到过类似的demo,但这些demo的质量参差不齐,用户体验也不好,接口设计也不行。最张没办法,终于忍不了了,自己就写了一个下拉刷新的框架,这个框架是一个通用的框架,效果和设计感觉都还不错,现在分享给各位看官。
致谢:
1. 感谢lk6233160同学提出的问题,旋转View时调用setRotation方法只能是在API Level11(3.0)以上才能用,这个问题的解决办法是给ImageView设置一个Matrix,把Matrix上面作用一个旋转矩阵,但是如果不是ImageView的话,可能实现起来比较麻烦,再次谢谢lk6233160同学。
2. 谢谢如毛毛风提出的问题,向下滑动后,再向上滑动到头,只能再松手后才能再次下拉。这个问题的回复请参考评论。
技术交流群:
QQ:197990971(人员已满)
1、IPullToRefresh
public interface IPullToRefresh {
public void setPullRefreshEnabled(boolean pullRefreshEnabled);
public void setPullLoadEnabled(boolean pullLoadEnabled);
public void setScrollLoadEnabled(boolean scrollLoadEnabled);
public boolean isPullRefreshEnabled();
public boolean isPullLoadEnabled();
public boolean isScrollLoadEnabled();
public void setOnRefreshListener(OnRefreshListener refreshListener);
public void onPullDownRefreshComplete();
public void onPullUpRefreshComplete();
public T getRefreshableView();
public LoadingLayout getHeaderLoadingLayout();
public LoadingLayout getFooterLoadingLayout();
public void setLastUpdatedLabel(CharSequence label);
}
这个接口是一个泛型的,它接受View的派生类,
因为要放到我们的容器中的不就是一个View吗?
/**
* 判断刷新的View是否滑动到顶部
*
* @return true表示已经滑动到顶部,否则false
*/
protected abstract boolean isReadyForPullDown();
/**
* 判断刷新的View是否滑动到底
*
* @return true表示已经滑动到底部,否则false
*/
protected abstract boolean isReadyForPullUp();
/**
* 创建可以刷新的View
*
* @param context context
* @param attrs 属性
* @return View
*/
protected abstract T createRefreshableView(Context context, AttributeSet attrs);
4、LoadingLayout
这个方法返回当前这个刷新Layout的大小,通常返回的是布局的高度,为了以后可以扩展为水平拉动,所以方法名字没有取成getLayoutHeight()之类的,这个返回值,将会作为松手后是否可以刷新的临界值,如果下拉的偏移值大于这个值,就认为可以刷新,否则不刷新,这个方法必须由派生类来实现。
这个方法用来设置当前刷新Layout的状态,PullToRefreshBase类会调用这个方法,当进入下拉,松手等动作时,都会调用这个方法,派生类里面只需要根据这些状态实现不同的界面显示,如下拉状态时,就显示出箭头,刷新状态时,就显示loading的图标。可能的状态值有: RESET, PULL_TO_REFRESH, RELEASE_TO_REFRESH, REFRESHING, NO_MORE_DATA
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mPullListView = new PullToRefreshListView(this);
setContentView(mPullListView);
// 上拉加载不可用
mPullListView.setPullLoadEnabled(false);
// 滚动到底自动加载可用
mPullListView.setScrollLoadEnabled(true);
mCurIndex = mLoadDataCount;
mListItems = new LinkedList();
mListItems.addAll(Arrays.asList(mStrings).subList(0, mCurIndex));
mAdapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1, mListItems);
// 得到实际的ListView
mListView = mPullListView.getRefreshableView();
// 绑定数据
mListView.setAdapter(mAdapter);
// 设置下拉刷新的listener
mPullListView.setOnRefreshListener(new OnRefreshListener() {
@Override
public void onPullDownToRefresh(PullToRefreshBase refreshView) {
mIsStart = true;
new GetDataTask().execute();
}
@Override
public void onPullUpToRefresh(PullToRefreshBase refreshView) {
mIsStart = false;
new GetDataTask().execute();
}
});
setLastUpdateTime();
// 自动刷新
mPullListView.doPullRefreshing(true, 500);
}
这是初始化一个下拉刷新的布局,并且调用setContentView来设置到Activity中。
@Override
public void setScrollLoadEnabled(boolean scrollLoadEnabled) {
if (isScrollLoadEnabled() == scrollLoadEnabled) {
return;
}
super.setScrollLoadEnabled(scrollLoadEnabled);
if (scrollLoadEnabled) {
// 设置Footer
if (null == mLoadMoreFooterLayout) {
mLoadMoreFooterLayout = new FooterLoadingLayout(getContext());
mListView.addFooterView(mLoadMoreFooterLayout, null, false);
}
mLoadMoreFooterLayout.show(true);
} else {
if (null != mLoadMoreFooterLayout) {
mLoadMoreFooterLayout.show(false);
}
}
}
/**
* 显示或隐藏这个布局
*
* @param show flag
*/
public void show(boolean show) {
// If is showing, do nothing.
if (show == (View.VISIBLE == getVisibility())) {
return;
}
ViewGroup.LayoutParams params = mContainer.getLayoutParams();
if (null != params) {
if (show) {
params.height = ViewGroup.LayoutParams.WRAP_CONTENT;
} else {
params.height = 0;
}
requestLayout();
setVisibility(show ? View.VISIBLE : View.INVISIBLE);
}
}
在更改LayoutParameter后,调用requestLayout()方法。
@Override
public void onPull(float scale) {
if (null == mRotationHelper) {
mRotationHelper = new ImageViewRotationHelper(mArrowImageView);
}
float angle = scale * 180f; // SUPPRESS CHECKSTYLE
mRotationHelper.setRotation(angle);
}
/**
* The image view rotation helper
*
* @author lihong06
* @since 2014-5-2
*/
static class ImageViewRotationHelper {
/** The imageview */
private final ImageView mImageView;
/** The matrix */
private Matrix mMatrix;
/** Pivot X */
private float mRotationPivotX;
/** Pivot Y */
private float mRotationPivotY;
/**
* The constructor method.
*
* @param imageView the image view
*/
public ImageViewRotationHelper(ImageView imageView) {
mImageView = imageView;
}
/**
* Sets the degrees that the view is rotated around the pivot point. Increasing values
* result in clockwise rotation.
*
* @param rotation The degrees of rotation.
*
* @see #getRotation()
* @see #getPivotX()
* @see #getPivotY()
* @see #setRotationX(float)
* @see #setRotationY(float)
*
* @attr ref android.R.styleable#View_rotation
*/
public void setRotation(float rotation) {
if (APIUtils.hasHoneycomb()) {
mImageView.setRotation(rotation);
} else {
if (null == mMatrix) {
mMatrix = new Matrix();
// 计算旋转的中心点
Drawable imageDrawable = mImageView.getDrawable();
if (null != imageDrawable) {
mRotationPivotX = Math.round(imageDrawable.getIntrinsicWidth() / 2f);
mRotationPivotY = Math.round(imageDrawable.getIntrinsicHeight() / 2f);
}
}
mMatrix.setRotate(rotation, mRotationPivotX, mRotationPivotY);
mImageView.setImageMatrix(mMatrix);
}
}
}