Android 드 롭 다운 새로 고침 업 로드 컨트롤(모든 View 에 적용)

앞에서 드 롭 다운 리 셋 컨트롤 에 관 한 글 을 쓴 적 이 있 습 니 다.드 롭 다운 리 셋 컨트롤 종결자:PullToRefreshLayout나중에 많은 사람들 이 더 많은 수 요 를 추가 하 는 것 을 보 았 습 니 다.그래서 앞에서 드 롭 다운 리 셋 컨트롤 을 바탕 으로 개선 되 었 고 드 롭 다운 기능 을 추 가 했 습 니 다.뿐만 아니 라 모든 View 에 통용 되 는 것 으로 바 꿨 습 니 다!이 두 가지 기능 을 마음대로 사용 할 수 있 습 니 다.
    나 는 ListView,GridView,ExpandableListView,ScrollView,WebView,ImageView,TextView 의 하단 리 셋 과 상단 리 셋 을 실현 하 는 데 모 를 만 들 었 다.다음 에 demo 다운로드 주 소 를 제공 합 니 다.최신 코드 는 github 에 업로드 되 었 습 니 다.https://github.com/jingchenUSTC/PullToRefreshAndLoad
관례 에 따 르 면 다음은 큰 효과 그림 이 될 것 이다.
demo 홈 페이지 도 내 려 갈 수 있 는 ListView 입 니 다.아래 에 table 을 추가 할 수 있 습 니 다.
                                            
ListView:
                
GridView:
                
ExpandableListView:
                
ScrollView:
               
WebView:
               
ImageView:
                
TextView:
                
괜 찮 죠?마지막 ImageView 와 TextView 는 가장 간단 합 니 다.아래 인터페이스 방법 에서 true 를 직접 되 돌려 줍 니 다.
상단 로드 를 늘 리 는 것 은 간단 합 니 다.하단 헤드 를 관리 하 는 것 과 마찬가지 로 상단 헤드 를 하나 더 관리 하 는 것 도 어렵 지 않 습 니 다.그것 을 통용 되 는 것 으로 바 꾸 려 면 View 의 행 위 를 통일 시 켜 야 합 니 다.그래서 저 는 이러한 인 터 페 이 스 를 정 의 했 습 니 다.
package com.jingchen.pulltorefresh.pullableview; 
 
public interface Pullable 

    /**
     * 드 롭 다운 이 가능 한 지 여 부 를 판단 하고 드 롭 다운 기능 이 필요 하지 않 으 면 직접 return false
     * 
     * @return true 내 려 갈 수 있 으 면 false 로 돌아 갑 니 다.
     */ 
    boolean canPullDown(); 
 
    /**
     * 위로 당 길 수 있 는 지 여 부 를 판단 하고 위로 당 길 수 있 는 기능 이 필요 하지 않 으 면 바로 return false
     * 
     * @return true 올 릴 수 있 으 면 false 로 돌아 갑 니 다.
     */ 
    boolean canPullUp(); 

인터페이스 이름 을 보면 당 길 수 있 는 지 여 부 를 판단 하 는 방법 을 제공 하 는 인터페이스 임 을 알 수 있다.이 인터페이스의 두 가지 방법 은 canPullDown()은 언제 내 려 갈 수 있 는 지 판단 하 는 방법 이 고,canPullUp()은 언제 올 라 갈 수 있 는 지 판단 하 는 것 이다.내 가 demo 에서 판단 한 것 은 꼭대기 까지 미 끄 러 질 때 내 려 갈 수 있 고 끝까지 미 끄 러 질 때 올 라 갈 수 있다 는 것 이다.위로 당기 고 아래로 당 겨 야 하 는 모든 View 는 이 인 터 페 이 스 를 실현 해 야 한다.뒤에 뷰 의 실현 이 있 을 겁 니 다.먼저 개 선 된 사용자 정의 레이아웃 PullToRefreshLayout 를 살 펴 보고 위 당 김 머리 를 추 가 했 습 니 다.아래 당 김 머리 와 위 당 김 머리 사이 의 View 는 Pullable 인 터 페 이 스 를 실현 한 pullableView 입 니 다.앞의 버 전에 비해 여기 서 변경 할 때 주의해 야 할 부분 은 다음 과 같다.
1.위 당 김 머리 를 추 가 했 고 이에 따라 제어 변 수 를 증가 했다.
2、당 길 때 content 제거view 이벤트 오류 방지 반사 사용 하지 않 고 직접 설정                       event.setAction(MotionEvent.ACTION_CANCEL)。
3.끌 어 당 기 는 과정 에서 여러 가지 접촉 으로 인 한 격변 을 없 앴 다.
4、content 를 설정 하지 않 습 니 다view 의 onTouListener 는 사용자 가 모니터 를 더욱 자 유 롭 게 설정 할 수 있 도록 합 니 다.
이 PullToRefreshLayout 는 세 개의 컨트롤 만 관리 합 니 다.하나의 View 가 상하 당 김 기능 이 필요 하 다 면 인터페이스 만 실현 하면 됩 니 다.다음은 PullToRefreshLayout 의 코드 를 보고 주석 을 많이 썼 습 니 다.

