Sunday, June 3, 2012

Pull to refresh in Android

“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);

No comments:

Post a Comment