diff --git a/README-cn.md b/README-cn.md index 1504223..23d65a6 100755 --- a/README-cn.md +++ b/README-cn.md @@ -1,8 +1,26 @@ -###### [关注我的GitHub吧,江湖救急,需要你的支持和帮助](http://www.liaohuqiu.net/cn/posts/follow-me-on-github/) +# 修改 -Github: https://github.com/liaohuqiu +本分支对原有的Ultra Pull To Refresh进行了修改,使得其支持了上拉加载更多。和下拉刷新一样,上拉加载同样支持所有view。全部的逻辑都是仿造原有的下拉刷新而来,并且配置信息(阻抗,持续时间等)为两者共享。目前,实例项目demo中只有`主页面`和`自动刷新`页面演示了如何使用上拉加载更多,不过原理上与下拉刷新一致,应该比较好理解。 -微博: http://weibo.com/liaohuqiu +如果想要使用本分支,请直接下载源码并导入IDE中。底部的gradle和maven目前并不支持。 + +注:本分支没有修改任何原有API。如果你之前已经在使用Ultra Pull To Refresh,那么你可以无缝的转换到本分支上来。 + +感谢`廖祜秋`开源了这个非常棒的下拉刷新库。他所写的代码可拓展性极高,使得我的修改工作也轻松不少。 + +本分支刚刚开发完成,可能会存在bug。如果有问题,欢迎指出! + +Github: https://github.com/captainbupt + +博客: http://blog.csdn.net/hwz2311245 + +# 添加的方法和类 + +- `setFooterView`:对应于`setHeaderView()`。在set完footer后,你需要调用 `addPtrUIHandler()`,这和设置header的机制是一样的。 +- `PtrHandler2`:原有`PtrHandler`类的一个补充。当需要使用上拉加载更多的时候,你应该调用`setPtrHandler(new PtrHandler2())`,而不是`setPtrHandler(new PtrHandler())`。 +- `PtrDefaultHandler2`:实现了默认的 `checkCanDoLoadMore()` 逻辑,可以适用于大部分的View。机制和`PtrDefaultHandler`一致。 +- `PtrClassicDefaultFooter`:默认的footer,将默认header反转了过来。 +- `setMode(Mode)`: Mode是本分支的一个新特性。通过调用`setMode`, 你可以任意的开启/关闭header或者footer。参数类型是一个枚举变量,可以通过以下方式调用:`setMode(Mode.BOTH)`. --- @@ -16,10 +34,6 @@ Github: https://github.com/liaohuqiu [APK下载](https://raw.githubusercontent.com/liaohuqiu/android-Ultra-Pull-To-Refresh/master/ptr-demo.apk) -#### 下拉刷新 + 加载更多? - -本类库是单纯的下拉刷新。如果你需要用到`加载更多`,看这个项目: https://github.com/liaohuqiu/android-cube-app - #### 使用eclipse的同学请注意, Intellij IDEA / Android Studio 请忽略 **demo可以直接在eclipse中运行, 编译demo项目的同学看这里: http://www.liaohuqiu.net/cn/posts/compile-ultra-ptr-in-eclipse/** diff --git a/README.md b/README.md index 995d152..a252ca5 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,31 @@ -###### [Please follow me on GitHub, I need your support](http://www.liaohuqiu.net/posts/follow-me-on-github/) +# Modification -Github: https://github.com/liaohuqiu +I had made a modification of the Ultra-Pull-to-Refresh library and let it support load-more function. It supports every view as like as pull-to-refresh. All the logic is simulated to the pull-to-refresh and the configurations(resistance, duration etc.) are shared for both load-more and pull-to-refresh. Currently, only the `Home page(PtrDemoHomeFragment)` and `AutoRefresh(WithGridView)` page in demo are modified to demonstrate how to use load-more. But the mechanism should be the same as pull-to-refresh. -twitter: https://twitter.com/liaohuqiu +If your want to this fork, please download this project and import to your IDE manually. The gradle and maven below is not supported by this fort yet. + +PS: All the API are not modified. If you have used the origin Ultra-Pull-To-Refresh library, there will be no need to change your code if you move to this fork. + +Thank you for `Huqiu Liao`, who created such a great library. His code was so scalable and made my modification easier. + +This fork is just developed and there would be bugs. Please feel free to report me if you found some bugs. + +Github: https://github.com/captainbupt + +# New functions and classes + +- `setFooterView`: Corresponding to `setHeaderView()`. You may also need call `addPtrUIHandler()` after setting the footer view, which is the same mechanism as setting header. +- `PtrHandler2`: A complementary of `PtrHandler`. When using load more function, you should call `setPtrHandler(new PtrHandler2())`, rather than `setPtrHandler(new PtrHandler())`. +- `PtrDefaultHandler2`: Implemented a default `checkCanDoLoadMore()` logic. The same mechanism as `PtrDefaultHandler`. +- `PtrClassicDefaultFooter`: Serve as a default footer which is the reverse of the default header. +- `setMode(Mode)`: The mode is a new feature of this library. By using `setMode`, you can enable or disable either header or footer. The argument is an enum, you should it like `setMode(Mode.BOTH)`. --- [![Build Status](https://travis-ci.org/liaohuqiu/android-Ultra-Pull-To-Refresh.svg?branch=master)](https://travis-ci.org/liaohuqiu/android-Ultra-Pull-To-Refresh) [![Android Arsenal](https://img.shields.io/badge/Android%20Arsenal-Ultra%20Pull%20To%20Refresh-brightgreen.svg?style=flat)](https://android-arsenal.com/details/1/1180) -#### [中文版文档](https://github.com/liaohuqiu/android-Ultra-Pull-To-Refresh/blob/master/README-cn.md) - -Wanna auto-load-more? This will be what you want: https://github.com/liaohuqiu/android-cube-app +#### [中文版文档](https://github.com/captainbupt/android-Ultra-Pull-To-Refresh-With-Load-More/blob/master/README-cn.md) # Ultra Pull To Refresh diff --git a/ptr-demo/res/layout/fragmengt_ptr_home.xml b/ptr-demo/res/layout/fragmengt_ptr_home.xml index 9ab8382..81cf3c4 100755 --- a/ptr-demo/res/layout/fragmengt_ptr_home.xml +++ b/ptr-demo/res/layout/fragmengt_ptr_home.xml @@ -13,7 +13,8 @@ cube_ptr:ptr_keep_header_when_refresh="true" cube_ptr:ptr_pull_to_fresh="false" cube_ptr:ptr_ratio_of_header_height_to_refresh="1.2" - cube_ptr:ptr_resistance="1.7"> + cube_ptr:ptr_resistance="1.7" + cube_ptr:ptr_mode="both"> parent, View view, int position, l listViewDataAdapter.setViewHolderClass(this, ViewHolder.class); ptrFrameLayout.setPtrHandler(new PtrDefaultHandler() { + @Override public void onRefreshBegin(PtrFrameLayout frame) { ptrFrameLayout.postDelayed(new Runnable() { diff --git a/ptr-demo/src/in/srain/cube/views/ptr/demo/ui/classic/AutoRefresh.java b/ptr-demo/src/in/srain/cube/views/ptr/demo/ui/classic/AutoRefresh.java index d0e98d4..81e2f96 100755 --- a/ptr-demo/src/in/srain/cube/views/ptr/demo/ui/classic/AutoRefresh.java +++ b/ptr-demo/src/in/srain/cube/views/ptr/demo/ui/classic/AutoRefresh.java @@ -12,7 +12,7 @@ protected void setupViews(final PtrClassicFrameLayout ptrFrame) { ptrFrame.postDelayed(new Runnable() { @Override public void run() { - ptrFrame.autoRefresh(true); + ptrFrame.autoLoadMore(true); } }, 150); } diff --git a/ptr-demo/src/in/srain/cube/views/ptr/demo/ui/classic/EvenOnlyATextView.java b/ptr-demo/src/in/srain/cube/views/ptr/demo/ui/classic/EvenOnlyATextView.java index 8cf028e..159e527 100755 --- a/ptr-demo/src/in/srain/cube/views/ptr/demo/ui/classic/EvenOnlyATextView.java +++ b/ptr-demo/src/in/srain/cube/views/ptr/demo/ui/classic/EvenOnlyATextView.java @@ -7,6 +7,7 @@ import in.srain.cube.mints.base.TitleBaseFragment; import in.srain.cube.views.ptr.PtrClassicFrameLayout; import in.srain.cube.views.ptr.PtrDefaultHandler; +import in.srain.cube.views.ptr.PtrDefaultHandler2; import in.srain.cube.views.ptr.PtrFrameLayout; import in.srain.cube.views.ptr.demo.R; @@ -20,7 +21,17 @@ public View createView(LayoutInflater inflater, ViewGroup container, Bundle save final PtrClassicFrameLayout ptrFrame = (PtrClassicFrameLayout) contentView.findViewById(R.id.fragment_rotate_header_with_text_view_frame); ptrFrame.setLastUpdateTimeRelateObject(this); - ptrFrame.setPtrHandler(new PtrDefaultHandler() { + ptrFrame.setPtrHandler(new PtrDefaultHandler2() { + @Override + public void onLoadMoreBegin(PtrFrameLayout frame) { + frame.postDelayed(new Runnable() { + @Override + public void run() { + ptrFrame.refreshComplete(); + } + }, 1500); + } + @Override public void onRefreshBegin(PtrFrameLayout frame) { frame.postDelayed(new Runnable() { diff --git a/ptr-demo/src/in/srain/cube/views/ptr/demo/ui/classic/WithGridView.java b/ptr-demo/src/in/srain/cube/views/ptr/demo/ui/classic/WithGridView.java index 4506325..ea89365 100755 --- a/ptr-demo/src/in/srain/cube/views/ptr/demo/ui/classic/WithGridView.java +++ b/ptr-demo/src/in/srain/cube/views/ptr/demo/ui/classic/WithGridView.java @@ -9,6 +9,7 @@ import android.widget.GridView; import android.widget.ImageView.ScaleType; import android.widget.LinearLayout; + import in.srain.cube.image.CubeImageView; import in.srain.cube.image.ImageLoader; import in.srain.cube.image.ImageLoaderFactory; @@ -21,8 +22,10 @@ import in.srain.cube.views.list.ViewHolderCreator; import in.srain.cube.views.ptr.PtrClassicFrameLayout; import in.srain.cube.views.ptr.PtrDefaultHandler; +import in.srain.cube.views.ptr.PtrDefaultHandler2; import in.srain.cube.views.ptr.PtrFrameLayout; import in.srain.cube.views.ptr.PtrHandler; +import in.srain.cube.views.ptr.PtrHandler2; import in.srain.cube.views.ptr.demo.R; import in.srain.cube.views.ptr.demo.data.DemoRequestData; import in.srain.cube.views.ptr.demo.ui.MaterialStyleFragment; @@ -65,16 +68,18 @@ public ViewHolderBase createViewHolder(int position) { mPtrFrame = (PtrClassicFrameLayout) contentView.findViewById(R.id.rotate_header_grid_view_frame); mPtrFrame.setLastUpdateTimeRelateObject(this); - mPtrFrame.setPtrHandler(new PtrHandler() { + mPtrFrame.setPtrHandler(new PtrDefaultHandler2() { + @Override - public void onRefreshBegin(PtrFrameLayout frame) { + public void onLoadMoreBegin(PtrFrameLayout frame) { updateData(); } @Override - public boolean checkCanDoRefresh(PtrFrameLayout frame, View content, View header) { - return PtrDefaultHandler.checkContentCanBePulledDown(frame, content, header); + public void onRefreshBegin(PtrFrameLayout frame) { + updateData(); } + }); // the following are default settings mPtrFrame.setResistance(1.7f); @@ -109,7 +114,7 @@ public void onRequestFinish(final JsonData data) { @Override public void run() { mAdapter.getDataList().clear(); - mAdapter.getDataList().addAll(data.optJson("data").optJson("list").toArrayList()); + mAdapter.getDataList().addAll(data.optJson("data").optJson("list").toArrayList().subList(0,10)); mPtrFrame.refreshComplete(); mAdapter.notifyDataSetChanged(); } diff --git a/ptr-demo/src/in/srain/cube/views/ptr/demo/ui/classic/WithWebView.java b/ptr-demo/src/in/srain/cube/views/ptr/demo/ui/classic/WithWebView.java index 8a4dc23..1e63027 100755 --- a/ptr-demo/src/in/srain/cube/views/ptr/demo/ui/classic/WithWebView.java +++ b/ptr-demo/src/in/srain/cube/views/ptr/demo/ui/classic/WithWebView.java @@ -6,6 +6,7 @@ import android.view.ViewGroup; import android.webkit.WebView; import android.webkit.WebViewClient; + import in.srain.cube.mints.base.TitleBaseFragment; import in.srain.cube.views.ptr.PtrClassicFrameLayout; import in.srain.cube.views.ptr.PtrDefaultHandler; diff --git a/ptr-demo/src/in/srain/cube/views/ptr/demo/ui/storehouse/StoreHouseUsingString.java b/ptr-demo/src/in/srain/cube/views/ptr/demo/ui/storehouse/StoreHouseUsingString.java index e7c04b5..9de019c 100755 --- a/ptr-demo/src/in/srain/cube/views/ptr/demo/ui/storehouse/StoreHouseUsingString.java +++ b/ptr-demo/src/in/srain/cube/views/ptr/demo/ui/storehouse/StoreHouseUsingString.java @@ -4,6 +4,7 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; + import in.srain.cube.image.CubeImageView; import in.srain.cube.image.ImageLoader; import in.srain.cube.image.ImageLoaderFactory; diff --git a/ptr-demo/src/in/srain/cube/views/ptr/demo/ui/storehouse/StoreHouseUsingStringArray.java b/ptr-demo/src/in/srain/cube/views/ptr/demo/ui/storehouse/StoreHouseUsingStringArray.java index 06dbd53..dd1297e 100755 --- a/ptr-demo/src/in/srain/cube/views/ptr/demo/ui/storehouse/StoreHouseUsingStringArray.java +++ b/ptr-demo/src/in/srain/cube/views/ptr/demo/ui/storehouse/StoreHouseUsingStringArray.java @@ -4,6 +4,7 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; + import in.srain.cube.image.CubeImageView; import in.srain.cube.image.ImageLoader; import in.srain.cube.image.ImageLoaderFactory; diff --git a/ptr-demo/src/in/srain/cube/views/ptr/demo/ui/viewpager/ViewPagerActivity.java b/ptr-demo/src/in/srain/cube/views/ptr/demo/ui/viewpager/ViewPagerActivity.java index 739c9e8..5531e4f 100644 --- a/ptr-demo/src/in/srain/cube/views/ptr/demo/ui/viewpager/ViewPagerActivity.java +++ b/ptr-demo/src/in/srain/cube/views/ptr/demo/ui/viewpager/ViewPagerActivity.java @@ -10,6 +10,7 @@ import android.view.ViewGroup; import android.widget.AbsListView; import android.widget.TextView; + import in.srain.cube.mints.base.TitleBaseActivity; import in.srain.cube.request.JsonData; import in.srain.cube.request.RequestFinishHandler; @@ -149,6 +150,13 @@ public boolean checkCanDoRefresh() { } return mCurrentFragment.checkCanDoRefresh(); } + + public boolean checkCanDoLoadMore() { + if (mCurrentFragment == null) { + return true; + } + return mCurrentFragment.checkCanDoLoadMore(); + } } private class HomeCatItemViewHolder extends TabPageIndicator.ViewHolderBase { diff --git a/ptr-demo/src/in/srain/cube/views/ptr/demo/ui/viewpager/ViewPagerFragment.java b/ptr-demo/src/in/srain/cube/views/ptr/demo/ui/viewpager/ViewPagerFragment.java index 604ae5c..45c903c 100644 --- a/ptr-demo/src/in/srain/cube/views/ptr/demo/ui/viewpager/ViewPagerFragment.java +++ b/ptr-demo/src/in/srain/cube/views/ptr/demo/ui/viewpager/ViewPagerFragment.java @@ -8,6 +8,7 @@ import android.widget.AdapterView; import android.widget.ListView; import android.widget.TextView; + import in.srain.cube.app.CubeFragment; import in.srain.cube.image.ImageLoader; import in.srain.cube.image.ImageLoaderFactory; @@ -51,8 +52,8 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa @Override public void onItemClick(AdapterView parent, View view, int position, long id) { if (position >= 0) { - JsonData js = mAdapter.getItem(position); - final String url = js!=null?js.optString("pic"):null; + JsonData js = mAdapter.getItem(position); + final String url = js != null ? js.optString("pic") : null; if (!TextUtils.isEmpty(url)) { getContext().pushFragmentToBackStack(MaterialStyleFragment.class, url); } @@ -88,4 +89,12 @@ public boolean checkCanDoRefresh() { return mListView.getFirstVisiblePosition() == 0 && mListView.getChildAt(0).getTop() == 0; } + public boolean checkCanDoLoadMore() { + if (mAdapter.getCount() == 0 || mListView == null) { + return true; + } + CLog.d("test", "checkCanDoRefresh: %s %s", mListView.getFirstVisiblePosition(), mListView.getChildAt(0).getTop()); + return mListView.getLastVisiblePosition() < mAdapter.getCount() - 1 || mListView.getChildAt(mAdapter.getCount() - 1) + .getBottom() > mListView.getPaddingBottom(); + } } diff --git a/ptr-lib/res/values-zh/cube_ptr_string.xml b/ptr-lib/res/values-zh/cube_ptr_string.xml index 1e0c492..6b4c711 100644 --- a/ptr-lib/res/values-zh/cube_ptr_string.xml +++ b/ptr-lib/res/values-zh/cube_ptr_string.xml @@ -1,6 +1,9 @@ + 上拉 + 上拉加载 + 释放加载 下拉 下拉刷新 释放刷新 diff --git a/ptr-lib/res/values/cube_ptr_attrs.xml b/ptr-lib/res/values/cube_ptr_attrs.xml index 06b967d..7891d78 100755 --- a/ptr-lib/res/values/cube_ptr_attrs.xml +++ b/ptr-lib/res/values/cube_ptr_attrs.xml @@ -3,26 +3,33 @@ - - + + + - + - + - - + + - + - + + + + + + + - + \ No newline at end of file diff --git a/ptr-lib/res/values/cube_ptr_string.xml b/ptr-lib/res/values/cube_ptr_string.xml index 99b777b..cddcac7 100644 --- a/ptr-lib/res/values/cube_ptr_string.xml +++ b/ptr-lib/res/values/cube_ptr_string.xml @@ -1,6 +1,9 @@ + Pull Up + Pull Up to Load + Release To Load Pull Down Pull Down to Refresh Release To Refresh diff --git a/ptr-lib/src/in/srain/cube/views/ptr/PtrClassicDefaultFooter.java b/ptr-lib/src/in/srain/cube/views/ptr/PtrClassicDefaultFooter.java new file mode 100644 index 0000000..5cad1e7 --- /dev/null +++ b/ptr-lib/src/in/srain/cube/views/ptr/PtrClassicDefaultFooter.java @@ -0,0 +1,51 @@ +package in.srain.cube.views.ptr; + +import android.content.Context; +import android.content.SharedPreferences; +import android.content.res.TypedArray; +import android.text.TextUtils; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.view.animation.LinearInterpolator; +import android.view.animation.RotateAnimation; +import android.widget.FrameLayout; +import android.widget.TextView; + +import java.text.SimpleDateFormat; +import java.util.Date; + +import in.srain.cube.views.ptr.indicator.PtrIndicator; + +public class PtrClassicDefaultFooter extends PtrClassicDefaultHeader { + + public PtrClassicDefaultFooter(Context context) { + super(context); + } + + public PtrClassicDefaultFooter(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public PtrClassicDefaultFooter(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + @Override + protected void buildAnimation() { + super.buildAnimation(); + RotateAnimation tmp = mFlipAnimation; + mFlipAnimation = mReverseFlipAnimation; + mReverseFlipAnimation = tmp; + } + + @Override + public void onUIRefreshPrepare(PtrFrameLayout frame) { + super.onUIRefreshPrepare(frame); + if (frame.isPullToRefresh()) { + mTitleTextView.setText(getResources().getString(R.string.cube_ptr_pull_up_to_load)); + } else { + mTitleTextView.setText(getResources().getString(R.string.cube_ptr_pull_up)); + } + } +} diff --git a/ptr-lib/src/in/srain/cube/views/ptr/PtrClassicDefaultHeader.java b/ptr-lib/src/in/srain/cube/views/ptr/PtrClassicDefaultHeader.java index 493dbc1..4659872 100644 --- a/ptr-lib/src/in/srain/cube/views/ptr/PtrClassicDefaultHeader.java +++ b/ptr-lib/src/in/srain/cube/views/ptr/PtrClassicDefaultHeader.java @@ -21,9 +21,9 @@ public class PtrClassicDefaultHeader extends FrameLayout implements PtrUIHandler private final static String KEY_SharedPreferences = "cube_ptr_classic_last_update"; private static SimpleDateFormat sDataFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); private int mRotateAniTime = 150; - private RotateAnimation mFlipAnimation; - private RotateAnimation mReverseFlipAnimation; - private TextView mTitleTextView; + protected RotateAnimation mFlipAnimation; + protected RotateAnimation mReverseFlipAnimation; + protected TextView mTitleTextView; private View mRotateView; private View mProgressBar; private long mLastUpdateTime = -1; @@ -102,7 +102,7 @@ public void setLastUpdateTimeRelateObject(Object object) { setLastUpdateTimeKey(object.getClass().getName()); } - private void buildAnimation() { + protected void buildAnimation() { mFlipAnimation = new RotateAnimation(0, -180, RotateAnimation.RELATIVE_TO_SELF, 0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f); mFlipAnimation.setInterpolator(new LinearInterpolator()); mFlipAnimation.setDuration(mRotateAniTime); diff --git a/ptr-lib/src/in/srain/cube/views/ptr/PtrClassicFrameLayout.java b/ptr-lib/src/in/srain/cube/views/ptr/PtrClassicFrameLayout.java index ba1b2cf..d61e308 100644 --- a/ptr-lib/src/in/srain/cube/views/ptr/PtrClassicFrameLayout.java +++ b/ptr-lib/src/in/srain/cube/views/ptr/PtrClassicFrameLayout.java @@ -6,6 +6,7 @@ public class PtrClassicFrameLayout extends PtrFrameLayout { private PtrClassicDefaultHeader mPtrClassicHeader; + private PtrClassicDefaultFooter mPtrClassicFooter; public PtrClassicFrameLayout(Context context) { super(context); @@ -26,6 +27,9 @@ private void initViews() { mPtrClassicHeader = new PtrClassicDefaultHeader(getContext()); setHeaderView(mPtrClassicHeader); addPtrUIHandler(mPtrClassicHeader); + mPtrClassicFooter = new PtrClassicDefaultFooter(getContext()); + setFooterView(mPtrClassicFooter); + addPtrUIHandler(mPtrClassicFooter); } public PtrClassicDefaultHeader getHeader() { @@ -38,19 +42,41 @@ public PtrClassicDefaultHeader getHeader() { * @param key */ public void setLastUpdateTimeKey(String key) { + setLastUpdateTimeHeaderKey(key); + setLastUpdateTimeFooterKey(key); + } + + public void setLastUpdateTimeHeaderKey(String key) { if (mPtrClassicHeader != null) { mPtrClassicHeader.setLastUpdateTimeKey(key); } } + public void setLastUpdateTimeFooterKey(String key) { + if (mPtrClassicFooter != null) { + mPtrClassicFooter.setLastUpdateTimeKey(key); + } + } + /** * Using an object to specify the last update time. * * @param object */ public void setLastUpdateTimeRelateObject(Object object) { + setLastUpdateTimeHeaderRelateObject(object); + setLastUpdateTimeFooterRelateObject(object); + } + + public void setLastUpdateTimeHeaderRelateObject(Object object) { if (mPtrClassicHeader != null) { mPtrClassicHeader.setLastUpdateTimeRelateObject(object); } } + + public void setLastUpdateTimeFooterRelateObject(Object object) { + if (mPtrClassicFooter != null) { + mPtrClassicFooter.setLastUpdateTimeRelateObject(object); + } + } } diff --git a/ptr-lib/src/in/srain/cube/views/ptr/PtrDefaultHandler.java b/ptr-lib/src/in/srain/cube/views/ptr/PtrDefaultHandler.java index aa67b0c..6a434db 100644 --- a/ptr-lib/src/in/srain/cube/views/ptr/PtrDefaultHandler.java +++ b/ptr-lib/src/in/srain/cube/views/ptr/PtrDefaultHandler.java @@ -2,6 +2,7 @@ import android.view.View; import android.widget.AbsListView; +import android.widget.ScrollView; public abstract class PtrDefaultHandler implements PtrHandler { @@ -36,4 +37,5 @@ public static boolean checkContentCanBePulledDown(PtrFrameLayout frame, View con public boolean checkCanDoRefresh(PtrFrameLayout frame, View content, View header) { return checkContentCanBePulledDown(frame, content, header); } + } \ No newline at end of file diff --git a/ptr-lib/src/in/srain/cube/views/ptr/PtrDefaultHandler2.java b/ptr-lib/src/in/srain/cube/views/ptr/PtrDefaultHandler2.java new file mode 100644 index 0000000..3aba01f --- /dev/null +++ b/ptr-lib/src/in/srain/cube/views/ptr/PtrDefaultHandler2.java @@ -0,0 +1,47 @@ +package in.srain.cube.views.ptr; + +import android.view.View; +import android.widget.AbsListView; +import android.widget.ScrollView; + +public abstract class PtrDefaultHandler2 extends PtrDefaultHandler implements PtrHandler2 { + + public static boolean canChildScrollDown(View view) { + if (android.os.Build.VERSION.SDK_INT < 14) { + if (view instanceof AbsListView) { + final AbsListView absListView = (AbsListView) view; + return absListView.getChildCount() > 0 + && (absListView.getLastVisiblePosition() < absListView.getChildCount() - 1 + || absListView.getChildAt(absListView.getChildCount() - 1).getBottom() > absListView.getPaddingBottom()); + } else if (view instanceof ScrollView) { + ScrollView scrollView = (ScrollView) view; + if (scrollView.getChildCount() == 0) { + return false; + } else { + return scrollView.getScrollY() < scrollView.getChildAt(0).getHeight() - scrollView.getHeight(); + } + } else { + return false; + } + } else { + return view.canScrollVertically(1); + } + } + + /** + * Default implement for check can perform pull to refresh + * + * @param frame + * @param content + * @param header + * @return + */ + public static boolean checkContentCanBePulledUp(PtrFrameLayout frame, View content, View header) { + return !canChildScrollDown(content); + } + + @Override + public boolean checkCanDoLoadMore(PtrFrameLayout frame, View content, View footer) { + return checkContentCanBePulledUp(frame, content, footer); + } +} \ No newline at end of file diff --git a/ptr-lib/src/in/srain/cube/views/ptr/PtrFrameLayout.java b/ptr-lib/src/in/srain/cube/views/ptr/PtrFrameLayout.java index 93e761c..2012d17 100755 --- a/ptr-lib/src/in/srain/cube/views/ptr/PtrFrameLayout.java +++ b/ptr-lib/src/in/srain/cube/views/ptr/PtrFrameLayout.java @@ -6,6 +6,9 @@ import android.view.*; import android.widget.Scroller; import android.widget.TextView; + +import java.util.ArrayList; + import in.srain.cube.views.ptr.indicator.PtrIndicator; import in.srain.cube.views.ptr.util.PtrCLog; @@ -16,14 +19,18 @@ */ public class PtrFrameLayout extends ViewGroup { + public enum Mode { + NONE, REFRESH, LOAD_MORE, BOTH + } + + private byte mStatus = PTR_STATUS_INIT; // status enum public final static byte PTR_STATUS_INIT = 1; - private byte mStatus = PTR_STATUS_INIT; public final static byte PTR_STATUS_PREPARE = 2; public final static byte PTR_STATUS_LOADING = 3; public final static byte PTR_STATUS_COMPLETE = 4; private static final boolean DEBUG_LAYOUT = true; - public static boolean DEBUG = false; + public static boolean DEBUG = true; private static int ID = 1; protected final String LOG_TAG = "ptr-frame-" + ++ID; // auto refresh status @@ -36,18 +43,23 @@ public class PtrFrameLayout extends ViewGroup { // optional config for define header and content in xml file private int mHeaderId = 0; private int mContainerId = 0; + private int mFooterId = 0; // config + private Mode mMode = Mode.BOTH; private int mDurationToClose = 200; private int mDurationToCloseHeader = 1000; private boolean mKeepHeaderWhenRefresh = true; private boolean mPullToRefresh = false; private View mHeaderView; + private View mFooterView; private PtrUIHandlerHolder mPtrUIHandlerHolder = PtrUIHandlerHolder.create(); private PtrHandler mPtrHandler; // working parameters private ScrollChecker mScrollChecker; private int mPagingTouchSlop; private int mHeaderHeight; + private int mFooterHeight; + private boolean mDisableWhenHorizontalMove = false; private int mFlag = 0x00; @@ -84,12 +96,11 @@ public PtrFrameLayout(Context context, AttributeSet attrs, int defStyle) { TypedArray arr = context.obtainStyledAttributes(attrs, R.styleable.PtrFrameLayout, 0, 0); if (arr != null) { - mHeaderId = arr.getResourceId(R.styleable.PtrFrameLayout_ptr_header, mHeaderId); mContainerId = arr.getResourceId(R.styleable.PtrFrameLayout_ptr_content, mContainerId); + mFooterId = arr.getResourceId(R.styleable.PtrFrameLayout_ptr_footer, mFooterId); - mPtrIndicator.setResistance( - arr.getFloat(R.styleable.PtrFrameLayout_ptr_resistance, mPtrIndicator.getResistance())); + mPtrIndicator.setResistance(arr.getFloat(R.styleable.PtrFrameLayout_ptr_resistance, mPtrIndicator.getResistance())); mDurationToClose = arr.getInt(R.styleable.PtrFrameLayout_ptr_duration_to_close, mDurationToClose); mDurationToCloseHeader = arr.getInt(R.styleable.PtrFrameLayout_ptr_duration_to_close_header, mDurationToCloseHeader); @@ -101,6 +112,9 @@ public PtrFrameLayout(Context context, AttributeSet attrs, int defStyle) { mKeepHeaderWhenRefresh = arr.getBoolean(R.styleable.PtrFrameLayout_ptr_keep_header_when_refresh, mKeepHeaderWhenRefresh); mPullToRefresh = arr.getBoolean(R.styleable.PtrFrameLayout_ptr_pull_to_fresh, mPullToRefresh); + + mMode = getModeFromIndex(arr.getInt(R.styleable.PtrFrameLayout_ptr_mode, 4)); + arr.recycle(); } @@ -108,14 +122,81 @@ public PtrFrameLayout(Context context, AttributeSet attrs, int defStyle) { final ViewConfiguration conf = ViewConfiguration.get(getContext()); mPagingTouchSlop = conf.getScaledTouchSlop() * 2; + mPtrIndicator.setTouchSlop(conf.getScaledTouchSlop()); + } + + private Mode getModeFromIndex(int index) { + switch (index) { + case 0: + return Mode.NONE; + case 1: + return Mode.REFRESH; + case 2: + return Mode.LOAD_MORE; + case 3: + return Mode.BOTH; + default: + return Mode.BOTH; + } } @Override protected void onFinishInflate() { final int childCount = getChildCount(); - if (childCount > 2) { - throw new IllegalStateException("PtrFrameLayout only can host 2 elements"); - } else if (childCount == 2) { + if (childCount > 3) { + throw new IllegalStateException("PtrFrameLayout only can host 3 elements"); + } else if (childCount == 3) { + if (mHeaderId != 0 && mHeaderView == null) { + mHeaderView = findViewById(mHeaderId); + } + if (mContainerId != 0 && mContent == null) { + mContent = findViewById(mContainerId); + } + if (mFooterId != 0 && mFooterView == null) { + mFooterView = findViewById(mFooterId); + } + // not specify header or content or footer + if (mContent == null || mHeaderView == null || mFooterView == null) { + final View child1 = getChildAt(0); + final View child2 = getChildAt(1); + final View child3 = getChildAt(2); + // all are not specified + if (mContent == null && mHeaderView == null && mFooterView == null) { + mHeaderView = child1; + mContent = child2; + mFooterView = child3; + } + // only some are specified + else { + ArrayList view = new ArrayList(3) {{ + add(child1); + add(child2); + add(child3); + }}; + if (mHeaderView != null) { + view.remove(mHeaderView); + } + if (mContent != null) { + view.remove(mContent); + } + if (mFooterView != null) { + view.remove(mFooterView); + } + if (mHeaderView == null && view.size() > 0) { + mHeaderView = view.get(0); + view.remove(0); + } + if (mContent == null && view.size() > 0) { + mContent = view.get(0); + view.remove(0); + } + if (mFooterView == null && view.size() > 0) { + mFooterView = view.get(0); + view.remove(0); + } + } + } + } else if (childCount == 2) { // ignore the footer by default if (mHeaderId != 0 && mHeaderView == null) { mHeaderView = findViewById(mHeaderId); } @@ -165,6 +246,9 @@ protected void onFinishInflate() { if (mHeaderView != null) { mHeaderView.bringToFront(); } + if (mFooterView != null) { + mFooterView.bringToFront(); + } super.onFinishInflate(); } @@ -198,6 +282,13 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { mPtrIndicator.setHeaderHeight(mHeaderHeight); } + if (mFooterView != null) { + measureChildWithMargins(mFooterView, widthMeasureSpec, 0, heightMeasureSpec, 0); + MarginLayoutParams lp = (MarginLayoutParams) mFooterView.getLayoutParams(); + mFooterHeight = mFooterView.getMeasuredHeight() + lp.topMargin + lp.bottomMargin; + mPtrIndicator.setFooterHeight(mFooterHeight); + } + if (mContent != null) { measureContentView(mContent, widthMeasureSpec, heightMeasureSpec); if (DEBUG && DEBUG_LAYOUT) { @@ -230,35 +321,69 @@ protected void onLayout(boolean flag, int i, int j, int k, int l) { } private void layoutChildren() { - int offsetX = mPtrIndicator.getCurrentPosY(); + // because the header and footer can not show at the same time, so when header has a offset, the footer's offset should be 0, vice versa.. + int offsetHeaderY; + int offsetFooterY; + if (mPtrIndicator.isHeader()) { + offsetHeaderY = mPtrIndicator.getCurrentPosY(); + offsetFooterY = 0; + } else { + offsetHeaderY = 0; + offsetFooterY = mPtrIndicator.getCurrentPosY(); + } int paddingLeft = getPaddingLeft(); int paddingTop = getPaddingTop(); + int contentBottom = 0; + + if (DEBUG && DEBUG_LAYOUT) { + PtrCLog.d(LOG_TAG, "onLayout offset: %s %s %s %s", offsetHeaderY, offsetFooterY, isPinContent(), mPtrIndicator.isHeader()); + } if (mHeaderView != null) { MarginLayoutParams lp = (MarginLayoutParams) mHeaderView.getLayoutParams(); final int left = paddingLeft + lp.leftMargin; - final int top = paddingTop + lp.topMargin + offsetX - mHeaderHeight; + final int top = paddingTop + lp.topMargin + offsetHeaderY - mHeaderHeight; final int right = left + mHeaderView.getMeasuredWidth(); final int bottom = top + mHeaderView.getMeasuredHeight(); mHeaderView.layout(left, top, right, bottom); if (DEBUG && DEBUG_LAYOUT) { - PtrCLog.d(LOG_TAG, "onLayout header: %s %s %s %s", left, top, right, bottom); + PtrCLog.d(LOG_TAG, "onLayout header: %s %s %s %s %s", left, top, right, bottom, mHeaderView.getMeasuredHeight()); } } if (mContent != null) { - if (isPinContent()) { - offsetX = 0; - } MarginLayoutParams lp = (MarginLayoutParams) mContent.getLayoutParams(); - final int left = paddingLeft + lp.leftMargin; - final int top = paddingTop + lp.topMargin + offsetX; - final int right = left + mContent.getMeasuredWidth(); - final int bottom = top + mContent.getMeasuredHeight(); + int left; + int top; + int right; + int bottom; + if (mPtrIndicator.isHeader()) { + left = paddingLeft + lp.leftMargin; + top = paddingTop + lp.topMargin + (isPinContent() ? 0 : offsetHeaderY); + right = left + mContent.getMeasuredWidth(); + bottom = top + mContent.getMeasuredHeight(); + } else { + left = paddingLeft + lp.leftMargin; + top = paddingTop + lp.topMargin - (isPinContent() ? 0 : offsetFooterY); + right = left + mContent.getMeasuredWidth(); + bottom = top + mContent.getMeasuredHeight(); + } + contentBottom = bottom; if (DEBUG && DEBUG_LAYOUT) { - PtrCLog.d(LOG_TAG, "onLayout content: %s %s %s %s", left, top, right, bottom); + PtrCLog.d(LOG_TAG, "onLayout content: %s %s %s %s %s", left, top, right, bottom, mContent.getMeasuredHeight()); } mContent.layout(left, top, right, bottom); } + if (mFooterView != null) { + MarginLayoutParams lp = (MarginLayoutParams) mFooterView.getLayoutParams(); + final int left = paddingLeft + lp.leftMargin; + final int top = paddingTop + lp.topMargin + contentBottom - (isPinContent() ? offsetFooterY : 0); + final int right = left + mFooterView.getMeasuredWidth(); + final int bottom = top + mFooterView.getMeasuredHeight(); + mFooterView.layout(left, top, right, bottom); + if (DEBUG && DEBUG_LAYOUT) { + PtrCLog.d(LOG_TAG, "onLayout footer: %s %s %s %s %s", left, top, right, bottom, mFooterView.getMeasuredHeight()); + } + } } public boolean dispatchTouchEventSupper(MotionEvent e) { @@ -309,9 +434,10 @@ public boolean dispatchTouchEvent(MotionEvent e) { float offsetY = mPtrIndicator.getOffsetY(); if (mDisableWhenHorizontalMove && !mPreventForHorizontal && (Math.abs(offsetX) > mPagingTouchSlop && Math.abs(offsetX) > Math.abs(offsetY))) { - if (mPtrIndicator.isInStartPosition()) { + //comment this check method is used to fix the ViewPager on top of PtrFrameLayout touch envent conflict + //if (mPtrIndicator.isInStartPosition()) { mPreventForHorizontal = true; - } + //} } if (mPreventForHorizontal) { return dispatchTouchEventSupper(e); @@ -319,26 +445,74 @@ public boolean dispatchTouchEvent(MotionEvent e) { boolean moveDown = offsetY > 0; boolean moveUp = !moveDown; - boolean canMoveUp = mPtrIndicator.hasLeftStartPosition(); + + boolean canMoveUp = mPtrIndicator.isHeader() && mPtrIndicator.hasLeftStartPosition(); // if the header is showing + + boolean canMoveDown = mFooterView != null && !mPtrIndicator.isHeader() && mPtrIndicator.hasLeftStartPosition(); // if the footer is showing + + boolean canHeaderMoveDown = mPtrHandler != null && mPtrHandler.checkCanDoRefresh(this, mContent, mHeaderView) && (mMode.ordinal() & 1) > 0; + boolean canFooterMoveUp = mPtrHandler != null && mFooterView != null // The footer view could be null, so need double check + && mPtrHandler instanceof PtrHandler2 && ((PtrHandler2) mPtrHandler).checkCanDoLoadMore(this, mContent, mFooterView) && (mMode.ordinal() & 2) > 0; if (DEBUG) { - boolean canMoveDown = mPtrHandler != null && mPtrHandler.checkCanDoRefresh(this, mContent, mHeaderView); - PtrCLog.v(LOG_TAG, "ACTION_MOVE: offsetY:%s, currentPos: %s, moveUp: %s, canMoveUp: %s, moveDown: %s: canMoveDown: %s", offsetY, mPtrIndicator.getCurrentPosY(), moveUp, canMoveUp, moveDown, canMoveDown); + PtrCLog.v(LOG_TAG, "ACTION_MOVE: offsetY:%s, currentPos: %s, moveUp: %s, canMoveUp: %s, moveDown: %s: canMoveDown: %s canHeaderMoveDown: %s canFooterMoveUp: %s", offsetY, mPtrIndicator.getCurrentPosY(), moveUp, canMoveUp, moveDown, canMoveDown, canHeaderMoveDown, canFooterMoveUp); + } + + // if either the header and footer are not showing + if (!canMoveUp && !canMoveDown) { + // disable move when header not reach top + if (moveDown && !canHeaderMoveDown) { + return dispatchTouchEventSupper(e); + } + if (moveUp && !canFooterMoveUp) { + return dispatchTouchEventSupper(e); + } + + // should show up header + if (moveDown) { + moveHeaderPos(offsetY); + return true; + } + + // should show up footer + if (moveUp) { + moveFooterPos(offsetY); + return true; + } } - // disable move when header not reach top - if (moveDown && mPtrHandler != null && !mPtrHandler.checkCanDoRefresh(this, mContent, mHeaderView)) { + // if header is showing, then no need to move footer + if (canMoveUp) { + moveHeaderPos(offsetY); + return true; + } + //disable move when moved distance is lesser than touchslop, + //Add this method is used to fix the ViewPager on top of PtrFrameLayout touch envent conflict + if(mPtrIndicator.moveLesserThanTouchSlop()){ return dispatchTouchEventSupper(e); } - if ((moveUp && canMoveUp) || moveDown) { - movePos(offsetY); + // if footer is showing, then no need to move header + // When status is completing, that is, the footer is hiding, disable pull up + if (canMoveDown && mStatus != PTR_STATUS_COMPLETE) { + moveFooterPos(offsetY); return true; } } return dispatchTouchEventSupper(e); } + private void moveFooterPos(float deltaY) { + mPtrIndicator.setIsHeader(false); + // to keep the consistence with refresh, need to converse the deltaY + movePos(-deltaY); + } + + private void moveHeaderPos(float deltaY) { + mPtrIndicator.setIsHeader(true); + movePos(deltaY); + } + /** * if deltaY > 0, move the content down * @@ -362,10 +536,9 @@ private void movePos(float deltaY) { } to = PtrIndicator.POS_START; } - mPtrIndicator.setCurrentPos(to); int change = to - mPtrIndicator.getLastPosY(); - updatePos(change); + updatePos(mPtrIndicator.isHeader() ? change : -change); } private void updatePos(int change) { @@ -420,7 +593,11 @@ private void updatePos(int change) { change, mPtrIndicator.getCurrentPosY(), mPtrIndicator.getLastPosY(), mContent.getTop(), mHeaderHeight); } - mHeaderView.offsetTopAndBottom(change); + if (mPtrIndicator.isHeader()) { + mHeaderView.offsetTopAndBottom(change); + } else { + mFooterView.offsetTopAndBottom(change); + } if (!isPinContent()) { mContent.offsetTopAndBottom(change); } @@ -440,6 +617,10 @@ public int getHeaderHeight() { return mHeaderHeight; } + public int getFooterHeight() { + return mFooterHeight; + } + private void onRelease(boolean stayForLoading) { tryToPerformRefresh(); @@ -488,7 +669,7 @@ public void run() { * Scroll back to to if is not under touch */ private void tryScrollBackToTop() { - if (!mPtrIndicator.isUnderTouch()) { + if (!mPtrIndicator.isUnderTouch() && mPtrIndicator.hasLeftStartPosition()) { mScrollChecker.tryToScrollTo(PtrIndicator.POS_START, mDurationToCloseHeader); } } @@ -536,7 +717,13 @@ private void performRefresh() { } } if (mPtrHandler != null) { - mPtrHandler.onRefreshBegin(this); + if (mPtrIndicator.isHeader()) { + mPtrHandler.onRefreshBegin(this); + } else { + if (mPtrHandler instanceof PtrHandler2) { + ((PtrHandler2) mPtrHandler).onLoadMoreBegin(this); + } + } } } @@ -672,7 +859,19 @@ private void clearFlag() { mFlag = mFlag & ~MASK_AUTO_REFRESH; } + public void autoLoadMore() { + autoRefresh(true, mDurationToCloseHeader, false); + } + + public void autoLoadMore(boolean atOnce) { + autoRefresh(atOnce, mDurationToCloseHeader, false); + } + public void autoRefresh(boolean atOnce, int duration) { + autoRefresh(atOnce, duration, true); + } + + public void autoRefresh(boolean atOnce, int duration, boolean isHeader) { if (mStatus != PTR_STATUS_INIT) { return; @@ -687,6 +886,7 @@ public void autoRefresh(boolean atOnce, int duration) { PtrCLog.i(LOG_TAG, "PtrUIHandler: onUIRefreshPrepare, mFlag %s", mFlag); } } + mPtrIndicator.setIsHeader(isHeader); mScrollChecker.tryToScrollTo(mPtrIndicator.getOffsetToRefresh(), duration); if (atOnce) { mStatus = PTR_STATUS_LOADING; @@ -788,6 +988,14 @@ public void setPtrIndicator(PtrIndicator slider) { mPtrIndicator = slider; } + public void setMode(Mode mode) { + mMode = mode; + } + + public Mode getMode() { + return mMode; + } + @SuppressWarnings({"unused"}) public float getResistance() { return mPtrIndicator.getResistance(); @@ -888,6 +1096,19 @@ public void setHeaderView(View header) { addView(header); } + public void setFooterView(View footer) { + if (mFooterView != null && footer != null && mHeaderView != footer) { + removeView(mHeaderView); + } + ViewGroup.LayoutParams lp = footer.getLayoutParams(); + if (lp == null) { + lp = new LayoutParams(-1, -2); + footer.setLayoutParams(lp); + } + mFooterView = footer; + addView(footer); + } + @Override protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { return p != null && p instanceof LayoutParams; @@ -976,13 +1197,21 @@ public void run() { } if (!finish) { mLastFlingY = curY; - movePos(deltaY); + if (mPtrIndicator.isHeader()) { + moveHeaderPos(deltaY); + } else { + moveFooterPos(-deltaY); + } post(this); } else { finish(); } } + public boolean isRunning() { + return mScroller.isFinished(); + } + private void finish() { if (DEBUG) { PtrCLog.v(LOG_TAG, "finish, currentPos:%s", mPtrIndicator.getCurrentPosY()); diff --git a/ptr-lib/src/in/srain/cube/views/ptr/PtrHandler.java b/ptr-lib/src/in/srain/cube/views/ptr/PtrHandler.java index 6cab011..4d8be52 100644 --- a/ptr-lib/src/in/srain/cube/views/ptr/PtrHandler.java +++ b/ptr-lib/src/in/srain/cube/views/ptr/PtrHandler.java @@ -9,12 +9,12 @@ public interface PtrHandler { *