package com.jingchen.pulltorefresh; 
 
import java.util.Timer; 
import java.util.TimerTask; 
 
import android.content.Context; 
import android.graphics.Canvas; 
import android.graphics.LinearGradient; 
import android.graphics.Paint; 
import android.graphics.Paint.Style; 
import android.graphics.RectF; 
import android.graphics.Shader.TileMode; 
import android.os.Handler; 
import android.os.Message; 
import android.util.AttributeSet; 
import android.util.Log; 
import android.view.MotionEvent; 
import android.view.View; 
import android.view.ViewGroup; 
import android.view.animation.AnimationUtils; 
import android.view.animation.LinearInterpolator; 
import android.view.animation.RotateAnimation; 
import android.widget.RelativeLayout; 
import android.widget.TextView; 
 
import com.jingchen.pulltorefresh.pullableview.Pullable; 
 
/** 
 *       ,         ,        ,        pullableView(     Pullable      View), 
 *         
 * 
 * @author    
 */ 
public class PullToRefreshLayout extends RelativeLayout 
{ 
 public static final String TAG = "PullToRefreshLayout"; 
 //      
 public static final int INIT = 0; 
 //      
 public static final int RELEASE_TO_REFRESH = 1; 
 //      
 public static final int REFRESHING = 2; 
 //      
 public static final int RELEASE_TO_LOAD = 3; 
 //      
 public static final int LOADING = 4; 
 //      
 public static final int DONE = 5; 
 //      
 private int state = INIT; 
 //        
 private OnRefreshListener mListener; 
 //      
 public static final int SUCCEED = 0; 
 //      
 public static final int FAIL = 1; 
 //   Y  ,      Y   
 private float downY, lastY; 
 
 //      。  :pullDownY pullUpY       0 
 public float pullDownY = 0; 
 //       
 private float pullUpY = 0; 
 
 //         
 private float refreshDist = 200; 
 //         
 private float loadmoreDist = 200; 
 
 private MyTimer timer; 
 //      
 public float MOVE_SPEED = 8; 
 //         
 private boolean isLayout = false; 
 //            
 private boolean isTouch = false; 
 //                 ,           
 private float radio = 2; 
 
 //       180°   
 private RotateAnimation rotateAnimation; 
 //        
 private RotateAnimation refreshingAnimation; 
 
 //     
 private View refreshView; 
 //       
 private View pullView; 
 //         
 private View refreshingView; 
 //        
 private View refreshStateImageView; 
 //     :      
 private TextView refreshStateTextView; 
 
 //     
 private View loadmoreView; 
 //       
 private View pullUpView; 
 //         
 private View loadingView; 
 //        
 private View loadStateImageView; 
 //     :      
 private TextView loadStateTextView; 
 
 //    Pullable   View 
 private View pullableView; 
 //        
 private int mEvents; 
 //          pull   ,      ,                  
 private boolean canPullDown = true; 
 private boolean canPullUp = true; 
 
