原来android里面写个下拉刷新时这么难的....
总体思路是要定义两个控件, 一个是ViewGroup控件, 一个是listview控件, 自定义的ViewGroup包裹着listview, 以方便对其进行控制
效果图和比较长的代码...
布局:
<com.lin.pulltorefresh.PullRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/pull_refresh_layout"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<include layout="@layout/pull_refresh_head"/>
<com.lin.pulltorefresh.PullRefreshListview
android:id="@+id/list_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<include layout="@layout/pull_refresh_foot"/>
</com.lin.pulltorefresh.PullRefreshLayout>
MainActivity.java:
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ListView listView= (ListView) findViewById(R.id.list_view);
listView.setAdapter(new MyAdapter(this.getApplicationContext()));
//
final PullRefreshLayout pullRefreshLayout= (PullRefreshLayout) findViewById(R.id.pull_refresh_layout);
final Handler handler=new Handler();
final Runnable failRunnale=new Runnable() {
@Override
public void run() {
pullRefreshLayout.notifyFail();
}
};
final Runnable successRunnale=new Runnable() {
@Override
public void run() {
pullRefreshLayout.notifySuccess();
}
};
pullRefreshLayout.setListener(new PullRefreshLayout.PullListener() {
@Override
public void refresh() {
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
handler.post(failRunnale);
}
handler.post(successRunnale);
}
}).start();
}
@Override
public void loadMore() {
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
handler.post(failRunnale);
}
handler.post(successRunnale);
}
}).start();
}
});
}
class MyAdapter extends BaseAdapter{
int size=32;
LayoutInflater lf;
ArrayList<String> datas=new ArrayList<>(size);
public MyAdapter(Context context){
for (int i=0;i<size;i++) datas.add("data id: "+i);
lf=LayoutInflater.from(context);
}
@Override
public int getCount() {
return size;
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return 0;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView==null){
convertView=lf.inflate(R.layout.item_adapter,parent,false);
}
TextView tv= (TextView) convertView.findViewById(R.id.text);
tv.setText(datas.get(position));
return convertView;
}
}
}
PullRefreshListview.java
public class PullRefreshListview extends ListView implements PullWidget{ public PullRefreshListview(Context context, AttributeSet attrs) { super(context, attrs); } @Override public boolean canPullDown() { if (getCount()==0) { return true; } if (getFirstVisiblePosition()==0&&getChildAt(0).getTop()>=0){ return true; } return false; } @Override public boolean canPullUp() { if (getCount()==0) { return true; } View lastView=getChildAt(getLastVisiblePosition()-getFirstVisiblePosition()); if (getLastVisiblePosition()==getCount()-1&&lastView.getBottom()<=getMeasuredHeight()){ return true; } return false; } }
(最复杂的, 主要是因为有太多情况要判断了)PullRefreshLayout.javapublic class PullRefreshLayout extends RelativeLayout { ViewGroup headView,footView; View pullTarget; ProgressBar headProgBar,footProgBar; TextView headText,footText; TextView headTimeText,footTimeText; ImageView headIcon,footIcon; PullListener mListener; //config int pullDonwDist,pullUpDist; int speed; int downY; int headHeight,footHeight; boolean first=true; int state; final static int READY_REFRESHING=0,READY_LOAGMOREING=1, NORMAL=2,REFRESHING=31,LOADING=32,RESET=4,SUCCESS=5, FAIL=6,WAITING=7; Handler mHandler; Handler timeHandler; int duration=300; private int fps=60; //date long preRefreshTime=0,preLoadTime=0; RotateAnimation rotateAnimation1=new RotateAnimation(0,180f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); RotateAnimation rotateAnimation2=new RotateAnimation(180,0f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); public PullRefreshLayout(Context context, AttributeSet attrs) { super(context, attrs); state=NORMAL; rotateAnimation1.setDuration(200); rotateAnimation1.setFillAfter(true); rotateAnimation2.setDuration(200); rotateAnimation2.setFillAfter(true); preRefreshTime=preLoadTime=System.currentTimeMillis(); mHandler=new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); if (state==RESET||state==SUCCESS||state==FAIL){ if (pullDonwDist>0){ pullDonwDist=pullDonwDist-speed>0?pullDonwDist-speed:0; }else if (pullUpDist<0){ pullUpDist=pullUpDist+speed<0?pullUpDist+speed:0; } requestLayout(); if (pullDonwDist!=0||pullUpDist!=0){ sendEmptyMessageDelayed(0,duration/fps); }else{ state=NORMAL; } } else if (state==REFRESHING){ if (pullDonwDist>headHeight){ pullDonwDist=pullDonwDist-speed>headHeight?pullDonwDist-speed:headHeight; } requestLayout(); if (pullDonwDist!=headHeight){ sendEmptyMessageDelayed(0,duration/fps); } } else if (state==LOADING){ if (pullUpDist<-footHeight){ pullUpDist=pullUpDist+speed<-footHeight?pullUpDist+speed:-footHeight; } requestLayout(); if (pullUpDist!=-footHeight){ sendEmptyMessageDelayed(0,duration/fps); } } } }; } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { if (first){ first=false; headView= (ViewGroup) getChildAt(0); footView= (ViewGroup) getChildAt(2); //init head headIcon= (ImageView) headView.findViewById(R.id.icon); headProgBar= (ProgressBar) headView.findViewById(R.id.progress_bar); headText= (TextView) headView.findViewById(R.id.text); headTimeText= (TextView) headView.findViewById(R.id.time_text); headHeight=headView.getMeasuredHeight(); //init foot footIcon= (ImageView) footView.findViewById(R.id.icon); footProgBar= (ProgressBar) footView.findViewById(R.id.progress_bar); footText= (TextView) footView.findViewById(R.id.text); footTimeText= (TextView) footView.findViewById(R.id.time_text); footHeight=footView.getMeasuredHeight(); //init target pullTarget=getChildAt(1); // timeHandler=new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); if (state==REFRESHING){ preRefreshTime=System.currentTimeMillis(); }else if (state==LOADING){ preLoadTime=System.currentTimeMillis(); } else{ headTimeText.setText("上次刷新时间: "+(System.currentTimeMillis()-preRefreshTime)/1000+"秒"); footTimeText.setText("上次刷新时间: "+(System.currentTimeMillis()-preLoadTime)/1000+"秒"); } sendEmptyMessageDelayed(0,1000); } }; timeHandler.sendEmptyMessage(0); } int dist=pullDonwDist+pullUpDist; headView.layout( 0, -headView.getMeasuredHeight()+dist, headView.getMeasuredWidth(), 0+dist); pullTarget.layout( 0, 0+dist, pullTarget.getMeasuredWidth(), pullTarget.getMeasuredHeight()+dist); footView.layout( 0, pullTarget.getMeasuredHeight()+dist, footView.getMeasuredWidth(), pullTarget.getMeasuredHeight()+footView.getMeasuredHeight()+dist); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { if (state==LOADING||state==RESET||state==SUCCESS||state==FAIL){ return super.dispatchTouchEvent(ev); } switch (ev.getAction()){ case MotionEvent.ACTION_DOWN: downY= (int) ev.getRawY(); pullDonwDist=pullUpDist=0; break; case MotionEvent.ACTION_MOVE: int moveY= (int) ev.getRawY(); int dist=moveY-downY; if (((PullWidget)pullTarget).canPullDown()&&dist>=0){ pullDonwDist=dist/2; pullUpDist=0; if (pullDonwDist>=headHeight){ if (state!=READY_REFRESHING){ state=READY_REFRESHING; headIcon.startAnimation(rotateAnimation1); headText.setText("松开刷新.."); } }else{ if (state!=NORMAL){ headIcon.startAnimation(rotateAnimation2); headText.setText("下拉刷新"); state=NORMAL; } } }else if (((PullWidget)pullTarget).canPullUp()&&dist<=0){ pullDonwDist=0; pullUpDist=dist/2; if (-pullUpDist>=footHeight){ if (state!=READY_LOAGMOREING){ state=READY_LOAGMOREING; footIcon.startAnimation(rotateAnimation1); footText.setText("松开加载更多.."); } }else{ if (state!=NORMAL){ footIcon.startAnimation(rotateAnimation2); footText.setText("上拉刷新"); state=NORMAL; } } }else{ pullDonwDist=pullUpDist=0; return super.dispatchTouchEvent(ev); } requestLayout(); return true; case MotionEvent.ACTION_UP: if (pullDonwDist>0){ //头部复位到刷新的位置 if (state==READY_REFRESHING){ headIcon.clearAnimation(); headIcon.setVisibility(View.INVISIBLE); headProgBar.setVisibility(View.VISIBLE); headText.setText("正在刷新"); state=REFRESHING; if (mListener!=null) { mListener.refresh(); }else{ notifySuccess(); } } //头部复位到0 else{ state=RESET; } speed=pullDonwDist*1000/fps/duration; }else if (-pullUpDist>0){ //尾部复位到刷新的位置 if (state==READY_LOAGMOREING){ footText.setText("正在刷新"); state=LOADING; footIcon.clearAnimation(); footIcon.setVisibility(View.INVISIBLE); footProgBar.setVisibility(View.VISIBLE); if (mListener!=null) { mListener.loadMore(); }else{ notifySuccess(); } } //尾部复位 else{ state=RESET; } speed=-pullUpDist*1000/fps/duration; } if (speed==0) speed=2; mHandler.sendEmptyMessage(0); break; } return super.dispatchTouchEvent(ev); } public void setListener(PullListener pullListener){ mListener=pullListener; } public void notifySuccess() { notifyState(SUCCESS); } public void notifyFail() { notifyState(FAIL); } private void notifyState(int _state) { state=_state; footProgBar.setVisibility(View.GONE); footIcon.setVisibility(View.VISIBLE); footIcon.startAnimation(rotateAnimation2); headProgBar.setVisibility(View.GONE); headIcon.setVisibility(View.VISIBLE); headIcon.startAnimation(rotateAnimation2); mHandler.sendEmptyMessage(0); } public interface PullListener{ public void refresh(); public void loadMore(); }