├── .gitignore ├── Pics ├── ScrollingTabs.gif ├── SimpleApp.gif └── SlidingMenu.gif ├── PullToRefreshListView.md ├── PullToRefreshListView ├── .project ├── AndroidManifest.xml ├── project.properties ├── res │ ├── drawable-hdpi │ │ ├── ic_launcher.png │ │ ├── indicator_arrow.png │ │ └── loading_logo.png │ ├── drawable-mdpi │ │ └── ic_launcher.png │ ├── drawable-xhdpi │ │ └── ic_launcher.png │ ├── drawable-xxhdpi │ │ └── ic_launcher.png │ ├── layout │ │ ├── load_more_footer.xml │ │ └── pull_to_refresh_header.xml │ ├── values-en │ │ └── strings.xml │ ├── values-sw600dp │ │ └── dimens.xml │ ├── values-sw720dp-land │ │ └── dimens.xml │ ├── values-v11 │ │ └── styles.xml │ ├── values-v14 │ │ └── styles.xml │ └── values │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml └── src │ └── com │ └── charon │ └── pulltorefreshlistview │ ├── LoadMoreGridView.java │ ├── LoadMoreListView.java │ ├── PullRefreshAndLoadMoreListView.java │ └── PullToRefreshListView.java ├── PullToRefreshListViewDemo ├── .project ├── AndroidManifest.xml ├── project.properties ├── res │ ├── drawable-hdpi │ │ └── ic_launcher.png │ ├── drawable-mdpi │ │ └── ic_launcher.png │ ├── drawable-xhdpi │ │ └── ic_launcher.png │ ├── drawable-xxhdpi │ │ └── ic_launcher.png │ ├── layout │ │ ├── activity_loadmore.xml │ │ ├── activity_loadmoregrid.xml │ │ ├── activity_main.xml │ │ ├── activity_pull_and_loadmore.xml │ │ └── activity_pulltorefresh.xml │ ├── menu │ │ └── main.xml │ ├── values-en │ │ └── strings.xml │ ├── values-sw600dp │ │ └── dimens.xml │ ├── values-sw720dp-land │ │ └── dimens.xml │ ├── values-v11 │ │ └── styles.xml │ ├── values-v14 │ │ └── styles.xml │ └── values │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml └── src │ └── com │ └── charon │ └── pulltorefresh │ └── demo │ ├── LoadMoreActivity.java │ ├── LoadMoreGridActivity.java │ ├── MainActivity.java │ ├── PullAndLoadMoreActivity.java │ └── PullToRefreshActivity.java ├── README.md ├── README_ScrollingTabs.md ├── README_SlidingMenu.md ├── ScrollingTabs.md ├── ScrollingTabs ├── .gitignore ├── .project ├── AndroidManifest.xml ├── libs │ └── android-support-v4.jar ├── project.properties ├── res │ ├── drawable-mdpi │ │ └── ic_launcher.png │ ├── drawable-xhdpi │ │ └── ic_launcher.png │ ├── drawable-xxhdpi │ │ └── ic_launcher.png │ ├── drawable │ │ └── triangle.xml │ ├── values-sw600dp │ │ └── dimens.xml │ ├── values-sw720dp-land │ │ └── dimens.xml │ ├── values-v11 │ │ └── styles.xml │ ├── values-v14 │ │ └── styles.xml │ └── values │ │ └── strings.xml └── src │ └── com │ └── charon │ └── scrollingtabs │ └── view │ ├── ScrollingTabs.java │ └── TriangleScrollingTabs.java ├── ScrollingTabsDemo ├── .gitignore ├── .project ├── AndroidManifest.xml ├── project.properties ├── res │ ├── drawable-hdpi │ │ └── ic_launcher.png │ ├── drawable-mdpi │ │ └── ic_launcher.png │ ├── drawable-xhdpi │ │ └── ic_launcher.png │ ├── drawable-xxhdpi │ │ └── ic_launcher.png │ ├── layout │ │ ├── activity_main.xml │ │ ├── activity_scrollingtabs.xml │ │ ├── activity_trianglescrollingtabs.xml │ │ ├── tab.xml │ │ └── triangletab.xml │ ├── menu │ │ └── main.xml │ ├── values-sw600dp │ │ └── dimens.xml │ ├── values-sw720dp-land │ │ └── dimens.xml │ ├── values-v11 │ │ └── styles.xml │ ├── values-v14 │ │ └── styles.xml │ └── values │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml └── src │ └── com │ └── charon │ └── scrollingtabsdemo │ ├── MainActivity.java │ ├── ScroolingTabsActivity.java │ └── TriangleScroolingTabsActivity.java ├── SimpleApp ├── .project ├── AndroidManifest.xml ├── libs │ └── android-support-v4.jar ├── project.properties ├── res │ ├── drawable-hdpi │ │ ├── base_actionbar_bg.9.png │ │ ├── base_main_action_personal_normal.png │ │ ├── biz_news_main_back_normal.png │ │ ├── head_default.jpg │ │ ├── ic_launcher.png │ │ ├── indicator_arrow.png │ │ └── loading_logo.png │ ├── drawable-mdpi │ │ └── ic_launcher.png │ ├── drawable-xhdpi │ │ └── ic_launcher.png │ ├── drawable-xxhdpi │ │ └── ic_launcher.png │ ├── drawable │ │ └── tab_bg.xml │ ├── layout │ │ ├── activity_main.xml │ │ ├── fragment_center.xml │ │ ├── fragment_left.xml │ │ ├── fragment_right.xml │ │ ├── frame_center.xml │ │ ├── frame_left.xml │ │ ├── frame_right.xml │ │ ├── item_lv_center.xml │ │ ├── load_more_footer.xml │ │ ├── lv_pager_center.xml │ │ ├── pull_to_refresh_header.xml │ │ └── tab_center.xml │ ├── menu │ │ └── main.xml │ └── values │ │ ├── color.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml └── src │ └── com │ └── charon │ └── simpleapp │ ├── MainActivity.java │ ├── base │ └── BaseActivity.java │ └── fragment │ ├── CenterFragment.java │ ├── LeftFragment.java │ ├── PagerFragment.java │ └── RightFragment.java ├── SlidingMenu.md ├── SlidingMenu ├── .gitignore ├── .project ├── AndroidManifest.xml ├── project.properties ├── res │ ├── drawable-hdpi │ │ └── ic_launcher.png │ ├── drawable-mdpi │ │ └── ic_launcher.png │ ├── drawable-xhdpi │ │ └── ic_launcher.png │ ├── drawable-xxhdpi │ │ └── ic_launcher.png │ ├── menu │ │ └── main.xml │ ├── values-sw600dp │ │ └── dimens.xml │ ├── values-sw720dp-land │ │ └── dimens.xml │ ├── values-v11 │ │ └── styles.xml │ ├── values-v14 │ │ └── styles.xml │ └── values │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml └── src │ └── com │ └── charon │ └── slidingmenu │ └── view │ └── SlidingMenu.java └── SlidingMenuDemo ├── .project ├── AndroidManifest.xml ├── libs └── android-support-v4.jar ├── proguard-project.txt ├── project.properties ├── res ├── drawable-hdpi │ ├── base_actionbar_bg.9.png │ ├── base_main_action_personal_normal.png │ ├── biz_news_main_back_normal.png │ ├── head_default.jpg │ └── ic_launcher.png ├── drawable-mdpi │ └── ic_launcher.png ├── drawable-xhdpi │ └── ic_launcher.png ├── drawable-xxhdpi │ └── ic_launcher.png ├── layout │ ├── activity_main.xml │ ├── fragment_center.xml │ ├── fragment_left.xml │ ├── fragment_right.xml │ ├── frame_center.xml │ ├── frame_left.xml │ └── frame_right.xml ├── menu │ └── main.xml ├── values-sw600dp │ └── dimens.xml ├── values-sw720dp-land │ └── dimens.xml ├── values-v11 │ └── styles.xml ├── values-v14 │ └── styles.xml └── values │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml └── src └── com └── charon └── slidingmenudemo ├── MainActivity.java └── fragment ├── CenterFragment.java ├── LeftFragment.java ├── PagerFragment.java └── RightFragment.java /.gitignore: -------------------------------------------------------------------------------- 1 | /PullToRefreshListView/.classpath 2 | /PullToRefreshListView/.settings 3 | /PullToRefreshListViewDemo/proguard-project.txt 4 | /PullToRefreshListViewDemo/.settings 5 | /PullToRefreshListViewDemo/assets 6 | /PullToRefreshListViewDemo/bin 7 | /PullToRefreshListViewDemo/gen 8 | /PullToRefreshListViewDemo/libs 9 | /PullToRefreshListViewDemo/.classpath 10 | /ScrollingTabs/.settings 11 | /ScrollingTabs/.classpath 12 | /ScrollingTabsDemo/.settings 13 | /ScrollingTabsDemo/.classpath 14 | /SimpleApp/.classpath 15 | /SimpleApp/.settings 16 | /SimpleApp/assets 17 | /SimpleApp/bin 18 | /SimpleApp/gen 19 | /PullToRefreshListView/bin 20 | /PullToRefreshListView/gen 21 | /PullToRefreshListView/assets 22 | /SlidingMenu/.classpath 23 | /SlidingMenu/.settings 24 | /SlidingMenuDemo/.classpath 25 | /SlidingMenuDemo/.settings 26 | /SlidingMenuDemo/assets 27 | /SlidingMenuDemo/bin 28 | /SlidingMenuDemo/gen 29 | -------------------------------------------------------------------------------- /Pics/ScrollingTabs.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CharonChui/PullRefresh-SlidingMenu/81246fa6d8f8708f8ab53f13bc3b09f38ede24ee/Pics/ScrollingTabs.gif -------------------------------------------------------------------------------- /Pics/SimpleApp.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CharonChui/PullRefresh-SlidingMenu/81246fa6d8f8708f8ab53f13bc3b09f38ede24ee/Pics/SimpleApp.gif -------------------------------------------------------------------------------- /Pics/SlidingMenu.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CharonChui/PullRefresh-SlidingMenu/81246fa6d8f8708f8ab53f13bc3b09f38ede24ee/Pics/SlidingMenu.gif -------------------------------------------------------------------------------- /PullToRefreshListView.md: -------------------------------------------------------------------------------- 1 | 自定义下拉刷新ListView 2 | === 3 | 4 | PullToRefreshListView 5 | --- 6 | 7 | **原理:** 8 | 拉刷新`ListView`无非就是对普通的`List View`添加一个`HeaderView`,然后通过对`ListView onTouchEvent`来获取当前下拉刷新的状态。然后去改变`HeaderView`的状态。 9 | 10 | 1. 自定义`ListView`,在构造方法中去添加`HeaderView` 11 | 通过`ListView.addHeaderView()`去添加`HeaderView`的时候,`HeaderView`会显示在屏幕的最初位置,我们需要它默认的时候是在屏幕的上方,这样默认时是不可见的,但是我们下拉`ListView`的时候,它就能够显示出来。这就要通过设置`HeaderView`的`padding`来实现它的隐藏。注意:View的显示最初要经过`Measure`测量宽高,我们在构造方法去添加的时候,该View可能并没有被测量,所以在获取`HeaderView`高度的时候会为0,这时候我们要手动的去测量一下`HeaderView`。 12 | ```java 13 | 14 | /** 15 | * 当前状态 16 | */ 17 | private State mState = State.ORIGNAL; 18 | 19 | public PullToRefreshListView(Context context, AttributeSet attrs, 20 | int defStyle) { 21 | super(context, attrs, defStyle); 22 | initView(context); 23 | } 24 | 25 | public PullToRefreshListView(Context context, AttributeSet attrs) { 26 | super(context, attrs); 27 | initView(context); 28 | } 29 | 30 | public PullToRefreshListView(Context context) { 31 | super(context); 32 | initView(context); 33 | } 34 | 35 | private void initView(Context context) { 36 | LayoutInflater inflater = (LayoutInflater) context 37 | .getSystemService(Context.LAYOUT_INFLATER_SERVICE); 38 | 39 | mHeader = inflater.inflate(R.layout.pull_to_refresh_header, null); 40 | iv_arrow = (ImageView) mHeader.findViewById(R.id.iv_arrow); 41 | pb_refresh = (ProgressBar) mHeader.findViewById(R.id.pb_refresh); 42 | tv_title = (TextView) mHeader.findViewById(R.id.tv_title); 43 | tv_time = (TextView) mHeader.findViewById(R.id.tv_time); 44 | 45 | measureHeaderView(mHeader); 46 | 47 | mHeaderHeight = mHeader.getMeasuredHeight(); 48 | // To make header view above the window, so use -mHeaderHeight. 49 | mHeader.setPadding(0, -mHeaderHeight, 0, 0); 50 | 51 | mHeader.invalidate(); 52 | 53 | addHeaderView(mHeader); 54 | } 55 | 56 | /** 57 | * 下拉刷新所有的状态 58 | */ 59 | public enum State { 60 | ORIGNAL, PULL_TO_REFRESH, REFRESHING, RELEASE_TO_REFRESH; 61 | } 62 | ``` 63 | 64 | 2. 重写onTouchEvent,来监听手指的下拉 65 | ```java 66 | public boolean onTouchEvent(MotionEvent ev) { 67 | int y = (int) ev.getRawY(); 68 | int action = ev.getAction(); 69 | switch (action) { 70 | case MotionEvent.ACTION_DOWN: 71 | //记录手指点下的位置 72 | downPositionY = y; 73 | break; 74 | case MotionEvent.ACTION_MOVE: 75 | //手指当前滑动的位置 76 | currentPositionY = y; 77 | //手指移动的距离,由于HeaderView高度固定,但是手指下拉的高度可以最大为屏幕的高度,如手指下拉屏幕高度时,HeaderView会很难看,所以我们让下拉的距离进行一个缩放。 78 | pullDistance = (currentPositionY - downPositionY) / RATIO; 79 | 80 | if (mState == State.REFRESHING) { 81 | break; 82 | } else if (mState == State.ORIGNAL && pullDistance > 0) { 83 | //如果现在处理起始的状态,并且距离大于0,就说明是下拉了,这时候状态需要变为下拉刷新的状态 84 | mState = State.PULL_TO_REFRESH; 85 | changeState(); 86 | } else if (mState == State.PULL_TO_REFRESH 87 | && pullDistance > mHeaderHeight) { 88 | //当时为下拉刷新的状态,但是下拉的距离大于HeaderView的高度。这时状态要变为松手即可刷新 89 | mState = State.RELEASE_TO_REFRESH; 90 | changeState(); 91 | } else if (mState == State.RELEASE_TO_REFRESH) { 92 | //释放刷新时有三种情况,一是我继续下啦,这时候不用管,因为继续下拉还是释放刷新。二是我手指往上移动,此时HeaderView不完全可见,这时候状态要改变为下拉刷新了。三是我手指上移的很厉害,导致HeaderView完全不可见了,这是状态要改变为起始状态。 93 | if (pullDistance < 0) { 94 | // 如果当时状态为松手刷新,但是这时候我并没有松手,而是直接将手指往上移动,移动回手指最先的位置,这时候状态要变为起始状态。 95 | mState = State.ORIGNAL; 96 | changeState(); 97 | } else if (pullDistance < mHeaderHeight) { 98 | //手指上移,但是并没有移动到HeaderView完全不可见,这时候要将状态改变为下拉刷新 99 | mState = State.PULL_TO_REFRESH; 100 | isBack = true; 101 | changeState(); 102 | } 103 | 104 | } 105 | 106 | //在移动的过程中不断的去改版Padding的值,控制其显示的大小 107 | if (mState != State.REFRESHING) { 108 | mHeader.setPadding(0, (int) (pullDistance - mHeaderHeight), 0, 109 | 0); 110 | } 111 | 112 | break; 113 | case MotionEvent.ACTION_UP: 114 | if (mState == State.REFRESHING) { 115 | //如果当前已经是正在刷新中了,再去下拉就不要处理了 116 | break; 117 | } else if (mState == State.PULL_TO_REFRESH) { 118 | //显现下拉刷新时,松手了,这时候要将其改为起始状态 119 | mState = State.ORIGNAL; 120 | } else if (mState == State.RELEASE_TO_REFRESH) { 121 | //松手刷新时松手了,这时候状态要变为正在刷新中。 122 | mState = State.REFRESHING; 123 | } else { 124 | break; 125 | } 126 | changeState(); 127 | break; 128 | 129 | default: 130 | break; 131 | } 132 | 133 | return super.onTouchEvent(ev); 134 | } 135 | ``` 136 | 137 | 3. 上面的写法仍有些问题。就是我们在`onTouchEvent`中`Move`里面对移动的距离进行了判断,但是`ListView`本身就是一个可以上下滑动的组件,如果我们直接这样判断,那`ListView`本上上下滑动的功能就被我们给抹去了。 138 | ```java 139 | @Override 140 | public boolean onTouchEvent(MotionEvent ev) { 141 | int y = (int) ev.getRawY(); 142 | int action = ev.getAction(); 143 | switch (action) { 144 | case MotionEvent.ACTION_DOWN: 145 | downPositionY = y; 146 | break; 147 | case MotionEvent.ACTION_MOVE: 148 | //一定要加上这句话,来判断当前是否可以下拉刷新 149 | if (!isCanPullToRefresh) { 150 | break; 151 | } 152 | ..... 153 | break; 154 | ``` 155 | 而对于`isCanPullToRefresh`的判断是通过`ListView.setOnScrollListener`去进行判断当前第一个可见条目是不是`ListView`的第一个条目,只有第一个条目在最顶端位置的时候才可以进行下拉刷新。 156 | ```java 157 | super.setOnScrollListener(new OnScrollListener() { 158 | 159 | @Override 160 | public void onScrollStateChanged(AbsListView view, int scrollState) { 161 | } 162 | 163 | @Override 164 | public void onScroll(AbsListView view, int firstVisibleItem, 165 | int visibleItemCount, int totalItemCount) { 166 | 167 | if (firstVisibleItem == 0) { 168 | isCanPullToRefresh = true; 169 | } else { 170 | isCanPullToRefresh = false; 171 | } 172 | } 173 | }); 174 | ``` 175 | 4. 提供刷新接口 176 | ```java 177 | public interface OnRefreshListener { 178 | abstract void onRefresh(); 179 | } 180 | ``` 181 | 5. ChangeState方法 182 | ```java 183 | /** 184 | * Change the state of header view when ListView in different state. 185 | */ 186 | private void changeState() { 187 | //在该方法中对mState进行判断,根据不同状态作出处理,并且去调用刷新方法 188 | } 189 | 190 | /** 191 | *数据刷新完成后需要调用此方法去恢复装填 192 | */ 193 | @SuppressWarnings("deprecation") 194 | public void onRefreshComplete() { 195 | mState = State.ORIGNAL; 196 | changeState(); 197 | tv_time.setText(getResources().getString(R.string.update_time) 198 | + new Date().toLocaleString()); 199 | } 200 | ``` 201 | 202 | LoadMoreListView 203 | --- 204 | 205 | **原理:** 206 | 滑动到底部自动加载更多的`ListView`,无非就是通过对其滑动过程进行监听,一旦滑动到底部的时候我们就去加载新的数据。通过对`ListView`添加`FooterView`,然后在不同的状态控制它的显示与隐藏。 207 | 208 | 1. 自定义ListView的子类,在构造方法中去添加FooterView. 209 | ```java 210 | public class LoadMoreListView extends ListView { 211 | 212 | public LoadMoreListView(Context context, AttributeSet attrs, int defStyle) { 213 | super(context, attrs, defStyle); 214 | init(context); 215 | } 216 | 217 | public LoadMoreListView(Context context, AttributeSet attrs) { 218 | super(context, attrs); 219 | init(context); 220 | } 221 | 222 | public LoadMoreListView(Context context) { 223 | super(context); 224 | init(context); 225 | } 226 | 227 | private void init(Context context) { 228 | mFooterView = View.inflate(context, R.layout.load_more_footer, null); 229 | addFooterView(mFooterView); 230 | hideFooterView(); 231 | } 232 | ``` 233 | 234 | 2. 监听滑动状态,通过`setOnScrollListener`即可实现对状态的监听。 235 | ```java 236 | private void init(Context context) { 237 | mFooterView = View.inflate(context, R.layout.load_more_footer, null); 238 | addFooterView(mFooterView); 239 | hideFooterView(); 240 | //为了防止在使用时调用setOnScrollListener会覆盖此时设置的Listener,我们在此使用super.setOnScrollListenr() 241 | super.setOnScrollListener(superOnScrollListener); 242 | } 243 | ``` 244 | 245 | 3. 在`ScrollListener`中去判断当前滑动的状态。从而依据不同的状态去控制`FooterView`的显示与隐藏。 246 | ```java 247 | private OnScrollListener superOnScrollListener = new OnScrollListener() { 248 | 249 | @Override 250 | public void onScrollStateChanged(AbsListView view, int scrollState) { 251 | mCurrentScrollState = scrollState; 252 | } 253 | 254 | @Override 255 | public void onScroll(AbsListView view, int firstVisibleItem, 256 | int visibleItemCount, int totalItemCount) { 257 | if (visibleItemCount == totalItemCount) { 258 | //此时说明当前ListView所有的条目比较少,不足一屏 259 | hideFooterView(); 260 | } else if (!mIsLoading 261 | && (firstVisibleItem + visibleItemCount >= totalItemCount) 262 | && mCurrentScrollState != SCROLL_STATE_IDLE) { 263 | //当第一个可见的条目位置加上当前也所有可见的条目数 等于 ListView当前总的条目数时,就说明已经滑动到了底部,这时候就要去显示FooterView。 264 | showFooterView(); 265 | mIsLoading = true; 266 | if (mOnLoadMoreListener != null) { 267 | mOnLoadMoreListener.onLoadMore(); 268 | } 269 | } 270 | } 271 | }; 272 | ``` 273 | 274 | 4. 提供自动加载更多的接口。 275 | ```java 276 | public interface OnLoadMoreListener { 277 | /** 278 | * Load more data. 279 | */ 280 | void onLoadMore(); 281 | } 282 | ``` 283 | 284 | 在使用的时候,与ListView使用方法相同,只需调用`setOnLoadMoreListener`对其设置`OnLoadMoreListener`即可,然后在数据加载完成后调用`onLoadComplete`方法去恢复状态。 285 | 286 | 287 | PullAndLoadMoreListView 288 | --- 289 | 290 | 将下拉刷新和自动加载更多进行整合。就不多说了 291 | 292 | ------------------------------------------ 293 | 294 | - 邮箱 :charon.chui@gmail.com 295 | - Good Luck! -------------------------------------------------------------------------------- /PullToRefreshListView/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | PullToRefreshListView 4 | 5 | 6 | 7 | 8 | 9 | com.android.ide.eclipse.adt.ResourceManagerBuilder 10 | 11 | 12 | 13 | 14 | com.android.ide.eclipse.adt.PreCompilerBuilder 15 | 16 | 17 | 18 | 19 | org.eclipse.jdt.core.javabuilder 20 | 21 | 22 | 23 | 24 | com.android.ide.eclipse.adt.ApkBuilder 25 | 26 | 27 | 28 | 29 | 30 | com.android.ide.eclipse.adt.AndroidNature 31 | org.eclipse.jdt.core.javanature 32 | 33 | 34 | -------------------------------------------------------------------------------- /PullToRefreshListView/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /PullToRefreshListView/project.properties: -------------------------------------------------------------------------------- 1 | # This file is automatically generated by Android Tools. 2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED! 3 | # 4 | # This file must be checked in Version Control Systems. 5 | # 6 | # To customize properties used by the Ant build system edit 7 | # "ant.properties", and override values to adapt the script to your 8 | # project structure. 9 | # 10 | # To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): 11 | #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt 12 | 13 | # Project target. 14 | target=android-20 15 | android.library=true 16 | -------------------------------------------------------------------------------- /PullToRefreshListView/res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CharonChui/PullRefresh-SlidingMenu/81246fa6d8f8708f8ab53f13bc3b09f38ede24ee/PullToRefreshListView/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /PullToRefreshListView/res/drawable-hdpi/indicator_arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CharonChui/PullRefresh-SlidingMenu/81246fa6d8f8708f8ab53f13bc3b09f38ede24ee/PullToRefreshListView/res/drawable-hdpi/indicator_arrow.png -------------------------------------------------------------------------------- /PullToRefreshListView/res/drawable-hdpi/loading_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CharonChui/PullRefresh-SlidingMenu/81246fa6d8f8708f8ab53f13bc3b09f38ede24ee/PullToRefreshListView/res/drawable-hdpi/loading_logo.png -------------------------------------------------------------------------------- /PullToRefreshListView/res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CharonChui/PullRefresh-SlidingMenu/81246fa6d8f8708f8ab53f13bc3b09f38ede24ee/PullToRefreshListView/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /PullToRefreshListView/res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CharonChui/PullRefresh-SlidingMenu/81246fa6d8f8708f8ab53f13bc3b09f38ede24ee/PullToRefreshListView/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /PullToRefreshListView/res/drawable-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CharonChui/PullRefresh-SlidingMenu/81246fa6d8f8708f8ab53f13bc3b09f38ede24ee/PullToRefreshListView/res/drawable-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /PullToRefreshListView/res/layout/load_more_footer.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 15 | 16 | 22 | 23 | -------------------------------------------------------------------------------- /PullToRefreshListView/res/layout/pull_to_refresh_header.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 8 | 9 | 15 | 16 | 21 | 22 | 27 | 28 | 29 | 34 | 35 | 41 | 42 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /PullToRefreshListView/res/values-en/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | PullToRefreshListView 5 | Settings 6 | Hello world! 7 | Pull To Refresh 8 | Load More 9 | Both 10 | Release To Refresh 11 | Refreshing... 12 | Update 13 | 14 | 15 | -------------------------------------------------------------------------------- /PullToRefreshListView/res/values-sw600dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /PullToRefreshListView/res/values-sw720dp-land/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 128dp 8 | 9 | 10 | -------------------------------------------------------------------------------- /PullToRefreshListView/res/values-v11/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /PullToRefreshListView/res/values-v14/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /PullToRefreshListView/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16dp 5 | 16dp 6 | 7 | 8 | -------------------------------------------------------------------------------- /PullToRefreshListView/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 下拉刷新 5 | 松开刷新 6 | 正在刷新中... 7 | 更新于 8 | 9 | 10 | -------------------------------------------------------------------------------- /PullToRefreshListView/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 14 | 15 | 16 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /PullToRefreshListView/src/com/charon/pulltorefreshlistview/LoadMoreGridView.java: -------------------------------------------------------------------------------- 1 | package com.charon.pulltorefreshlistview; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.widget.AbsListView; 6 | import android.widget.GridView; 7 | 8 | /** 9 | * Android load more GridView when scroll down. 10 | * 11 | * @author CharonChui 12 | */ 13 | public class LoadMoreGridView extends GridView { 14 | private OnScrollListener mOnScrollListener; 15 | private OnLoadMoreListener mOnLoadMoreListener; 16 | 17 | /** 18 | * If is loading now. 19 | */ 20 | private boolean mIsLoading; 21 | 22 | private int mCurrentScrollState; 23 | 24 | public LoadMoreGridView(Context context, AttributeSet attrs, int defStyle) { 25 | super(context, attrs, defStyle); 26 | init(context); 27 | } 28 | 29 | public LoadMoreGridView(Context context, AttributeSet attrs) { 30 | super(context, attrs); 31 | init(context); 32 | } 33 | 34 | public LoadMoreGridView(Context context) { 35 | super(context); 36 | init(context); 37 | } 38 | 39 | private void init(Context context) { 40 | /* 41 | * Must use super.setOnScrollListener() here to avoid override when call this view's setOnScrollListener method 42 | */ 43 | super.setOnScrollListener(mSuperOnScrollListener); 44 | } 45 | 46 | @Override 47 | public void setOnScrollListener(OnScrollListener l) { 48 | mOnScrollListener = l; 49 | } 50 | 51 | /** 52 | * Set load more listener, usually you should get more data here. 53 | * 54 | * @param listener OnLoadMoreListener 55 | * @see OnLoadMoreListener 56 | */ 57 | public void setOnLoadMoreListener(OnLoadMoreListener listener) { 58 | mOnLoadMoreListener = listener; 59 | } 60 | 61 | /** 62 | * When complete load more data, you must use this method to hide the footer view, if not the footer view will be 63 | * shown all the time. 64 | */ 65 | public void onLoadMoreComplete() { 66 | mIsLoading = false; 67 | } 68 | 69 | private OnScrollListener mSuperOnScrollListener = new OnScrollListener() { 70 | 71 | @Override 72 | public void onScrollStateChanged(AbsListView view, int scrollState) { 73 | mCurrentScrollState = scrollState; 74 | // Avoid override when use setOnScrollListener 75 | if (mOnScrollListener != null) { 76 | mOnScrollListener.onScrollStateChanged(view, scrollState); 77 | } 78 | } 79 | 80 | @Override 81 | public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { 82 | if (mOnScrollListener != null) { 83 | mOnScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount); 84 | } 85 | // The count of footer view will be add to visibleItemCount also are 86 | // added to totalItemCount 87 | if (visibleItemCount == totalItemCount) { 88 | // If all the item can not fill screen, we should make the 89 | // footer view invisible. 90 | } else if (!mIsLoading && (firstVisibleItem + visibleItemCount >= totalItemCount) 91 | && mCurrentScrollState != SCROLL_STATE_IDLE) { 92 | mIsLoading = true; 93 | if (mOnLoadMoreListener != null) { 94 | mOnLoadMoreListener.onLoadMore(); 95 | } 96 | } 97 | } 98 | }; 99 | 100 | /** 101 | * Interface for load more 102 | */ 103 | public interface OnLoadMoreListener { 104 | /** 105 | * Load more data. 106 | */ 107 | void onLoadMore(); 108 | } 109 | 110 | } 111 | -------------------------------------------------------------------------------- /PullToRefreshListView/src/com/charon/pulltorefreshlistview/LoadMoreListView.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Charon Chui 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.charon.pulltorefreshlistview; 18 | 19 | import com.charon.pulltorefreshlistview.R; 20 | 21 | import android.content.Context; 22 | import android.util.AttributeSet; 23 | import android.view.View; 24 | import android.widget.AbsListView; 25 | import android.widget.ListView; 26 | 27 | /** 28 | * Android load more ListView when scroll down. 29 | * 30 | * @author Charon Chui 31 | */ 32 | public class LoadMoreListView extends ListView { 33 | protected static final String TAG = "LoadMoreListView"; 34 | private View mFooterView; 35 | private OnScrollListener mOnScrollListener; 36 | private OnLoadMoreListener mOnLoadMoreListener; 37 | 38 | /** 39 | * If is loading now. 40 | */ 41 | private boolean mIsLoading; 42 | 43 | private int mCurrentScrollState; 44 | 45 | public LoadMoreListView(Context context, AttributeSet attrs, int defStyle) { 46 | super(context, attrs, defStyle); 47 | init(context); 48 | } 49 | 50 | public LoadMoreListView(Context context, AttributeSet attrs) { 51 | super(context, attrs); 52 | init(context); 53 | } 54 | 55 | public LoadMoreListView(Context context) { 56 | super(context); 57 | init(context); 58 | } 59 | 60 | private void init(Context context) { 61 | mFooterView = View.inflate(context, R.layout.load_more_footer, null); 62 | addFooterView(mFooterView); 63 | hideFooterView(); 64 | /* 65 | * Must use super.setOnScrollListener() here to avoid override when call this view's setOnScrollListener method 66 | */ 67 | super.setOnScrollListener(superOnScrollListener); 68 | } 69 | 70 | /** 71 | * Hide the load more view(footer view) 72 | */ 73 | private void hideFooterView() { 74 | mFooterView.setVisibility(View.GONE); 75 | } 76 | 77 | /** 78 | * Show load more view 79 | */ 80 | private void showFooterView() { 81 | mFooterView.setVisibility(View.VISIBLE); 82 | } 83 | 84 | @Override 85 | public void setOnScrollListener(OnScrollListener l) { 86 | mOnScrollListener = l; 87 | } 88 | 89 | /** 90 | * Set load more listener, usually you should get more data here. 91 | * 92 | * @param listener OnLoadMoreListener 93 | * @see OnLoadMoreListener 94 | */ 95 | public void setOnLoadMoreListener(OnLoadMoreListener listener) { 96 | mOnLoadMoreListener = listener; 97 | } 98 | 99 | /** 100 | * When complete load more data, you must use this method to hide the footer view, if not the footer view will be 101 | * shown all the time. 102 | */ 103 | public void onLoadMoreComplete() { 104 | mIsLoading = false; 105 | hideFooterView(); 106 | } 107 | 108 | private OnScrollListener superOnScrollListener = new OnScrollListener() { 109 | 110 | @Override 111 | public void onScrollStateChanged(AbsListView view, int scrollState) { 112 | mCurrentScrollState = scrollState; 113 | // Avoid override when use setOnScrollListener 114 | if (mOnScrollListener != null) { 115 | mOnScrollListener.onScrollStateChanged(view, scrollState); 116 | } 117 | } 118 | 119 | @Override 120 | public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { 121 | if (mOnScrollListener != null) { 122 | mOnScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount); 123 | } 124 | // The count of footer view will be add to visibleItemCount also are 125 | // added to totalItemCount 126 | if (visibleItemCount == totalItemCount) { 127 | // If all the item can not fill screen, we should make the 128 | // footer view invisible. 129 | hideFooterView(); 130 | } else if (!mIsLoading && (firstVisibleItem + visibleItemCount >= totalItemCount) 131 | && mCurrentScrollState != SCROLL_STATE_IDLE) { 132 | showFooterView(); 133 | mIsLoading = true; 134 | if (mOnLoadMoreListener != null) { 135 | mOnLoadMoreListener.onLoadMore(); 136 | } 137 | } 138 | } 139 | }; 140 | 141 | /** 142 | * Interface for load more 143 | */ 144 | public interface OnLoadMoreListener { 145 | /** 146 | * Load more data. 147 | */ 148 | void onLoadMore(); 149 | } 150 | 151 | } 152 | -------------------------------------------------------------------------------- /PullToRefreshListView/src/com/charon/pulltorefreshlistview/PullRefreshAndLoadMoreListView.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Charon Chui 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.charon.pulltorefreshlistview; 18 | 19 | import com.charon.pulltorefreshlistview.R; 20 | 21 | import android.content.Context; 22 | import android.util.AttributeSet; 23 | import android.view.View; 24 | import android.widget.AbsListView; 25 | 26 | /** 27 | * Android load more ListView when scroll down. 28 | * 29 | * @author Charon Chui 30 | */ 31 | public class PullRefreshAndLoadMoreListView extends PullToRefreshListView { 32 | protected static final String TAG = "LoadMoreListView"; 33 | private View mFooterView; 34 | private OnScrollListener mOnScrollListener; 35 | private OnLoadMoreListener mOnLoadMoreListener; 36 | 37 | /** 38 | * If is loading now. 39 | */ 40 | private boolean mIsLoading; 41 | 42 | private int mCurrentScrollState; 43 | 44 | public PullRefreshAndLoadMoreListView(Context context, AttributeSet attrs, int defStyle) { 45 | super(context, attrs, defStyle); 46 | init(context); 47 | } 48 | 49 | public PullRefreshAndLoadMoreListView(Context context, AttributeSet attrs) { 50 | super(context, attrs); 51 | init(context); 52 | } 53 | 54 | public PullRefreshAndLoadMoreListView(Context context) { 55 | super(context); 56 | init(context); 57 | } 58 | 59 | private void init(Context context) { 60 | mFooterView = View.inflate(context, R.layout.load_more_footer, null); 61 | addFooterView(mFooterView); 62 | hideFooterView(); 63 | /* 64 | * Must use super.setOnScrollListener() here to avoid override when call this view's setOnScrollListener method 65 | */ 66 | super.setOnScrollListener(superOnScrollListener); 67 | } 68 | 69 | /** 70 | * Hide the load more view(footer view) 71 | */ 72 | private void hideFooterView() { 73 | mFooterView.setVisibility(View.GONE); 74 | } 75 | 76 | /** 77 | * Show load more view 78 | */ 79 | private void showFooterView() { 80 | mFooterView.setVisibility(View.VISIBLE); 81 | } 82 | 83 | @Override 84 | public void setOnScrollListener(OnScrollListener l) { 85 | mOnScrollListener = l; 86 | } 87 | 88 | /** 89 | * Set load more listener, usually you should get more data here. 90 | * 91 | * @param listener OnLoadMoreListener 92 | * @see OnLoadMoreListener 93 | */ 94 | public void setOnLoadMoreListener(OnLoadMoreListener listener) { 95 | mOnLoadMoreListener = listener; 96 | } 97 | 98 | /** 99 | * When complete load more data, you must use this method to hide the footer view, if not the footer view will be 100 | * shown all the time. 101 | */ 102 | public void onLoadMoreComplete() { 103 | mIsLoading = false; 104 | hideFooterView(); 105 | } 106 | 107 | private OnScrollListener superOnScrollListener = new OnScrollListener() { 108 | 109 | @Override 110 | public void onScrollStateChanged(AbsListView view, int scrollState) { 111 | mCurrentScrollState = scrollState; 112 | // Avoid override when use setOnScrollListener 113 | if (mOnScrollListener != null) { 114 | mOnScrollListener.onScrollStateChanged(view, scrollState); 115 | } 116 | } 117 | 118 | @Override 119 | public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { 120 | if (mOnScrollListener != null) { 121 | mOnScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount); 122 | } 123 | // The count of footer view will be add to visibleItemCount also are 124 | // added to totalItemCount 125 | if (visibleItemCount == totalItemCount) { 126 | // If all the item can not fill screen, we should make the 127 | // footer view invisible. 128 | hideFooterView(); 129 | } else if (!mIsLoading && (firstVisibleItem + visibleItemCount >= totalItemCount) 130 | && mCurrentScrollState != SCROLL_STATE_IDLE) { 131 | showFooterView(); 132 | mIsLoading = true; 133 | if (mOnLoadMoreListener != null) { 134 | mOnLoadMoreListener.onLoadMore(); 135 | } 136 | } 137 | } 138 | }; 139 | 140 | /** 141 | * Interface for load more 142 | */ 143 | public interface OnLoadMoreListener { 144 | /** 145 | * Load more data. 146 | */ 147 | void onLoadMore(); 148 | } 149 | 150 | } 151 | -------------------------------------------------------------------------------- /PullToRefreshListView/src/com/charon/pulltorefreshlistview/PullToRefreshListView.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Charon Chui 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.charon.pulltorefreshlistview; 18 | 19 | import java.util.Date; 20 | 21 | import android.annotation.SuppressLint; 22 | import android.content.Context; 23 | import android.util.AttributeSet; 24 | import android.view.LayoutInflater; 25 | import android.view.MotionEvent; 26 | import android.view.View; 27 | import android.view.ViewGroup; 28 | import android.view.animation.Animation; 29 | import android.view.animation.LinearInterpolator; 30 | import android.view.animation.RotateAnimation; 31 | import android.widget.AbsListView; 32 | import android.widget.ImageView; 33 | import android.widget.ListAdapter; 34 | import android.widget.ListView; 35 | import android.widget.ProgressBar; 36 | import android.widget.TextView; 37 | 38 | import com.charon.pulltorefreshlistview.R; 39 | 40 | /** 41 | * Android pull to refresh ListView. 42 | */ 43 | public class PullToRefreshListView extends ListView { 44 | 45 | /** 46 | * Avoid pull all the screen height will make header view too large, so make a scale ratio. 47 | */ 48 | private static final int RATIO = 3; 49 | 50 | private View mHeader; 51 | private ImageView iv_arrow; 52 | private ProgressBar pb_refresh; 53 | private TextView tv_title; 54 | private TextView tv_time; 55 | 56 | /** 57 | * Height of the HeaderView 58 | */ 59 | private int mHeaderHeight; 60 | 61 | /** 62 | * The Y axis position when finger onTouch down. 63 | */ 64 | private int downPositionY; 65 | 66 | /** 67 | * The Y axis position when finger onTouch moving 68 | */ 69 | private int currentPositionY; 70 | 71 | /** 72 | * The pull distance. 73 | */ 74 | private int pullDistance; 75 | 76 | /** 77 | * Current State 78 | */ 79 | private State mState; 80 | 81 | /** 82 | * Animation for the arrow when is moving clockwise. 83 | */ 84 | private Animation animation; 85 | 86 | /** 87 | * Animation for the arrow when is moving anticlockwise. 88 | */ 89 | private Animation reverseAnimation; 90 | 91 | /** 92 | * If can pull now 93 | */ 94 | private boolean isCanPullToRefresh; 95 | 96 | private OnRefreshListener mOnRefreshListener; 97 | 98 | /** 99 | * If you pull down but not release, your finger move up, this time we should play a reverse Animation. 100 | */ 101 | private boolean isBack; 102 | 103 | private OnScrollListener mOnScrollListener; 104 | 105 | public PullToRefreshListView(Context context, AttributeSet attrs, int defStyle) { 106 | super(context, attrs, defStyle); 107 | initView(context); 108 | } 109 | 110 | public PullToRefreshListView(Context context, AttributeSet attrs) { 111 | super(context, attrs); 112 | initView(context); 113 | } 114 | 115 | public PullToRefreshListView(Context context) { 116 | super(context); 117 | initView(context); 118 | } 119 | 120 | @SuppressLint("InflateParams") 121 | private void initView(Context context) { 122 | LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 123 | 124 | mHeader = inflater.inflate(R.layout.pull_to_refresh_header, null); 125 | iv_arrow = (ImageView) mHeader.findViewById(R.id.iv_arrow); 126 | pb_refresh = (ProgressBar) mHeader.findViewById(R.id.pb_refresh); 127 | tv_title = (TextView) mHeader.findViewById(R.id.tv_title); 128 | tv_time = (TextView) mHeader.findViewById(R.id.tv_time); 129 | 130 | measureHeaderView(mHeader); 131 | 132 | mHeaderHeight = mHeader.getMeasuredHeight(); 133 | // To make header view above the window, so use -mHeaderHeight. 134 | mHeader.setPadding(0, -mHeaderHeight, 0, 0); 135 | 136 | mHeader.invalidate(); 137 | 138 | addHeaderView(mHeader); 139 | 140 | mState = State.ORIGNAL; 141 | 142 | super.setOnScrollListener(new OnScrollListener() { 143 | 144 | @Override 145 | public void onScrollStateChanged(AbsListView view, int scrollState) { 146 | // Avoid override when use setOnScrollListener 147 | if (mOnScrollListener != null) { 148 | mOnScrollListener.onScrollStateChanged(view, scrollState); 149 | } 150 | } 151 | 152 | @Override 153 | public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { 154 | 155 | if (mOnScrollListener != null) { 156 | mOnScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount); 157 | } 158 | 159 | if (firstVisibleItem == 0) { 160 | isCanPullToRefresh = true; 161 | } else { 162 | isCanPullToRefresh = false; 163 | } 164 | } 165 | }); 166 | 167 | animation = new RotateAnimation(-180, 0, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); 168 | animation.setDuration(250); 169 | animation.setFillAfter(true); 170 | animation.setInterpolator(new LinearInterpolator()); 171 | 172 | reverseAnimation = 173 | new RotateAnimation(0, -180, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); 174 | reverseAnimation.setDuration(200); 175 | reverseAnimation.setFillAfter(true); 176 | reverseAnimation.setInterpolator(new LinearInterpolator()); 177 | } 178 | 179 | /** 180 | * Measure the height of the view will be when showing. 181 | * 182 | * @param view View to measure. 183 | */ 184 | private void measureHeaderView(View view) { 185 | ViewGroup.LayoutParams lp = view.getLayoutParams(); 186 | if (lp == null) { 187 | lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); 188 | } 189 | 190 | int childMeasureWidth = ViewGroup.getChildMeasureSpec(0, 0, lp.width); 191 | int childMeasureHeight; 192 | if (lp.height > 0) { 193 | childMeasureHeight = MeasureSpec.makeMeasureSpec(lp.height, MeasureSpec.EXACTLY); 194 | } else { 195 | // Measure specification mode: The parent has not imposed any 196 | // constraint on the child. It can be whatever size it wants. 197 | childMeasureHeight = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); 198 | } 199 | view.measure(childMeasureWidth, childMeasureHeight); 200 | } 201 | 202 | @SuppressLint("ClickableViewAccessibility") 203 | @Override 204 | public boolean onTouchEvent(MotionEvent ev) { 205 | int y = (int) ev.getRawY(); 206 | int action = ev.getAction(); 207 | switch (action) { 208 | case MotionEvent.ACTION_DOWN: 209 | downPositionY = y; 210 | break; 211 | case MotionEvent.ACTION_MOVE: 212 | if (!isCanPullToRefresh) { 213 | break; 214 | } 215 | currentPositionY = y; 216 | 217 | pullDistance = (currentPositionY - downPositionY) / RATIO; 218 | 219 | if (mState == State.REFRESHING) { 220 | break; 221 | } else if (mState == State.ORIGNAL && pullDistance > 0) { 222 | mState = State.PULL_TO_REFRESH; 223 | changeState(); 224 | } else if (mState == State.PULL_TO_REFRESH && pullDistance > mHeaderHeight) { 225 | mState = State.RELEASE_TO_REFRESH; 226 | changeState(); 227 | } else if (mState == State.RELEASE_TO_REFRESH) { 228 | if (pullDistance < 0) { 229 | // invisible 230 | mState = State.ORIGNAL; 231 | changeState(); 232 | } else if (pullDistance < mHeaderHeight) { 233 | mState = State.PULL_TO_REFRESH; 234 | isBack = true; 235 | changeState(); 236 | } 237 | 238 | } 239 | 240 | if (mState != State.REFRESHING) { 241 | mHeader.setPadding(0, (int) (pullDistance - mHeaderHeight), 0, 0); 242 | } 243 | 244 | break; 245 | case MotionEvent.ACTION_UP: 246 | if (mState == State.REFRESHING) { 247 | break; 248 | } else if (mState == State.PULL_TO_REFRESH) { 249 | mState = State.ORIGNAL; 250 | } else if (mState == State.RELEASE_TO_REFRESH) { 251 | mState = State.REFRESHING; 252 | } else { 253 | break; 254 | } 255 | changeState(); 256 | break; 257 | 258 | default: 259 | break; 260 | } 261 | 262 | return super.onTouchEvent(ev); 263 | } 264 | 265 | /** 266 | * Change the state of header view when ListView in different state. 267 | */ 268 | private void changeState() { 269 | if (mState == State.ORIGNAL) { 270 | iv_arrow.setVisibility(View.VISIBLE); 271 | pb_refresh.setVisibility(View.GONE); 272 | tv_time.setVisibility(View.VISIBLE); 273 | tv_title.setVisibility(View.VISIBLE); 274 | iv_arrow.clearAnimation(); 275 | mHeader.setPadding(0, -mHeaderHeight, 0, 0); 276 | } else if (mState == State.PULL_TO_REFRESH) { 277 | iv_arrow.setVisibility(View.VISIBLE); 278 | pb_refresh.setVisibility(View.GONE); 279 | tv_title.setVisibility(View.VISIBLE); 280 | tv_time.setVisibility(View.VISIBLE); 281 | 282 | iv_arrow.clearAnimation(); 283 | tv_title.setText(getResources().getString(R.string.pull_refresh)); 284 | 285 | if (isBack) { 286 | // Come from release to refresh to pull to refresh 287 | iv_arrow.startAnimation(animation); 288 | isBack = false; 289 | } 290 | } else if (mState == State.RELEASE_TO_REFRESH) { 291 | iv_arrow.setVisibility(View.VISIBLE); 292 | pb_refresh.setVisibility(View.GONE); 293 | tv_title.setVisibility(View.VISIBLE); 294 | tv_time.setVisibility(View.VISIBLE); 295 | 296 | iv_arrow.clearAnimation(); 297 | tv_time.setText(getResources().getString(R.string.release_to_refresh)); 298 | 299 | iv_arrow.startAnimation(reverseAnimation); 300 | } else if (mState == State.REFRESHING) { 301 | iv_arrow.setVisibility(View.GONE); 302 | pb_refresh.setVisibility(View.VISIBLE); 303 | tv_title.setVisibility(View.VISIBLE); 304 | tv_time.setVisibility(View.VISIBLE); 305 | 306 | iv_arrow.clearAnimation(); 307 | tv_title.setText(getResources().getString(R.string.refreshing)); 308 | 309 | mHeader.setPadding(0, 0, 0, 0); 310 | 311 | if (mOnRefreshListener != null) { 312 | mOnRefreshListener.onRefresh(); 313 | } 314 | } 315 | 316 | } 317 | 318 | /** 319 | * When complete refresh data, you must use this method to hide the header view, if not the header view will be 320 | * shown all the time. 321 | */ 322 | @SuppressWarnings("deprecation") 323 | public void onRefreshComplete() { 324 | mState = State.ORIGNAL; 325 | changeState(); 326 | tv_time.setText(getResources().getString(R.string.update_time) + new Date().toLocaleString()); 327 | } 328 | 329 | @SuppressWarnings("deprecation") 330 | @Override 331 | public void setAdapter(ListAdapter adapter) { 332 | tv_time.setText(getResources().getString(R.string.update_time) + new Date().toLocaleString()); 333 | super.setAdapter(adapter); 334 | } 335 | 336 | @Override 337 | public void setOnScrollListener(OnScrollListener l) { 338 | this.mOnScrollListener = l; 339 | } 340 | 341 | /** 342 | * Set refresh data listener. 343 | * 344 | * @param listener OnRefreshListener 345 | */ 346 | public void setOnRefreshListener(OnRefreshListener listener) { 347 | this.mOnRefreshListener = listener; 348 | } 349 | 350 | /** 351 | * All the state of ListView 352 | */ 353 | public enum State { 354 | ORIGNAL, PULL_TO_REFRESH, REFRESHING, RELEASE_TO_REFRESH; 355 | } 356 | 357 | /** 358 | * Interface when refresh data. 359 | */ 360 | public interface OnRefreshListener { 361 | abstract void onRefresh(); 362 | } 363 | 364 | } 365 | -------------------------------------------------------------------------------- /PullToRefreshListViewDemo/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | PullToRefreshListViewDemo 4 | 5 | 6 | 7 | 8 | 9 | com.android.ide.eclipse.adt.ResourceManagerBuilder 10 | 11 | 12 | 13 | 14 | com.android.ide.eclipse.adt.PreCompilerBuilder 15 | 16 | 17 | 18 | 19 | org.eclipse.jdt.core.javabuilder 20 | 21 | 22 | 23 | 24 | com.android.ide.eclipse.adt.ApkBuilder 25 | 26 | 27 | 28 | 29 | 30 | com.android.ide.eclipse.adt.AndroidNature 31 | org.eclipse.jdt.core.javanature 32 | 33 | 34 | -------------------------------------------------------------------------------- /PullToRefreshListViewDemo/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 10 | 11 | 15 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /PullToRefreshListViewDemo/project.properties: -------------------------------------------------------------------------------- 1 | # This file is automatically generated by Android Tools. 2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED! 3 | # 4 | # This file must be checked in Version Control Systems. 5 | # 6 | # To customize properties used by the Ant build system edit 7 | # "ant.properties", and override values to adapt the script to your 8 | # project structure. 9 | # 10 | # To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): 11 | #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt 12 | 13 | # Project target. 14 | target=android-20 15 | android.library.reference.1=../PullToRefreshListView 16 | -------------------------------------------------------------------------------- /PullToRefreshListViewDemo/res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CharonChui/PullRefresh-SlidingMenu/81246fa6d8f8708f8ab53f13bc3b09f38ede24ee/PullToRefreshListViewDemo/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /PullToRefreshListViewDemo/res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CharonChui/PullRefresh-SlidingMenu/81246fa6d8f8708f8ab53f13bc3b09f38ede24ee/PullToRefreshListViewDemo/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /PullToRefreshListViewDemo/res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CharonChui/PullRefresh-SlidingMenu/81246fa6d8f8708f8ab53f13bc3b09f38ede24ee/PullToRefreshListViewDemo/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /PullToRefreshListViewDemo/res/drawable-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CharonChui/PullRefresh-SlidingMenu/81246fa6d8f8708f8ab53f13bc3b09f38ede24ee/PullToRefreshListViewDemo/res/drawable-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /PullToRefreshListViewDemo/res/layout/activity_loadmore.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /PullToRefreshListViewDemo/res/layout/activity_loadmoregrid.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 12 | 13 | 27 | 28 | 29 | 39 | 40 | 44 | 45 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /PullToRefreshListViewDemo/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 |