 /** 
 *        handler 
 */ 
 Handler updateHandler = new Handler() 
 { 
 
 @Override 
 public void handleMessage(Message msg) 
 { 
 //          moveDeltaY      
 MOVE_SPEED = (float) (8 + 5 * Math.tan(Math.PI / 2 
  / getMeasuredHeight() * (pullDownY + Math.abs(pullUpY)))); 
 if (!isTouch) 
 { 
 //     ,           ,  "    ..." 
 if (state == REFRESHING && pullDownY <= refreshDist) 
 { 
  pullDownY = refreshDist; 
  timer.cancel(); 
 } else if (state == LOADING && -pullUpY <= loadmoreDist) 
 { 
  pullUpY = -loadmoreDist; 
  timer.cancel(); 
 } 
 
 } 
 if (pullDownY > 0) 
 pullDownY -= MOVE_SPEED; 
 else if (pullUpY < 0) 
 pullUpY += MOVE_SPEED; 
 if (pullDownY < 0) 
 { 
 //       
 pullDownY = 0; 
 pullView.clearAnimation(); 
 //              ,                   
 if (state != REFRESHING && state != LOADING) 
  changeState(INIT); 
 timer.cancel(); 
 } 
 if (pullUpY > 0) 
 { 
 //       
 pullUpY = 0; 
 pullUpView.clearAnimation(); 
 //              ,                   
 if (state != REFRESHING && state != LOADING) 
  changeState(INIT); 
 timer.cancel(); 
 } 
 //     ,     onLayout 
 requestLayout(); 
 } 
 
 }; 
 
 public void setOnRefreshListener(OnRefreshListener listener) 
 { 
 mListener = listener; 
 } 
 
 public PullToRefreshLayout(Context context) 
 { 
 super(context); 
 initView(context); 
 } 
 
 public PullToRefreshLayout(Context context, AttributeSet attrs) 
 { 
 super(context, attrs); 
 initView(context); 
 } 
 
 public PullToRefreshLayout(Context context, AttributeSet attrs, int defStyle) 
 { 
 super(context, attrs, defStyle); 
 initView(context); 
 } 
 
 private void initView(Context context) 
 { 
 timer = new MyTimer(updateHandler); 
 rotateAnimation = (RotateAnimation) AnimationUtils.loadAnimation( 
 context, R.anim.reverse_anim); 
 refreshingAnimation = (RotateAnimation) AnimationUtils.loadAnimation( 
 context, R.anim.rotating); 
 //          
 LinearInterpolator lir = new LinearInterpolator(); 
 rotateAnimation.setInterpolator(lir); 
 refreshingAnimation.setInterpolator(lir); 
 } 
 
 private void hide() 
 { 
 timer.schedule(5); 
 } 
 
 /** 
 *       ,      。  :               
 */ 
 /** 
 * @param refreshResult 
 * PullToRefreshLayout.SUCCEED    ,PullToRefreshLayout.FAIL     
 */ 
 public void refreshFinish(int refreshResult) 
 { 
 refreshingView.clearAnimation(); 
 refreshingView.setVisibility(View.GONE); 
 switch (refreshResult) 
 { 
 case SUCCEED: 
 //      
 refreshStateImageView.setVisibility(View.VISIBLE); 
 refreshStateTextView.setText(R.string.refresh_succeed); 
 refreshStateImageView 
  .setBackgroundResource(R.drawable.refresh_succeed); 
 break; 
 case FAIL: 
 default: 
 //      
 refreshStateImageView.setVisibility(View.VISIBLE); 
 refreshStateTextView.setText(R.string.refresh_fail); 
 refreshStateImageView 
  .setBackgroundResource(R.drawable.refresh_failed); 
 break; 
 } 
 //       1  
 new Handler() 
 { 
 @Override 
 public void handleMessage(Message msg) 
 { 
 changeState(DONE); 
 hide(); 
 } 
 }.sendEmptyMessageDelayed(0, 1000); 
 } 
 
 /** 
 *     ,      。  :               
 * 
 * @param refreshResult 
 * PullToRefreshLayout.SUCCEED    ,PullToRefreshLayout.FAIL     
 */ 
 public void loadmoreFinish(int refreshResult) 
 { 
 loadingView.clearAnimation(); 
 loadingView.setVisibility(View.GONE); 
 switch (refreshResult) 
 { 
 case SUCCEED: 
 //      
 loadStateImageView.setVisibility(View.VISIBLE); 
 loadStateTextView.setText(R.string.load_succeed); 
 loadStateImageView.setBackgroundResource(R.drawable.load_succeed); 
 break; 
 case FAIL: 
 default: 
 //      
 loadStateImageView.setVisibility(View.VISIBLE); 
 loadStateTextView.setText(R.string.load_fail); 
 loadStateImageView.setBackgroundResource(R.drawable.load_failed); 
 break; 
 } 
 //       1  
 new Handler() 
 { 
 @Override 
 public void handleMessage(Message msg) 
 { 
 changeState(DONE); 
 hide(); 
 } 
 }.sendEmptyMessageDelayed(0, 1000); 
 } 
 
