└── PullToRefresh ├── .classpath ├── .project ├── AndroidManifest.xml ├── bin ├── PullToRefresh.apk ├── classes.dex └── resources.ap_ ├── default.properties ├── proguard.cfg ├── res ├── drawable-hdpi │ ├── ic_pulltorefresh_arrow.png │ └── icon.png ├── drawable-ldpi │ ├── ic_pulltorefresh_arrow.png │ └── icon.png ├── drawable-mdpi │ ├── ic_pulltorefresh_arrow.png │ └── icon.png ├── drawable │ └── pull_to_refresh_header_background.xml ├── layout │ ├── main.xml │ └── pull_to_refresh_list.xml └── values │ └── strings.xml └── src └── com └── pullToRefresh ├── Main.java ├── NoRefreshListener.java ├── OnPullingAction.java ├── OnReleaseReady.java ├── PullToRefreshComponent.java ├── PullToRefreshListView.java ├── PullToRefreshState.java ├── PullingDownState.java ├── PullingUpState.java ├── RefreshListener.java ├── RefreshingState.java ├── ScrollingState.java └── utils └── Pixel.java /PullToRefresh/.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /PullToRefresh/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | PullToRefresh 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 | -------------------------------------------------------------------------------- /PullToRefresh/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /PullToRefresh/bin/PullToRefresh.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guillep/PullToRefresh/3b6be414370989ebecd3b8c3301d0e8f3af6c879/PullToRefresh/bin/PullToRefresh.apk -------------------------------------------------------------------------------- /PullToRefresh/bin/classes.dex: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guillep/PullToRefresh/3b6be414370989ebecd3b8c3301d0e8f3af6c879/PullToRefresh/bin/classes.dex -------------------------------------------------------------------------------- /PullToRefresh/bin/resources.ap_: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guillep/PullToRefresh/3b6be414370989ebecd3b8c3301d0e8f3af6c879/PullToRefresh/bin/resources.ap_ -------------------------------------------------------------------------------- /PullToRefresh/default.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 use, 7 | # "build.properties", and override values to adapt the script to your 8 | # project structure. 9 | 10 | # Project target. 11 | target=android-4 12 | -------------------------------------------------------------------------------- /PullToRefresh/proguard.cfg: -------------------------------------------------------------------------------- 1 | -optimizationpasses 5 2 | -dontusemixedcaseclassnames 3 | -dontskipnonpubliclibraryclasses 4 | -dontpreverify 5 | -verbose 6 | -optimizations !code/simplification/arithmetic,!field/*,!class/merging/* 7 | 8 | -keep public class * extends android.app.Activity 9 | -keep public class * extends android.app.Application 10 | -keep public class * extends android.app.Service 11 | -keep public class * extends android.content.BroadcastReceiver 12 | -keep public class * extends android.content.ContentProvider 13 | -keep public class * extends android.app.backup.BackupAgentHelper 14 | -keep public class * extends android.preference.Preference 15 | -keep public class com.android.vending.licensing.ILicensingService 16 | 17 | -keepclasseswithmembernames class * { 18 | native ; 19 | } 20 | 21 | -keepclasseswithmembernames class * { 22 | public (android.content.Context, android.util.AttributeSet); 23 | } 24 | 25 | -keepclasseswithmembernames class * { 26 | public (android.content.Context, android.util.AttributeSet, int); 27 | } 28 | 29 | -keepclassmembers enum * { 30 | public static **[] values(); 31 | public static ** valueOf(java.lang.String); 32 | } 33 | 34 | -keep class * implements android.os.Parcelable { 35 | public static final android.os.Parcelable$Creator *; 36 | } 37 | -------------------------------------------------------------------------------- /PullToRefresh/res/drawable-hdpi/ic_pulltorefresh_arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guillep/PullToRefresh/3b6be414370989ebecd3b8c3301d0e8f3af6c879/PullToRefresh/res/drawable-hdpi/ic_pulltorefresh_arrow.png -------------------------------------------------------------------------------- /PullToRefresh/res/drawable-hdpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guillep/PullToRefresh/3b6be414370989ebecd3b8c3301d0e8f3af6c879/PullToRefresh/res/drawable-hdpi/icon.png -------------------------------------------------------------------------------- /PullToRefresh/res/drawable-ldpi/ic_pulltorefresh_arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guillep/PullToRefresh/3b6be414370989ebecd3b8c3301d0e8f3af6c879/PullToRefresh/res/drawable-ldpi/ic_pulltorefresh_arrow.png -------------------------------------------------------------------------------- /PullToRefresh/res/drawable-ldpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guillep/PullToRefresh/3b6be414370989ebecd3b8c3301d0e8f3af6c879/PullToRefresh/res/drawable-ldpi/icon.png -------------------------------------------------------------------------------- /PullToRefresh/res/drawable-mdpi/ic_pulltorefresh_arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guillep/PullToRefresh/3b6be414370989ebecd3b8c3301d0e8f3af6c879/PullToRefresh/res/drawable-mdpi/ic_pulltorefresh_arrow.png -------------------------------------------------------------------------------- /PullToRefresh/res/drawable-mdpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guillep/PullToRefresh/3b6be414370989ebecd3b8c3301d0e8f3af6c879/PullToRefresh/res/drawable-mdpi/icon.png -------------------------------------------------------------------------------- /PullToRefresh/res/drawable/pull_to_refresh_header_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 16 | 18 | 22 | -------------------------------------------------------------------------------- /PullToRefresh/res/layout/main.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /PullToRefresh/res/layout/pull_to_refresh_list.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | 14 | 18 | 23 | 25 | 31 | 36 | 43 | 49 | 50 | 51 | 52 | 53 | 58 | 59 | 60 | 61 | 62 | 65 | 70 | 72 | 78 | 83 | 90 | 96 | 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /PullToRefresh/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | PullToRefresh 4 | Pull to refresh... 5 | Pull to refresh... 6 | Release to refresh... 7 | Refreshing... 8 | 9 | -------------------------------------------------------------------------------- /PullToRefresh/src/com/pullToRefresh/Main.java: -------------------------------------------------------------------------------- 1 | package com.pullToRefresh; 2 | 3 | import android.app.ListActivity; 4 | import android.os.Bundle; 5 | import android.os.Handler; 6 | import android.view.ViewGroup; 7 | import android.widget.ArrayAdapter; 8 | 9 | import com.pull.R; 10 | 11 | public class Main extends ListActivity { 12 | private PullToRefreshComponent pullToRefresh; 13 | private ArrayAdapter adapter; 14 | 15 | /** Called when the activity is first created. */ 16 | @Override 17 | public void onCreate(Bundle savedInstanceState) { 18 | super.onCreate(savedInstanceState); 19 | this.setContentView(R.layout.main); 20 | ViewGroup upperButton = (ViewGroup) this 21 | .findViewById(R.id.refresh_upper_button); 22 | ViewGroup lowerButton = (ViewGroup) this 23 | .findViewById(R.id.refresh_lower_button); 24 | this.adapter = new ArrayAdapter(this, 25 | android.R.layout.simple_list_item_1); 26 | this.getListView().setAdapter(this.adapter); 27 | 28 | this.pullToRefresh = new PullToRefreshComponent(upperButton, 29 | lowerButton, this.getListView(), new Handler()); 30 | this.pullToRefresh.setOnPullDownRefreshAction(new RefreshListener() { 31 | 32 | @Override 33 | public void refreshFinished() { 34 | Main.this.runOnUiThread(new Runnable() { 35 | 36 | @Override 37 | public void run() { 38 | Main.this.adapter.add("itemm from pull down"); 39 | } 40 | }); 41 | } 42 | 43 | @Override 44 | public void doRefresh() { 45 | try { 46 | Thread.sleep(1500); 47 | } catch (InterruptedException e) { 48 | e.printStackTrace(); 49 | } 50 | } 51 | }); 52 | 53 | this.pullToRefresh.setOnPullUpRefreshAction(new RefreshListener() { 54 | 55 | @Override 56 | public void refreshFinished() { 57 | Main.this.runOnUiThread(new Runnable() { 58 | 59 | @Override 60 | public void run() { 61 | Main.this.adapter.add("itemm from the up"); 62 | } 63 | }); 64 | } 65 | 66 | @Override 67 | public void doRefresh() { 68 | try { 69 | Thread.sleep(1500); 70 | } catch (InterruptedException e) { 71 | e.printStackTrace(); 72 | } 73 | } 74 | }); 75 | } 76 | } -------------------------------------------------------------------------------- /PullToRefresh/src/com/pullToRefresh/NoRefreshListener.java: -------------------------------------------------------------------------------- 1 | package com.pullToRefresh; 2 | 3 | public class NoRefreshListener implements RefreshListener { 4 | 5 | @Override 6 | public void doRefresh() { 7 | } 8 | 9 | @Override 10 | public void refreshFinished() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /PullToRefresh/src/com/pullToRefresh/OnPullingAction.java: -------------------------------------------------------------------------------- 1 | package com.pullToRefresh; 2 | 3 | public interface OnPullingAction { 4 | 5 | void handlePull(boolean down, int height); 6 | 7 | } 8 | -------------------------------------------------------------------------------- /PullToRefresh/src/com/pullToRefresh/OnReleaseReady.java: -------------------------------------------------------------------------------- 1 | package com.pullToRefresh; 2 | 3 | public interface OnReleaseReady { 4 | 5 | void releaseReady(boolean ready); 6 | 7 | } 8 | -------------------------------------------------------------------------------- /PullToRefresh/src/com/pullToRefresh/PullToRefreshComponent.java: -------------------------------------------------------------------------------- 1 | package com.pullToRefresh; 2 | 3 | import java.util.Date; 4 | 5 | import android.os.Handler; 6 | import android.util.Log; 7 | import android.view.MotionEvent; 8 | import android.view.View; 9 | import android.view.View.OnTouchListener; 10 | import android.widget.ListView; 11 | 12 | import com.pullToRefresh.utils.Pixel; 13 | 14 | public class PullToRefreshComponent { 15 | 16 | private static final int EVENT_COUNT = 3; 17 | protected static final float MIN_PULL_ELEMENT_HEIGHT = 100; 18 | protected static final float MAX_PULL_ELEMENT_HEIGHT = 200; 19 | private static final float PULL_ELEMENT_STANDBY_HEIGHT = 100; 20 | protected static int firstVisibleItem = 0; 21 | 22 | private View upperView; 23 | private View lowerView; 24 | private ListView listView; 25 | private Handler uiThreadHandler; 26 | 27 | private float[] lastYs = new float[EVENT_COUNT]; 28 | 29 | private RefreshListener onPullDownRefreshAction = new NoRefreshListener(); 30 | private RefreshListener onPullUpRefreshAction = new NoRefreshListener(); 31 | 32 | protected ScrollingState state; 33 | private boolean mayPullUpToRefresh = true; 34 | private boolean mayPullDownToRefresh; 35 | private OnReleaseReady onReleaseLowerReady; 36 | private OnReleaseReady onReleaseUpperReady; 37 | 38 | public PullToRefreshComponent(View upperView, View lowerView, 39 | ListView listView, Handler uiThreadHandler) { 40 | this.upperView = upperView; 41 | this.lowerView = lowerView; 42 | this.listView = listView; 43 | this.uiThreadHandler = uiThreadHandler; 44 | this.initialize(); 45 | } 46 | 47 | private void initialize() { 48 | this.state = new PullToRefreshState(); 49 | this.listView.setOnTouchListener(new OnTouchListener() { 50 | 51 | @Override 52 | public boolean onTouch(View v, MotionEvent event) { 53 | if (event.getAction() == MotionEvent.ACTION_UP) { 54 | PullToRefreshComponent.this.initializeYsHistory(); 55 | return PullToRefreshComponent.this.state.touchStopped( 56 | event, PullToRefreshComponent.this); 57 | } else if (event.getAction() == MotionEvent.ACTION_MOVE) { 58 | return PullToRefreshComponent.this.state.handleMovement( 59 | event, PullToRefreshComponent.this); 60 | } 61 | return false; 62 | } 63 | }); 64 | } 65 | 66 | protected float average(float[] ysArray) { 67 | float avg = 0; 68 | for (int i = 0; i < EVENT_COUNT; i++) { 69 | avg += ysArray[i]; 70 | } 71 | return avg / EVENT_COUNT; 72 | } 73 | 74 | public void beginPullDownRefresh() { 75 | this.beginRefresh(this.upperView, this.onPullDownRefreshAction); 76 | } 77 | 78 | private void beginRefresh(View viewToUpdate, 79 | final RefreshListener refreshAction) { 80 | android.view.ViewGroup.LayoutParams params = viewToUpdate 81 | .getLayoutParams(); 82 | params.height = (int) PULL_ELEMENT_STANDBY_HEIGHT; 83 | viewToUpdate.setLayoutParams(params); 84 | Log.i("PullDown", "refreshing"); 85 | this.state = new RefreshingState(); 86 | new Thread(new Runnable() { 87 | @Override 88 | public void run() { 89 | try { 90 | Date start = new Date(); 91 | refreshAction.doRefresh(); 92 | Date finish = new Date(); 93 | long difference = finish.getTime() - start.getTime(); 94 | try { 95 | Thread.sleep(Math.max(difference, 1500)); 96 | } catch (InterruptedException e) { 97 | } 98 | } catch (RuntimeException e) { 99 | Log.e("Error", e.getMessage(), e); 100 | throw e; 101 | } finally { 102 | PullToRefreshComponent.this.runOnUiThread(new Runnable() { 103 | @Override 104 | public void run() { 105 | PullToRefreshComponent.this 106 | .refreshFinished(refreshAction); 107 | } 108 | }); 109 | } 110 | } 111 | }).start(); 112 | } 113 | 114 | public void beginPullUpRefresh() { 115 | this.beginRefresh(this.lowerView, this.onPullUpRefreshAction); 116 | } 117 | 118 | /**************************************************************/ 119 | // Listeners 120 | /**************************************************************/ 121 | 122 | public void setOnPullDownRefreshAction(RefreshListener onRefreshAction) { 123 | this.enablePullDownToRefresh(); 124 | this.onPullDownRefreshAction = onRefreshAction; 125 | } 126 | 127 | public void setOnPullUpRefreshAction(RefreshListener onRefreshAction) { 128 | this.enablePullUpToRefresh(); 129 | this.onPullUpRefreshAction = onRefreshAction; 130 | } 131 | 132 | protected void refreshFinished(final RefreshListener refreshAction) { 133 | Log.i("PullDown", "ready"); 134 | this.state = new PullToRefreshState(); 135 | this.initializeYsHistory(); 136 | this.runOnUiThread(new Runnable() { 137 | @Override 138 | public void run() { 139 | float dp = new Pixel(0, PullToRefreshComponent.this.listView 140 | .getResources()).toDp(); 141 | PullToRefreshComponent.this.setUpperButtonHeight(dp); 142 | PullToRefreshComponent.this.setLowerButtonHeight(dp); 143 | refreshAction.refreshFinished(); 144 | } 145 | }); 146 | 147 | } 148 | 149 | private void runOnUiThread(Runnable runnable) { 150 | this.uiThreadHandler.post(runnable); 151 | } 152 | 153 | synchronized void setUpperButtonHeight(float height) { 154 | this.setHeight(height, this.upperView); 155 | } 156 | 157 | synchronized void setLowerButtonHeight(float height) { 158 | this.setHeight(height, this.lowerView); 159 | } 160 | 161 | private void setHeight(float height, View view) { 162 | if (view == null) { 163 | return; 164 | } 165 | android.view.ViewGroup.LayoutParams params = view.getLayoutParams(); 166 | if (params == null) { 167 | return; 168 | } 169 | params.height = (int) height; 170 | view.setLayoutParams(params); 171 | view.getParent().requestLayout(); 172 | } 173 | 174 | public int getListTop() { 175 | return this.listView.getTop(); 176 | } 177 | 178 | public void initializeYsHistory() { 179 | for (int i = 0; i < EVENT_COUNT; i++) { 180 | PullToRefreshComponent.this.lastYs[i] = 0; 181 | } 182 | } 183 | 184 | /**************************************************************/ 185 | // HANDLE PULLING 186 | /**************************************************************/ 187 | 188 | public void pullDown(MotionEvent event, float firstY) { 189 | float averageY = PullToRefreshComponent.this 190 | .average(PullToRefreshComponent.this.lastYs); 191 | 192 | int height = (int) Math.max( 193 | Math.min(averageY - firstY, MAX_PULL_ELEMENT_HEIGHT), 0); 194 | PullToRefreshComponent.this.setUpperButtonHeight(height); 195 | } 196 | 197 | public void pullUp(MotionEvent event, float firstY) { 198 | float averageY = PullToRefreshComponent.this 199 | 200 | .average(PullToRefreshComponent.this.lastYs); 201 | 202 | int height = (int) Math.max( 203 | Math.min(firstY - averageY, MAX_PULL_ELEMENT_HEIGHT), 0); 204 | PullToRefreshComponent.this.setLowerButtonHeight(height); 205 | } 206 | 207 | public boolean isPullingDownToRefresh() { 208 | return this.mayPullDownToRefresh && this.isIncremental() 209 | && this.isFirstVisible(); 210 | } 211 | 212 | public boolean isPullingUpToRefresh() { 213 | return this.mayPullUpToRefresh && this.isDecremental() 214 | && this.isLastVisible(); 215 | } 216 | 217 | private boolean isFirstVisible() { 218 | if (this.listView.getCount() == 0) { 219 | return true; 220 | } else if (this.listView.getFirstVisiblePosition() == 0) { 221 | return this.listView.getChildAt(0).getTop() >= this.listView 222 | .getTop(); 223 | } else { 224 | return false; 225 | } 226 | } 227 | 228 | private boolean isLastVisible() { 229 | if (this.listView.getCount() == 0) { 230 | return true; 231 | } else if (this.listView.getLastVisiblePosition() + 1 == this.listView 232 | .getCount()) { 233 | return this.listView.getChildAt(this.listView.getChildCount() - 1) 234 | .getBottom() <= this.listView.getBottom(); 235 | } else { 236 | return false; 237 | } 238 | } 239 | 240 | private boolean isIncremental(int from, int to, int step) { 241 | float realFrom = this.lastYs[from]; 242 | float realTo = this.lastYs[to]; 243 | Log.i("pull to refresh", "scrolling from " + String.valueOf(realFrom)); 244 | Log.i("pull to refresh", "scrolling to " + String.valueOf(realTo)); 245 | return this.lastYs[from] != 0 && realTo != 0 246 | && Math.abs(realFrom - realTo) > 50 && realFrom * step < realTo; 247 | } 248 | 249 | private boolean isIncremental() { 250 | return this.isIncremental(0, EVENT_COUNT - 1, +1); 251 | } 252 | 253 | private boolean isDecremental() { 254 | return this.isIncremental(0, EVENT_COUNT - 1, -1); 255 | } 256 | 257 | public void updateEventStates(MotionEvent event) { 258 | for (int i = 0; i < EVENT_COUNT - 1; i++) { 259 | PullToRefreshComponent.this.lastYs[i] = PullToRefreshComponent.this.lastYs[i + 1]; 260 | } 261 | 262 | float y = event.getY(); 263 | int top = PullToRefreshComponent.this.listView.getTop(); 264 | Log.i("Pulltorefresh", "event y:" + String.valueOf(y)); 265 | Log.i("Pulltorefresh", "list top:" + String.valueOf(top)); 266 | PullToRefreshComponent.this.lastYs[EVENT_COUNT - 1] = y + top; 267 | } 268 | 269 | /**************************************************************/ 270 | // State Change 271 | /**************************************************************/ 272 | 273 | public void setPullingDown(MotionEvent event) { 274 | Log.i("PullDown", "pulling down"); 275 | this.state = new PullingDownState(event); 276 | } 277 | 278 | public void setPullingUp(MotionEvent event) { 279 | Log.i("PullDown", "pulling up"); 280 | this.state = new PullingUpState(event); 281 | } 282 | 283 | public float getBottomViewHeight() { 284 | return this.lowerView.getHeight(); 285 | } 286 | 287 | public RefreshListener getOnUpperRefreshAction() { 288 | return this.onPullDownRefreshAction; 289 | } 290 | 291 | public RefreshListener getOnLowerRefreshAction() { 292 | return this.onPullUpRefreshAction; 293 | } 294 | 295 | public void disablePullUpToRefresh() { 296 | this.mayPullUpToRefresh = false; 297 | } 298 | 299 | public void disablePullDownToRefresh() { 300 | this.mayPullDownToRefresh = false; 301 | } 302 | 303 | public void enablePullUpToRefresh() { 304 | this.mayPullUpToRefresh = true; 305 | } 306 | 307 | public void enablePullDownToRefresh() { 308 | this.mayPullDownToRefresh = true; 309 | } 310 | 311 | public void setOnReleaseUpperReady(OnReleaseReady onReleaseUpperReady) { 312 | this.onReleaseUpperReady = onReleaseUpperReady; 313 | } 314 | 315 | public void setOnReleaseLowerReady(OnReleaseReady onReleaseUpperReady) { 316 | this.onReleaseLowerReady = onReleaseUpperReady; 317 | } 318 | 319 | public void readyToReleaseUpper(boolean ready) { 320 | if (this.onReleaseUpperReady != null) { 321 | this.onReleaseUpperReady.releaseReady(ready); 322 | } 323 | } 324 | 325 | public void readyToReleaseLower(boolean ready) { 326 | if (this.onReleaseLowerReady != null) { 327 | this.onReleaseLowerReady.releaseReady(ready); 328 | } 329 | } 330 | } 331 | -------------------------------------------------------------------------------- /PullToRefresh/src/com/pullToRefresh/PullToRefreshListView.java: -------------------------------------------------------------------------------- 1 | package com.pullToRefresh; 2 | 3 | import android.content.Context; 4 | import android.os.Handler; 5 | import android.util.AttributeSet; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | import android.view.ViewGroup; 9 | import android.widget.ListView; 10 | import android.widget.RelativeLayout; 11 | import android.widget.TextView; 12 | 13 | import com.pull.R; 14 | 15 | public class PullToRefreshListView extends RelativeLayout { 16 | 17 | private PullToRefreshComponent pullToRefresh; 18 | private ViewGroup upperButton; 19 | private ViewGroup lowerButton; 20 | private Handler uiThreadHandler; 21 | private ListView listView; 22 | 23 | public PullToRefreshListView(Context context, AttributeSet attrs) { 24 | super(context, attrs); 25 | LayoutInflater inflater = (LayoutInflater) context 26 | .getSystemService(Context.LAYOUT_INFLATER_SERVICE); 27 | inflater.inflate(R.layout.pull_to_refresh_list, this); 28 | this.listView = (ListView) this.findViewById(android.R.id.list); 29 | this.uiThreadHandler = new Handler(); 30 | this.initializePullToRefreshList(); 31 | } 32 | 33 | private void initializePullToRefreshList() { 34 | this.initializeRefreshUpperButton(); 35 | this.initializeRefreshLowerButton(); 36 | this.pullToRefresh = new com.pullToRefresh.PullToRefreshComponent( 37 | this.upperButton, this.lowerButton, this.listView, 38 | this.uiThreadHandler); 39 | 40 | this.pullToRefresh.setOnReleaseUpperReady(new OnReleaseReady() { 41 | @Override 42 | public void releaseReady(boolean ready) { 43 | if (ready) { 44 | ((TextView) PullToRefreshListView.this.upperButton 45 | .findViewById(R.id.pull_to_refresh_text)) 46 | .setText(R.string.pull_to_refresh_release_label); 47 | } else { 48 | ((TextView) PullToRefreshListView.this.upperButton 49 | .findViewById(R.id.pull_to_refresh_text)) 50 | .setText(R.string.pull_to_refresh_pull_down_label); 51 | } 52 | } 53 | }); 54 | 55 | this.pullToRefresh.setOnReleaseLowerReady(new OnReleaseReady() { 56 | @Override 57 | public void releaseReady(boolean ready) { 58 | if (ready) { 59 | ((TextView) PullToRefreshListView.this.lowerButton 60 | .findViewById(R.id.pull_to_refresh_text)) 61 | .setText(R.string.pull_to_refresh_release_label); 62 | } else { 63 | ((TextView) PullToRefreshListView.this.lowerButton 64 | .findViewById(R.id.pull_to_refresh_text)) 65 | .setText(R.string.pull_to_refresh_pull_up_label); 66 | } 67 | } 68 | }); 69 | } 70 | 71 | public void setOnPullDownRefreshAction(final RefreshListener listener) { 72 | this.pullToRefresh.setOnPullDownRefreshAction(new RefreshListener() { 73 | 74 | @Override 75 | public void refreshFinished() { 76 | PullToRefreshListView.this.runOnUIThread(new Runnable() { 77 | 78 | @Override 79 | public void run() { 80 | PullToRefreshListView.this 81 | .setPullToRefresh(PullToRefreshListView.this.upperButton); 82 | PullToRefreshListView.this.invalidate(); 83 | } 84 | 85 | }); 86 | PullToRefreshListView.this.runOnUIThread(new Runnable() { 87 | 88 | @Override 89 | public void run() { 90 | listener.refreshFinished(); 91 | } 92 | }); 93 | } 94 | 95 | @Override 96 | public void doRefresh() { 97 | PullToRefreshListView.this.uiThreadHandler.post(new Runnable() { 98 | @Override 99 | public void run() { 100 | PullToRefreshListView.this 101 | .setRefreshing(PullToRefreshListView.this.upperButton); 102 | PullToRefreshListView.this.invalidate(); 103 | } 104 | }); 105 | listener.doRefresh(); 106 | } 107 | }); 108 | } 109 | 110 | protected void runOnUIThread(Runnable runnable) { 111 | this.uiThreadHandler.post(runnable); 112 | } 113 | 114 | public void setOnPullUpRefreshAction(final RefreshListener listener) { 115 | this.pullToRefresh.setOnPullUpRefreshAction(new RefreshListener() { 116 | 117 | @Override 118 | public void refreshFinished() { 119 | PullToRefreshListView.this 120 | .setPullToRefresh(PullToRefreshListView.this.lowerButton); 121 | PullToRefreshListView.this.invalidate(); 122 | listener.refreshFinished(); 123 | } 124 | 125 | @Override 126 | public void doRefresh() { 127 | PullToRefreshListView.this.uiThreadHandler.post(new Runnable() { 128 | @Override 129 | public void run() { 130 | PullToRefreshListView.this 131 | .setRefreshing(PullToRefreshListView.this.lowerButton); 132 | PullToRefreshListView.this.invalidate(); 133 | } 134 | }); 135 | listener.doRefresh(); 136 | } 137 | }); 138 | } 139 | 140 | private void initializeRefreshLowerButton() { 141 | this.upperButton = (ViewGroup) this 142 | .findViewById(R.id.refresh_upper_button); 143 | } 144 | 145 | private void initializeRefreshUpperButton() { 146 | this.lowerButton = (ViewGroup) this 147 | .findViewById(R.id.refresh_lower_button); 148 | } 149 | 150 | protected void setPullToRefresh(ViewGroup refreshView) { 151 | int text = 0; 152 | if (refreshView == this.upperButton) { 153 | text = R.string.pull_to_refresh_pull_down_label; 154 | } else { 155 | text = R.string.pull_to_refresh_pull_up_label; 156 | } 157 | ((TextView) refreshView.findViewById(R.id.pull_to_refresh_text)) 158 | .setText(text); 159 | refreshView.findViewById(R.id.pull_to_refresh_progress).setVisibility( 160 | View.INVISIBLE); 161 | refreshView.findViewById(R.id.pull_to_refresh_image).setVisibility( 162 | View.VISIBLE); 163 | } 164 | 165 | protected void setRefreshing(ViewGroup refreshView) { 166 | ((TextView) refreshView.findViewById(R.id.pull_to_refresh_text)) 167 | .setText(R.string.pull_to_refresh_refreshing_label); 168 | refreshView.findViewById(R.id.pull_to_refresh_progress).setVisibility( 169 | View.VISIBLE); 170 | refreshView.findViewById(R.id.pull_to_refresh_image).setVisibility( 171 | View.INVISIBLE); 172 | } 173 | 174 | public void disablePullUpToRefresh() { 175 | this.pullToRefresh.disablePullUpToRefresh(); 176 | } 177 | 178 | public void disablePullDownToRefresh() { 179 | this.pullToRefresh.disablePullDownToRefresh(); 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /PullToRefresh/src/com/pullToRefresh/PullToRefreshState.java: -------------------------------------------------------------------------------- 1 | package com.pullToRefresh; 2 | 3 | import android.view.MotionEvent; 4 | 5 | public class PullToRefreshState implements ScrollingState { 6 | 7 | @Override 8 | public boolean touchStopped(MotionEvent event, 9 | PullToRefreshComponent onTouchListener) { 10 | return false; 11 | } 12 | 13 | @Override 14 | public boolean handleMovement(MotionEvent event, 15 | PullToRefreshComponent pullToRefreshComponent) { 16 | pullToRefreshComponent.updateEventStates(event); 17 | if (pullToRefreshComponent.isPullingDownToRefresh()) { 18 | pullToRefreshComponent.setPullingDown(event); 19 | return true; 20 | } else if (pullToRefreshComponent.isPullingUpToRefresh()) { 21 | pullToRefreshComponent.setPullingUp(event); 22 | return true; 23 | } 24 | return false; 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /PullToRefresh/src/com/pullToRefresh/PullingDownState.java: -------------------------------------------------------------------------------- 1 | package com.pullToRefresh; 2 | 3 | import android.view.MotionEvent; 4 | 5 | public class PullingDownState implements ScrollingState { 6 | 7 | private float firstY; 8 | 9 | public PullingDownState(MotionEvent event) { 10 | this.firstY = event.getY(); 11 | } 12 | 13 | @Override 14 | public boolean touchStopped(MotionEvent event, 15 | PullToRefreshComponent pullToRefreshComponent) { 16 | if (pullToRefreshComponent.getListTop() > PullToRefreshComponent.MIN_PULL_ELEMENT_HEIGHT) { 17 | pullToRefreshComponent.beginPullDownRefresh(); 18 | } else { 19 | pullToRefreshComponent.refreshFinished(pullToRefreshComponent 20 | .getOnUpperRefreshAction()); 21 | } 22 | return true; 23 | } 24 | 25 | @Override 26 | public boolean handleMovement(MotionEvent event, 27 | PullToRefreshComponent pullToRefreshComponent) { 28 | pullToRefreshComponent.updateEventStates(event); 29 | pullToRefreshComponent.pullDown(event, this.firstY); 30 | pullToRefreshComponent.readyToReleaseUpper(pullToRefreshComponent 31 | .getListTop() > PullToRefreshComponent.MIN_PULL_ELEMENT_HEIGHT); 32 | return true; 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /PullToRefresh/src/com/pullToRefresh/PullingUpState.java: -------------------------------------------------------------------------------- 1 | package com.pullToRefresh; 2 | 3 | import android.view.MotionEvent; 4 | 5 | public class PullingUpState implements ScrollingState { 6 | 7 | private float firstY; 8 | 9 | public PullingUpState(MotionEvent event) { 10 | this.firstY = event.getY(); 11 | } 12 | 13 | @Override 14 | public boolean touchStopped(MotionEvent event, 15 | PullToRefreshComponent pullToRefreshComponent) { 16 | if (pullToRefreshComponent.getBottomViewHeight() > PullToRefreshComponent.MIN_PULL_ELEMENT_HEIGHT) { 17 | pullToRefreshComponent.beginPullUpRefresh(); 18 | } else { 19 | pullToRefreshComponent.refreshFinished(pullToRefreshComponent 20 | .getOnLowerRefreshAction()); 21 | } 22 | return true; 23 | } 24 | 25 | @Override 26 | public boolean handleMovement(MotionEvent event, 27 | PullToRefreshComponent pullToRefreshComponent) { 28 | pullToRefreshComponent.updateEventStates(event); 29 | pullToRefreshComponent.pullUp(event, this.firstY); 30 | pullToRefreshComponent 31 | .readyToReleaseLower(pullToRefreshComponent 32 | .getBottomViewHeight() > PullToRefreshComponent.MIN_PULL_ELEMENT_HEIGHT); 33 | return true; 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /PullToRefresh/src/com/pullToRefresh/RefreshListener.java: -------------------------------------------------------------------------------- 1 | package com.pullToRefresh; 2 | 3 | public interface RefreshListener { 4 | 5 | void doRefresh(); 6 | 7 | void refreshFinished(); 8 | 9 | } 10 | -------------------------------------------------------------------------------- /PullToRefresh/src/com/pullToRefresh/RefreshingState.java: -------------------------------------------------------------------------------- 1 | package com.pullToRefresh; 2 | 3 | import android.view.MotionEvent; 4 | 5 | public class RefreshingState implements ScrollingState { 6 | 7 | @Override 8 | public boolean touchStopped(MotionEvent event, 9 | PullToRefreshComponent pullToRefreshComponent) { 10 | return true; 11 | } 12 | 13 | @Override 14 | public boolean handleMovement(MotionEvent event, 15 | PullToRefreshComponent pullToRefreshComponent) { 16 | return true; 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /PullToRefresh/src/com/pullToRefresh/ScrollingState.java: -------------------------------------------------------------------------------- 1 | package com.pullToRefresh; 2 | 3 | import android.view.MotionEvent; 4 | 5 | public interface ScrollingState { 6 | 7 | boolean touchStopped(MotionEvent event, 8 | PullToRefreshComponent pullToRefreshComponent); 9 | 10 | boolean handleMovement(MotionEvent event, 11 | PullToRefreshComponent pullToRefreshComponent); 12 | 13 | } 14 | -------------------------------------------------------------------------------- /PullToRefresh/src/com/pullToRefresh/utils/Pixel.java: -------------------------------------------------------------------------------- 1 | package com.pullToRefresh.utils; 2 | 3 | import android.content.res.Resources; 4 | 5 | public class Pixel { 6 | 7 | private Resources resources; 8 | private int value; 9 | 10 | public Pixel(int value, Resources resources) { 11 | this.value = value; 12 | this.resources = resources; 13 | } 14 | 15 | public float toDp() { 16 | return this.value * this.resources.getDisplayMetrics().density + 0.5f; 17 | } 18 | } 19 | --------------------------------------------------------------------------------