“Pull To Refresh” is a UI gesture made popular by the Twitter for Iphone app. It allows a user to refresh a list by pulling down and releasing from the top of the list. IOS developers have had library support for this feature for some time now. Over in Android-land, only the official Twitter app has managed to implement this gesture. Long-promised to be open-sourced, Twitter’s solution is still secret and Android developers have no official means to implement this increasingly demanded feature.
Its not my own library. I don't know who is written this library. But i think it will help u more than when compared to other libraries. Here they implemented via frame layout and a list view. dragging and releasing part has done inside the frame layout. Check an example below.
In xml file ....
package com.example.helpers;
import android.content.Context;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.LinearInterpolator;
import android.view.animation.RotateAnimation;
import android.widget.AdapterView;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.Scroller;
import android.widget.TextView;
import com.example.R;
public class PullToRefresh extends FrameLayout implements
GestureDetector.OnGestureListener {
public static final int STATE_CLOSE = 1;
public static final int STATE_OPEN = 2;
public static final int STATE_OPEN_MAX = 4;
public static final int STATE_OPEN_MAX_RELEASE = 5;
public static final int STATE_OPEN_RELEASE = 3;
public static final int STATE_UPDATE = 6;
public static final int STATE_UPDATE_SCROLL = 7;
private final int MAXHEIGHT = 100;
// private final String TAG = "PullToRefresh";
private ImageView mArrow;
private String mDate;
private GestureDetector mDetector;
private Flinger mFlinger;
private boolean mIsAutoScroller;
private int mPading;
private ProgressBar mProgressBar;
private int mState;
private TextView mTitle;
private FrameLayout mUpdateContent;
private UpdateHandle mUpdateHandle;
private RotateAnimation mFlipAnimation;
private RotateAnimation mReverseFlipAnimation;
private Context ctx;
public PullToRefresh(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
this.ctx = context;
addUpdateBar();
init();
}
public PullToRefresh(Context context, AttributeSet attrs) {
super(context, attrs);
this.ctx = context;
addUpdateBar();
init();
}
public PullToRefresh(Context context) {
super(context);
this.ctx = context;
addUpdateBar();
init();
}
// private void init() {
// MAX_LENGHT = getResources().getDimensionPixelSize(
// R.dimen.updatebar_height);
// setDrawingCacheEnabled(true);
// // setBackgroundDrawable(null);
// setClipChildren(true);
// this.mDetector.setIsLongpressEnabled(false);
// }
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
boolean bool1 = this.mIsAutoScroller;
GestureDetector localGestureDetector = this.mDetector;
localGestureDetector.onTouchEvent(ev);
switch (ev.getAction()) {
case MotionEvent.ACTION_MOVE:
int i1 = getChildAt(1).getTop();
if (i1 != 0) {
updateView();
}
break;
case MotionEvent.ACTION_UP:
if (this.mState == STATE_OPEN) {
this.mState = STATE_OPEN_RELEASE;
}
if (this.mState == STATE_OPEN_MAX) {
this.mState = STATE_OPEN_MAX_RELEASE;
}
release();
break;
}
if (mState != STATE_UPDATE) {
bool1 = super.dispatchTouchEvent(ev);
}
int i1 = getChildAt(1).getTop();
if (i1 != 0) {
ev.setAction(3);
super.dispatchTouchEvent(ev);
updateView();
}
return bool1;
}
private void init() {
GestureDetector localGestureDetector = new GestureDetector(this);
this.mDetector = localGestureDetector;
Flinger localFlinger = new Flinger();
this.mFlinger = localFlinger;
this.mState = 1;
setDrawingCacheEnabled(true);
setClipChildren(true);
this.mDetector.setIsLongpressEnabled(false);
}
private void updateView() {
View localView1 = getChildAt(0);
View localView2 = getChildAt(1);
if (this.mDate == null)
this.mDate = "";
switch (this.mState) {
case STATE_CLOSE:
if (localView1.getVisibility() != View.INVISIBLE)
localView1.setVisibility(View.INVISIBLE);
case STATE_OPEN:
case STATE_OPEN_RELEASE:
// STATE_OPEN
int m = localView2.getTop();
int n = -this.mPading - m;
localView2.offsetTopAndBottom(n);
if (localView1.getVisibility() != 0)
localView1.setVisibility(View.VISIBLE);
int i1 = localView1.getTop();// 相对于父窗�的顶部大�
int i2 = -MAXHEIGHT;
int i3 = this.mPading;
int i4 = i2 - i3 - i1;
localView1.offsetTopAndBottom(i4);
TextView localTextView1 = this.mTitle;
String str1 = ctx.getText(R.string.update_when_release_the_finger).toString();
StringBuilder localStringBuilder1 = new StringBuilder(str1)
.append("\n");
// String str2 = this.mDate;
// String str3 = str2;
localStringBuilder1.append(this.mDate);
localTextView1.setText(localStringBuilder1.toString());
this.mProgressBar.setVisibility(View.INVISIBLE);
this.mArrow.setVisibility(View.VISIBLE);
break;
case STATE_OPEN_MAX_RELEASE:
case STATE_OPEN_MAX:
// STATE_OPEN_MAX
int i5 = localView2.getTop();
int i6 = -this.mPading - i5;
localView2.offsetTopAndBottom(i6);
if (localView1.getVisibility() != View.VISIBLE)
localView1.setVisibility(View.VISIBLE);
int i7 = localView1.getTop();
int i8 = -MAXHEIGHT;
int i9 = this.mPading;
int i10 = i8 - i9 - i7;
localView1.offsetTopAndBottom(i10);
TextView localTextView2 = this.mTitle;
String str4 = ctx.getText(R.string.update_when_release_the_finger).toString();// release_update:
StringBuilder localStringBuilder2 = new StringBuilder(str4).append("\n");
localStringBuilder2.append(this.mDate);
localTextView2.setText(localStringBuilder2.toString());
this.mProgressBar.setVisibility(View.INVISIBLE);
this.mArrow.setVisibility(View.VISIBLE);
break;
case STATE_UPDATE:
// STATE_UPDATE
int i11 = localView2.getTop();
int i12 = -this.mPading - i11;
localView2.offsetTopAndBottom(i12);
int i13 = localView1.getTop();
if (this.mProgressBar.getVisibility() != View.VISIBLE)
this.mProgressBar.setVisibility(View.VISIBLE);
if (this.mArrow.getVisibility() != View.INVISIBLE)
this.mArrow.setVisibility(View.INVISIBLE);
TextView localTextView3 = this.mTitle;
String str7 = ctx.getString(R.string.updating).toString();// doing_update:åŠ è½½ä¸...
StringBuilder localStringBuilder3 = new StringBuilder(str7)
.append("\n");
localStringBuilder3.append(this.mDate);
localTextView3.setText(localStringBuilder3.toString());
int i14 = -MAXHEIGHT;
int i15 = this.mPading;
int i16 = i14 - i15 - i13;
localView1.offsetTopAndBottom(i16);
if (localView1.getVisibility() != 0)
localView1.setVisibility(0);
this.mProgressBar.setVisibility(View.VISIBLE);
this.mArrow.setVisibility(View.GONE);
mArrow.clearAnimation();
break;
}
invalidate();
}
private void addUpdateBar() {
Context localContext1 = getContext();
mFlipAnimation = new RotateAnimation(0, -180,
Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f);
mFlipAnimation.setInterpolator(new LinearInterpolator());
mFlipAnimation.setDuration(200);
mFlipAnimation.setFillAfter(true);
mReverseFlipAnimation = new RotateAnimation(-180, 0,
Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f);
mReverseFlipAnimation.setInterpolator(new LinearInterpolator());
mReverseFlipAnimation.setDuration(200);
mReverseFlipAnimation.setFillAfter(true);
View localView = LayoutInflater.from(localContext1).inflate(
R.layout.vw_update_bar, null);
localView.setVisibility(4);
addView(localView);
this.mArrow = new ImageView(localContext1);
FrameLayout.LayoutParams localLayoutParams1 = new FrameLayout.LayoutParams(
android.view.ViewGroup.LayoutParams.FILL_PARENT,
android.view.ViewGroup.LayoutParams.FILL_PARENT);
ImageView localImageView2 = this.mArrow;
ImageView.ScaleType localScaleType = ImageView.ScaleType.FIT_CENTER;
localImageView2.setScaleType(localScaleType);
this.mArrow.setLayoutParams(localLayoutParams1);
this.mArrow.setImageResource(R.drawable.arrow_down);
this.mUpdateContent = (FrameLayout) getChildAt(0).findViewById(
R.id.iv_content);
FrameLayout localFrameLayout2 = this.mUpdateContent;
localFrameLayout2.addView(this.mArrow);
FrameLayout.LayoutParams localLayoutParams2 = new FrameLayout.LayoutParams(
android.view.ViewGroup.LayoutParams.FILL_PARENT,
android.view.ViewGroup.LayoutParams.FILL_PARENT);
localLayoutParams2.gravity = Gravity.CENTER_VERTICAL;
this.mProgressBar = new ProgressBar(localContext1);
int i = getResources().getDimensionPixelSize(R.dimen.progress_padding);
this.mProgressBar.setPadding(i, i, i, i);
this.mProgressBar.setLayoutParams(localLayoutParams2);
this.mUpdateContent.addView(mProgressBar);
this.mTitle = (TextView) findViewById(R.id.tv_title);
}
@Override
protected void onLayout(boolean paramBoolean, int paramInt1, int paramInt2,
int paramInt3, int paramInt4) {
View localView1 = getChildAt(0);
int i = -MAXHEIGHT;
int j = this.mPading;
int k = i - j;
int l = getMeasuredWidth();
int i1 = -this.mPading;
localView1.layout(0, k, l, i1);
View localView2 = getChildAt(1);
int i2 = -this.mPading;
int i3 = getMeasuredWidth();
int i4 = getMeasuredHeight();
int i5 = this.mPading;
int i6 = i4 - i5;
localView2.layout(0, i2, i3, i6);
}
public void endUpdate(String paramString) {
this.mDate = paramString;
if (this.mPading != 0) {
this.mState = STATE_CLOSE;
scrollToClose();
}
}
@Override
public boolean onDown(MotionEvent e) {
return false;
}
@Override
public void onShowPress(MotionEvent e) {
}
@Override
public boolean onSingleTapUp(MotionEvent e) {
return false;
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
float distanceY) {
@SuppressWarnings("rawtypes")
AdapterView localAdapterView = (AdapterView) getChildAt(1);
int k = localAdapterView.getCount();
if (k == 0) {
return false;
}
k = localAdapterView.getFirstVisiblePosition();// 获�第一个显示项目的position
if (k == 0) {
int t = localAdapterView.getChildAt(0).getTop();
if (t != 0) {
return false;
} else {
this.mPading = (int) (this.mPading + distanceY / 2);
if (this.mPading > 0)
this.mPading = 0;
// if (distanceY < 0) {
if (Math.abs(this.mPading) <= MAXHEIGHT) {
this.mState = STATE_OPEN;
} else {
this.mState = STATE_OPEN_MAX;
}
// }
updateView();
}
}
return false;
}
@Override
public void onLongPress(MotionEvent e) {
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
return false;
}
@SuppressWarnings("unused")
private int getScrollRange() {
int scrollRange = 0;
if (getChildCount() > 0) {
View child = getChildAt(0);
scrollRange = Math.max(0,child.getHeight() - (getHeight() - this.getBottomPaddingOffset() - this .getTopPaddingOffset()));
}
return scrollRange;
}
private boolean release() {
int tempStatus = STATE_OPEN_MAX_RELEASE;
int i = this.mPading;
if (i >= 0) {
return true;
}
int j = this.mState;
switch (j) {
case STATE_OPEN_RELEASE:
int k = Math.abs(this.mPading);
int i1 = MAXHEIGHT;
if (k < i1) {
tempStatus = STATE_OPEN_MAX_RELEASE;
this.mState = tempStatus;
}
scrollToClose();
break;
case STATE_OPEN_MAX_RELEASE:
this.mState = tempStatus;
scrollToUpdate();
break;
}
return false;
}
private void scrollToClose() {
Flinger localFlinger = this.mFlinger;
int i = -this.mPading;
localFlinger.startUsingDistance(i, 300);
}
private void scrollToUpdate() {
Flinger localFlinger = this.mFlinger;
int k = -this.mPading - MAXHEIGHT;
localFlinger.startUsingDistance(k, 300);
}
class Flinger implements Runnable {
private int mLastFlingX;
private Scroller mScroller;
public Flinger() {
Context localContext = PullToRefresh.this.getContext();
Scroller localScroller = new Scroller(localContext);
this.mScroller = localScroller;
}
private void startCommon() {
PullToRefresh.this.removeCallbacks(this);
}
@Override
public void run() {
boolean bool1 = Math.abs(mPading) != MAXHEIGHT;
Scroller localScroller = this.mScroller;
boolean bool2 = localScroller.computeScrollOffset();
int i = localScroller.getCurrX();
int j = this.mLastFlingX - i;
PullToRefresh localPullDownView = PullToRefresh.this;
localPullDownView.move(j, bool1);
PullToRefresh.this.updateView();
if (bool2) {
this.mLastFlingX = i;
PullToRefresh.this.post(this);
} else {
PullToRefresh.this.mIsAutoScroller = bool1;
PullToRefresh.this.removeCallbacks(this);
}
}
public void startUsingDistance(int paramInt1, int paramInt2) {
int i = 0;
if (paramInt1 == 0)
--paramInt1;
startCommon();
this.mLastFlingX = i;
Scroller localScroller = this.mScroller;
localScroller.startScroll(i, 0, -paramInt1, 0, paramInt2);
PullToRefresh.this.mIsAutoScroller = true;
PullToRefresh.this.post(this);
}
}
/**
*
*
* @param f
* @param bool1
*/
public void move(float f, boolean bool1) {
if (this.mState != STATE_CLOSE) {
if (!bool1) {
// 刷新
if (mState == STATE_OPEN_MAX_RELEASE) {
this.mState = STATE_UPDATE;
if (mUpdateHandle != null) {
mUpdateHandle.onUpdate();
}
}
}
if (this.mState == STATE_OPEN_MAX_RELEASE
|| this.mState == STATE_OPEN_RELEASE) {
this.mPading += f;
}
} else {
if (mIsAutoScroller) {
this.mPading += f;
}
}
}
public abstract interface UpdateHandle {
public abstract void onUpdate();
}
public void setUpdateDate(String paramString) {
this.mDate = paramString;
}
public void setUpdateHandle(UpdateHandle paramUpdateHandle) {
this.mUpdateHandle = paramUpdateHandle;
}
// public void update()
// {
// int i = -MAXHEIGHT;
// this.mPading = i;
// this.mState = 7;
// }
public void updateWithoutOffset() {
this.mState = STATE_UPDATE_SCROLL;
invalidate();
}
}
Values for the strings.
Refresh
Updating ...
Last Updated :
Release to update..
vw_update_bar layout xml.
dimension.xml used
120px
120px
20px
10px
Update activity class as follows.
package com.example
public class Home extends Activity implements PullToRefresh.UpdateHandle {
private PullToRefresh refresh;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
refresh = (PullToRefresh) findViewById(R.id.pullDownView1);
refresh.setUpdateHandle(this);
// .....
}
//...
@Override
public void onUpdate() {
// Update call .. do the need ful for updating.
}
//....
private final Handler handler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
refresh.endUpdate(getString(R.string.pulltorefreshhandle_text) + getTime());
}
};
}
After adding adapter values means child items to the list view then call this method to finish update.
Message msg = handler.obtainMessage();
handler.sendMessage(msg);