 private void changeState(int to) 
 { 
 state = to; 
 switch (state) 
 { 
 case INIT: 
 //          
 refreshStateImageView.setVisibility(View.GONE); 
 refreshStateTextView.setText(R.string.pull_to_refresh); 
 pullView.clearAnimation(); 
 pullView.setVisibility(View.VISIBLE); 
 //          
 loadStateImageView.setVisibility(View.GONE); 
 loadStateTextView.setText(R.string.pullup_to_load); 
 pullUpView.clearAnimation(); 
 pullUpView.setVisibility(View.VISIBLE); 
 break; 
 case RELEASE_TO_REFRESH: 
 //        
 refreshStateTextView.setText(R.string.release_to_refresh); 
 pullView.startAnimation(rotateAnimation); 
 break; 
 case REFRESHING: 
 //        
 pullView.clearAnimation(); 
 refreshingView.setVisibility(View.VISIBLE); 
 pullView.setVisibility(View.INVISIBLE); 
 refreshingView.startAnimation(refreshingAnimation); 
 refreshStateTextView.setText(R.string.refreshing); 
 break; 
 case RELEASE_TO_LOAD: 
 //        
 loadStateTextView.setText(R.string.release_to_load); 
 pullUpView.startAnimation(rotateAnimation); 
 break; 
 case LOADING: 
 //        
 pullUpView.clearAnimation(); 
 loadingView.setVisibility(View.VISIBLE); 
 pullUpView.setVisibility(View.INVISIBLE); 
 loadingView.startAnimation(refreshingAnimation); 
 loadStateTextView.setText(R.string.loading); 
 break; 
 case DONE: 
 //        ,     
 break; 
 } 
 } 
 
 /** 
 *          
 */ 
 private void releasePull() 
 { 
 canPullDown = true; 
 canPullUp = true; 
 } 
 
 /* 
 * (  Javadoc)            ,       
 * 
 * @see android.view.ViewGroup#dispatchTouchEvent(android.view.MotionEvent) 
 */ 
 @Override 
 public boolean dispatchTouchEvent(MotionEvent ev) 
 { 
 switch (ev.getActionMasked()) 
 { 
 case MotionEvent.ACTION_DOWN: 
 downY = ev.getY(); 
 lastY = downY; 
 timer.cancel(); 
 mEvents = 0; 
 releasePull(); 
 break; 
 case MotionEvent.ACTION_POINTER_DOWN: 
 case MotionEvent.ACTION_POINTER_UP: 
 //        
 mEvents = -1; 
 break; 
 case MotionEvent.ACTION_MOVE: 
 if (mEvents == 0) 
 { 
 if (((Pullable) pullableView).canPullDown() && canPullDown 
  && state != LOADING) 
 { 
  //     ,          
  //           ,         
  pullDownY = pullDownY + (ev.getY() - lastY) / radio; 
  if (pullDownY < 0) 
  { 
  pullDownY = 0; 
  canPullDown = false; 
  canPullUp = true; 
  } 
  if (pullDownY > getMeasuredHeight()) 
  pullDownY = getMeasuredHeight(); 
  if (state == REFRESHING) 
  { 
  //             
  isTouch = true; 
  } 
 } else if (((Pullable) pullableView).canPullUp() && canPullUp 
  && state != REFRESHING) 
 { 
  //     ,          
  pullUpY = pullUpY + (ev.getY() - lastY) / radio; 
  if (pullUpY > 0) 
  { 
  pullUpY = 0; 
  canPullDown = true; 
  canPullUp = false; 
  } 
  if (pullUpY < -getMeasuredHeight()) 
  pullUpY = -getMeasuredHeight(); 
  if (state == LOADING) 
  { 
  //             
  isTouch = true; 
  } 
 } else 
  releasePull(); 
 } else 
 mEvents = 0; 
 lastY = ev.getY(); 
 //            
 radio = (float) (2 + 2 * Math.tan(Math.PI / 2 / getMeasuredHeight() 
  * (pullDownY + Math.abs(pullUpY)))); 
 requestLayout(); 
 if (pullDownY <= refreshDist 
<span style="white-space:pre">  </span>&& (state == RELEASE_TO_REFRESH || state == DONE)) { 
<span style="white-space:pre"> </span>//                         ,          
<span style="white-space:pre"> </span>changeState(INIT); 
<span style="white-space:pre"> </span>} 
<span style="white-space:pre"> </span>if (pullDownY >= refreshDist && (state == INIT || state == DONE)) { 
<span style="white-space:pre"> </span>//                          ,          
<span style="white-space:pre"> </span>changeState(RELEASE_TO_REFRESH); 
<span style="white-space:pre"> </span>} 
<span style="white-space:pre"> </span>//           ,  ,  pullUpY    
<span style="white-space:pre"> </span>if (-pullUpY <= loadmoreDist 
<span style="white-space:pre">  </span>&& (state == RELEASE_TO_LOAD || state == DONE)) { 
<span style="white-space:pre"> </span>changeState(INIT); 
<span style="white-space:pre"> </span>} 
<span style="white-space:pre"> </span>if (-pullUpY >= loadmoreDist && (state == INIT || state == DONE)) { 
<span style="white-space:pre"> </span>changeState(RELEASE_TO_LOAD); 
<span style="white-space:pre"> </span>} 
 //                ,  pullDownY pullUpY      0,     (pullDownY + 
 // Math.abs(pullUpY))              
 if ((pullDownY + Math.abs(pullUpY)) > 8) 
 { 
 //                     
 ev.setAction(MotionEvent.ACTION_CANCEL); 
 } 
 break; 
 case MotionEvent.ACTION_UP: 
 if (pullDownY > refreshDist || -pullUpY > loadmoreDist) 
 //         (        ),      (   )    
 isTouch = false; 
 if (state == RELEASE_TO_REFRESH) 
 { 
 changeState(REFRESHING); 
 //      
 if (mListener != null) 
  mListener.onRefresh(this); 
 } else if (state == RELEASE_TO_LOAD) 
 { 
 changeState(LOADING); 
 //      
 if (mListener != null) 
  mListener.onLoadMore(this); 
 } 
 hide(); 
 default: 
 break; 
 } 
 //          
 super.dispatchTouchEvent(ev); 
 return true; 
 } 
 