* {@link in.srain.cube.views.ptr.PtrDefaultHandler#checkContentCanBePulledDown} */ - public boolean checkCanDoRefresh(final PtrFrameLayout frame, final View content, final View header); + boolean checkCanDoRefresh(final PtrFrameLayout frame, final View content, final View header); /** * When refresh begin * * @param frame */ - public void onRefreshBegin(final PtrFrameLayout frame); + void onRefreshBegin(final PtrFrameLayout frame); } \ No newline at end of file diff --git a/ptr-lib/src/in/srain/cube/views/ptr/PtrHandler2.java b/ptr-lib/src/in/srain/cube/views/ptr/PtrHandler2.java new file mode 100644 index 0000000..3006af6 --- /dev/null +++ b/ptr-lib/src/in/srain/cube/views/ptr/PtrHandler2.java @@ -0,0 +1,20 @@ +package in.srain.cube.views.ptr; + +import android.view.View; + +public interface PtrHandler2 extends PtrHandler{ + + /** + * Check can do load more or not. For example the content is empty or the first child is in view. + *

+ * {@link PtrDefaultHandler#checkContentCanBePulledDown} + */ + boolean checkCanDoLoadMore(final PtrFrameLayout frame, final View content, final View footer); + + /** + * When load more begin + * + * @param frame + */ + void onLoadMoreBegin(final PtrFrameLayout frame); +} \ No newline at end of file diff --git a/ptr-lib/src/in/srain/cube/views/ptr/indicator/PtrIndicator.java b/ptr-lib/src/in/srain/cube/views/ptr/indicator/PtrIndicator.java index 769b667..5d32d5e 100644 --- a/ptr-lib/src/in/srain/cube/views/ptr/indicator/PtrIndicator.java +++ b/ptr-lib/src/in/srain/cube/views/ptr/indicator/PtrIndicator.java @@ -6,13 +6,17 @@ public class PtrIndicator { public final static int POS_START = 0; protected int mOffsetToRefresh = 0; + protected int mOffsetToLoadMore = 0; private PointF mPtLastMove = new PointF(); + private PointF mPtLastDown = new PointF(); private float mOffsetX; private float mOffsetY; private int mCurrentPos = 0; private int mLastPos = 0; private int mHeaderHeight; + private int mFooterHeight; private int mPressedPos = 0; + private boolean isHeader = true; private float mRatioOfHeaderHeightToRefresh = 1.2f; private float mResistance = 1.7f; @@ -21,6 +25,16 @@ public class PtrIndicator { // record the refresh complete position private int mRefreshCompleteY = 0; + private int mTouchSlop; + + public boolean isHeader() { + return isHeader; + } + + public void setIsHeader(boolean isHeader) { + this.isHeader = isHeader; + } + public boolean isUnderTouch() { return mIsUnderTouch; } @@ -52,6 +66,7 @@ protected void processOnMove(float currentX, float currentY, float offsetX, floa public void setRatioOfHeaderHeightToRefresh(float ratio) { mRatioOfHeaderHeightToRefresh = ratio; mOffsetToRefresh = (int) (mHeaderHeight * ratio); + mOffsetToLoadMore = (int) (mFooterHeight * ratio); } public float getRatioOfHeaderToHeightRefresh() { @@ -62,15 +77,21 @@ public int getOffsetToRefresh() { return mOffsetToRefresh; } + public int getOffstToLoadMore() { + return mOffsetToLoadMore; + } + public void setOffsetToRefresh(int offset) { mRatioOfHeaderHeightToRefresh = mHeaderHeight * 1f / offset; mOffsetToRefresh = offset; + mOffsetToLoadMore = offset; } public void onPressDown(float x, float y) { mIsUnderTouch = true; mPressedPos = mCurrentPos; mPtLastMove.set(x, y); + mPtLastDown.set(x, y); } public final void onMove(float x, float y) { @@ -123,8 +144,14 @@ public void setHeaderHeight(int height) { updateHeight(); } + public void setFooterHeight(int height) { + mFooterHeight = height; + updateHeight(); + } + protected void updateHeight() { mOffsetToRefresh = (int) (mRatioOfHeaderHeightToRefresh * mHeaderHeight); + mOffsetToLoadMore = (int) (mRatioOfHeaderHeightToRefresh * mFooterHeight); } public void convertFrom(PtrIndicator ptrSlider) { @@ -145,6 +172,15 @@ public boolean hasJustBackToStartPosition() { return mLastPos != POS_START && isInStartPosition(); } + public void setTouchSlop(int touchSlop){ + this.mTouchSlop = touchSlop; + } + + public boolean moveLesserThanTouchSlop(){ + + return Math.abs(mPtLastMove.y - mPtLastDown.y) < mTouchSlop; + } + public boolean isOverOffsetToRefresh() { return mCurrentPos >= getOffsetToRefresh(); }