 private void initView() 
 { 
 //         
 pullView = refreshView.findViewById(R.id.pull_icon); 
 refreshStateTextView = (TextView) refreshView 
 .findViewById(R.id.state_tv); 
 refreshingView = refreshView.findViewById(R.id.refreshing_icon); 
 refreshStateImageView = refreshView.findViewById(R.id.state_iv); 
 //         
 pullUpView = loadmoreView.findViewById(R.id.pullup_icon); 
 loadStateTextView = (TextView) loadmoreView 
 .findViewById(R.id.loadstate_tv); 
 loadingView = loadmoreView.findViewById(R.id.loading_icon); 
 loadStateImageView = loadmoreView.findViewById(R.id.loadstate_iv); 
 } 
 
 @Override 
 protected void onLayout(boolean changed, int l, int t, int r, int b) 
 { 
 if (!isLayout) 
 { 
 //                   
 refreshView = getChildAt(0); 
 pullableView = getChildAt(1); 
 loadmoreView = getChildAt(2); 
 isLayout = true; 
 initView(); 
 refreshDist = ((ViewGroup) refreshView).getChildAt(0) 
  .getMeasuredHeight(); 
 loadmoreDist = ((ViewGroup) loadmoreView).getChildAt(0) 
  .getMeasuredHeight(); 
 } 
 //         ,     (pullDownY + pullUpY)     ,               
 refreshView.layout(0, 
 (int) (pullDownY + pullUpY) - refreshView.getMeasuredHeight(), 
 refreshView.getMeasuredWidth(), (int) (pullDownY + pullUpY)); 
 pullableView.layout(0, (int) (pullDownY + pullUpY), 
 pullableView.getMeasuredWidth(), (int) (pullDownY + pullUpY) 
  + pullableView.getMeasuredHeight()); 
 loadmoreView.layout(0, 
 (int) (pullDownY + pullUpY) + pullableView.getMeasuredHeight(), 
 loadmoreView.getMeasuredWidth(), 
 (int) (pullDownY + pullUpY) + pullableView.getMeasuredHeight() 
  + loadmoreView.getMeasuredHeight()); 
 } 
 
 class MyTimer 
 { 
 private Handler handler; 
 private Timer timer; 
 private MyTask mTask; 
 
 public MyTimer(Handler handler) 
 { 
 this.handler = handler; 
 timer = new Timer(); 
 } 
 
 public void schedule(long period) 
 { 
 if (mTask != null) 
 { 
 mTask.cancel(); 
 mTask = null; 
 } 
 mTask = new MyTask(handler); 
 timer.schedule(mTask, 0, period); 
 } 
 
 public void cancel() 
 { 
 if (mTask != null) 
 { 
 mTask.cancel(); 
 mTask = null; 
 } 
 } 
 
 class MyTask extends TimerTask 
 { 
 private Handler handler; 
 
 public MyTask(Handler handler) 
 { 
 this.handler = handler; 
 } 
 
 @Override 
 public void run() 
 { 
 handler.obtainMessage().sendToTarget(); 
 } 
 
 } 
 } 
 
 /** 
 *          
 * 
 * @author chenjing 
 * 
 */ 
 public interface OnRefreshListener 
 { 
 /** 
 *      
 */ 
 void onRefresh(PullToRefreshLayout pullToRefreshLayout); 
 
 /** 
 *      
 */ 
 void onLoadMore(PullToRefreshLayout pullToRefreshLayout); 
 } 
 
} 
위 에 전체 레이아웃 의 코드 가 있어 서 어렵 지 않 습 니 다.
다음은 각 View 가 Pullable 인터페이스 에 대한 실현 을 살 펴 보 겠 습 니 다.ListView 와 GridView,그리고 ExpandableListView 의 판단 방법 은 같 습 니 다.
PullableListView:

package com.jingchen.pulltorefresh.pullableview; 
 
import android.content.Context; 
import android.util.AttributeSet; 
import android.util.Log; 
import android.widget.ListView; 
 
public class PullableListView extends ListView implements Pullable 
{ 
 
 public PullableListView(Context context) 
 { 
 super(context); 
 } 
 
 public PullableListView(Context context, AttributeSet attrs) 
 { 
 super(context, attrs); 
 } 
 
 public PullableListView(Context context, AttributeSet attrs, int defStyle) 
 { 
 super(context, attrs, defStyle); 
 } 
 
 @Override 
 public boolean canPullDown() 
 { 
 if (getCount() == 0) 
 { 
 //   item           
 return true; 
 } else if (getFirstVisiblePosition() == 0 
 && getChildAt(0).getTop() >= 0) 
 { 
 //   ListView     
 return true; 
 } else 
 return false; 
 } 
 
 @Override 
 public boolean canPullUp() 
 { 
 if (getCount() == 0) 
 { 
 //   item           
 return true; 
 } else if (getLastVisiblePosition() == (getCount() - 1)) 
 { 
 //       
 if (getChildAt(getLastVisiblePosition() - getFirstVisiblePosition()) != null 
  && getChildAt( 
  getLastVisiblePosition() 
   - getFirstVisiblePosition()).getBottom() <= getMeasuredHeight()) 
 return true; 
 } 
 return false; 
 } 
}
 
PullableExpandableListView:

package com.jingchen.pulltorefresh.pullableview; 
 
import android.content.Context; 
import android.util.AttributeSet; 
import android.widget.ExpandableListView; 
 
public class PullableExpandableListView extends ExpandableListView implements 
 Pullable 
{ 
 
 public PullableExpandableListView(Context context) 
 { 
 super(context); 
 } 
 
 public PullableExpandableListView(Context context, AttributeSet attrs) 
 { 
 super(context, attrs); 
 } 
 
 public PullableExpandableListView(Context context, AttributeSet attrs, 
 int defStyle) 
 { 
 super(context, attrs, defStyle); 
 } 
 
 @Override 
 public boolean canPullDown() 
 { 
 if (getCount() == 0) 
 { 
 //   item           
 return true; 
 } else if (getFirstVisiblePosition() == 0 
 && getChildAt(0).getTop() >= 0) 
 { 
 //       
 return true; 
 } else 
 return false; 
 } 
 
 @Override 
 public boolean canPullUp() 
 { 
 if (getCount() == 0) 
 { 
 //   item           
 return true; 
 } else if (getLastVisiblePosition() == (getCount() - 1)) 
 { 
 //       
 if (getChildAt(getLastVisiblePosition() - getFirstVisiblePosition()) != null 
  && getChildAt( 
  getLastVisiblePosition() 
   - getFirstVisiblePosition()).getBottom() <= getMeasuredHeight()) 
 return true; 
 } 
 return false; 
 } 
 
} 
PullableGridView:

package com.jingchen.pulltorefresh.pullableview; 
 
import android.content.Context; 
import android.util.AttributeSet; 
import android.widget.GridView; 
 
public class PullableGridView extends GridView implements Pullable 
{ 
 
 public PullableGridView(Context context) 
 { 
 super(context); 
 } 
 
 public PullableGridView(Context context, AttributeSet attrs) 
 { 
 super(context, attrs); 
 } 
 
 public PullableGridView(Context context, AttributeSet attrs, int defStyle) 
 { 
 super(context, attrs, defStyle); 
 } 
 
 @Override 
 public boolean canPullDown() 
 { 
 if (getCount() == 0) 
 { 
 //   item           
 return true; 
 } else if (getFirstVisiblePosition() == 0 
 && getChildAt(0).getTop() >= 0) 
 { 
 //       
 return true; 
 } else 
 return false; 
 } 
 
 @Override 
 public boolean canPullUp() 
 { 
 if (getCount() == 0) 
 { 
 //   item           
 return true; 
 } else if (getLastVisiblePosition() == (getCount() - 1)) 
 { 
 //       
 if (getChildAt(getLastVisiblePosition() - getFirstVisiblePosition()) != null 
  && getChildAt( 
  getLastVisiblePosition() 
   - getFirstVisiblePosition()).getBottom() <= getMeasuredHeight()) 
 return true; 
 } 
 return false; 
 } 
 
} 
PullableScrollView:

package com.jingchen.pulltorefresh.pullableview; 
 
import android.content.Context; 
import android.util.AttributeSet; 
import android.widget.ScrollView; 
 
public class PullableScrollView extends ScrollView implements Pullable 
{ 
 
 public PullableScrollView(Context context) 
 { 
 super(context); 
 } 
 
 public PullableScrollView(Context context, AttributeSet attrs) 
 { 
 super(context, attrs); 
 } 
 
 public PullableScrollView(Context context, AttributeSet attrs, int defStyle) 
 { 
 super(context, attrs, defStyle); 
 } 
 
 @Override 
 public boolean canPullDown() 
 { 
 if (getScrollY() == 0) 
 return true; 
 else 
 return false; 
 } 
 
 @Override 
 public boolean canPullUp() 
 { 
 if (getScrollY() >= (getChildAt(0).getHeight() - getMeasuredHeight())) 
 return true; 
 else 
 return false; 
 } 
 
} 
PullableWebView:

package com.jingchen.pulltorefresh.pullableview; 
 
import android.content.Context; 
import android.util.AttributeSet; 
import android.webkit.WebView; 
 
public class PullableWebView extends WebView implements Pullable 
{ 
 
 public PullableWebView(Context context) 
 { 
 super(context); 
 } 
 
 public PullableWebView(Context context, AttributeSet attrs) 
 { 
 super(context, attrs); 
 } 
 
 public PullableWebView(Context context, AttributeSet attrs, int defStyle) 
 { 
 super(context, attrs, defStyle); 
 } 
 
 @Override 
 public boolean canPullDown() 
 { 
 if (getScrollY() == 0) 
 return true; 
 else 
 return false; 
 } 
 
 @Override 
 public boolean canPullUp() 
 { 
 if (getScrollY() >= getContentHeight() * getScale() 
 - getMeasuredHeight()) 
 return true; 
 else 
 return false; 
 } 
} 
ImageView 와 TextView 는 붙 이지 않 고 방법 에서 트 루 로 돌 아 왔 습 니 다.
원본 다운로드:유 니 버 설 드 롭 다운 리 셋 
하 이 라이트 주제 공유:javascriptAndroid 불 러 오기 기능 집합
본 고 는 이미 정리 되 었 습 니 다&lt;안 드 로 이 드 드 드 롭 다운 리 셋 업 로드 효과&gt;.여러분 의 학습 연 구 를 환영 합 니 다.
이 글 은 안 드 로 이 드 드 드 롭 다운 을 배 우 는 데 도움 이 되 기 를 바 랍 니 다.

좋은 웹페이지 즐겨찾기