这种方法的原理就是Layout包含loadingLayout显示加载布局和Listview显示数据布局,通过onTouch监听下拉时间,然后设置这个Layout的顶距和loadingLayout的动画来达到刷新的效果。
布局文件xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<RelativeLayout android:id="@+id/test_parent"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
// loadingLayout显示刷新的效果
<LinearLayout
android:id="@+id/test_top"
android:layout_width="fill_parent"
android:layout_height="50dip"
android:background="#cc3366"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:orientation="horizontal">
<ImageView
android:id="@+id/test_image"
android:layout_width="40dip"
android:layout_height="40dip"
android:layout_marginLeft="30dip"
android:layout_marginTop="5dip"
android:src="@drawable/indicator_arrow" />
<TextView android:id="@+id/test_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="正在刷新......"
android:layout_marginLeft="30dip"
android:layout_marginTop="15dip" />
</LinearLayout>// end
// 显示ListView 数据
<ListView android:id="@+id/test_listview"
android:layout_below="@+id/test_top"
android:layout_width="wrap_content"
android:layout_height="wrap_content" >
</ListView>// end
// 底部菜单
<RelativeLayout android:id="@+id/test_bottom"
android:layout_width="fill_parent"
android:layout_height="50dip"
android:layout_alignParentBottom="true"
android:background="#ccff99"
android:visibility="gone" >
<Button
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
</RelativeLayout>
</RelativeLayout>
</LinearLayout>
下面是实现的一个activity类,当然也可以设置成自己的自定义类,以后用方便。
public class TestPullToRefreshActivity extends Activity implements OnScrollListener,OnTouchListener {
private static final String TAG = "TestPullToRefreshActivity";
private ListView listview;
// loadingLayout
private LinearLayout topLayout;
// 底部菜单
private RelativeLayout bottomLayout;
// 整个layout
private RelativeLayout parentLayout ;
private MarginLayoutParams parentLayoutParams;
// 整个高度
private int parentLayoutHeight;
private int allViewheight;
private boolean ableToPull = true;
private int hideHeaderHeight;
// 下拉状态
private static final int status_pull_to_refresh = 0;
// 释放立即刷新
private static final int status_refresh_to_refresh = 1;
// 正在刷新
private static final int status_refreshing = 2;
// 刷新完成
private static final int status_refresh_finished= 3;
// 当前状态
private int currentStatus = status_refresh_finished;
private static final int status_top_start = 1;
private static final int status_top_stop = 0;
private int topStatus = status_top_start;
private ArrayAdapter<String> adapter;
private List<String> data = new ArrayList<String>();
private ImageView imageview;
private TextView textview;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test_listview);
listview = (ListView)this.findViewById(R.id.test_listview);
topLayout = (LinearLayout)this.findViewById(R.id.test_top);
bottomLayout = (RelativeLayout)this.findViewById(R.id.test_bottom);
parentLayout = (RelativeLayout)this.findViewById(R.id.test_parent);
parentLayoutHeight = parentLayout.getHeight();
parentLayoutParams = (MarginLayoutParams) parentLayout.getLayoutParams();
ViewTreeObserver vto = topLayout.getViewTreeObserver();
vto.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
private boolean hasMeasured = false;
public boolean onPreDraw() {
if (!hasMeasured)
{
hideHeaderHeight = topLayout.getMeasuredHeight();
Log.i(TAG, "hideHeaderHeight-->"+hideHeaderHeight);
hasMeasured = true;
}
return true;
}
});
ViewTreeObserver vto2 = listview.getViewTreeObserver();
vto2.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
private boolean hasMeasured = false;
public boolean onPreDraw() {
if (!hasMeasured)
{
allViewheight = listview.getMeasuredHeight();
Log.i(TAG, "allViewheight-->"+allViewheight);
hasMeasured = true;
}
return true;
}
});
Log.i(TAG, "parentLayoutParams.height:"+parentLayoutParams.height);
Log.i(TAG, "hideHeaderHeight:"+hideHeaderHeight);
allViewheight = ViewConfiguration.get(TestPullToRefreshActivity.this).getScaledTouchSlop();
data.add("A-1---");data.add("A-2---");data.add("A-3---");data.add("A-4---");data.add("A-5---");
data.add("A----");data.add("A----");data.add("A----");data.add("A----");data.add("A----");
data.add("A----");data.add("A----");data.add("A----");data.add("A----");
adapter= new ArrayAdapter<String>(getApplicationContext(), android.R.layout.simple_list_item_1,data);
listview.setAdapter(adapter);
listview.setOnScrollListener(this);
// listview.setOnTouchListener(this);
imageview = (ImageView)this.findViewById(R.id.test_image);
textview = (TextView)this.findViewById(R.id.test_text);
}
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
//Log.i(TAG, "onScrollStateChanged()");
}
private int concurrentVisiableItem = 0;
@Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
Log.v(TAG, "Scroll>>>first: " + firstVisibleItem + ", visible: " + visibleItemCount + ", total: " + totalItemCount);
Log.v(TAG, "concurrentVisiableItem:"+concurrentVisiableItem);
Log.w(TAG, "msg");
if(visibleItemCount > 3){
// 上滑
if(concurrentVisiableItem > firstVisibleItem){
if(bottomLayout.getVisibility()==View.VISIBLE)
bottomLayout.setVisibility(View.GONE);
concurrentVisiableItem = firstVisibleItem;
}
// 下滑
if(concurrentVisiableItem < firstVisibleItem){
if(bottomLayout.getVisibility()==View.GONE)
bottomLayout.setVisibility(View.VISIBLE);
concurrentVisiableItem = firstVisibleItem;
}
}
// if(firstVisibleItem==0){
// Log.e(TAG, "滑到顶部");
// }
// if(visibleItemCount+firstVisibleItem==totalItemCount){
// Log.e(TAG, "滑到底部");
// if(bottomLayout.getVisibility()==View.GONE){
// bottomLayout.setVisibility(View.VISIBLE);
// }
// }
}
private float mY = 0;
private float mX = 0;
@Override
public boolean onTouch(View v, MotionEvent event) {
setIsAbleToPull(event);
if(ableToPull){
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:Log.i(TAG, "ACTION_DOWN()---------");
mY = event.getRawY();
Log.i(TAG, "mY:"+mY);
Log.i(TAG, "ACTION_DOWN()------------");
break;
case MotionEvent.ACTION_MOVE:Log.i(TAG, "ACTION_MOVE()-----------");
float yMove = event.getRawY();
int distance = (int) (yMove - mY);
Log.i(TAG, "distance:"+distance);
Log.i(TAG, "hideHeaderHeight:"+hideHeaderHeight);
Log.i(TAG, " parentLayoutParams.topMargin:"+ parentLayoutParams.topMargin);
if( distance>0 && parentLayoutParams.topMargin >= 0){
return false;
}
if(distance < 0 && parentLayoutParams.topMargin <= hideHeaderHeight){
Log.i(TAG, "超过了不移动");
return false;
}
// 不是正在刷新中就不做任何事情
if(currentStatus != status_refreshing){
currentStatus = status_pull_to_refresh;
Log.i(TAG, "可以刷新了");
}
parentLayoutParams.topMargin = -hideHeaderHeight + (distance / 2) ;
parentLayout.setLayoutParams(parentLayoutParams);
Log.i(TAG, "ACTION_MOVE()-----------");
break;
case MotionEvent.ACTION_UP:Log.i(TAG, "ACTION_UP()--------------");
if(currentStatus == status_pull_to_refresh){
new TopAsyncTask().execute();
}
Log.i(TAG, "ACTION_UP()--------------");
break;
default:
break;
}
// 时刻记得更新下拉头中的信息
if (currentStatus == status_pull_to_refresh || currentStatus == status_refresh_to_refresh) {
updateHeaderView();
topStatus = status_top_stop;
}
// 当前正处于下拉或释放状态,要让ListView失去焦点,否则被点击的那一项会一直处于选中状态
listview.setPressed(false);
listview.setFocusable(false);
listview.setFocusableInTouchMode(false);
return true;
}
return false;
}
public void updateHeaderView(){
if (topStatus == status_top_start) {
if (currentStatus == status_pull_to_refresh) {
rotateArrow();
} else if (currentStatus == status_refresh_to_refresh) {
} else if (currentStatus == status_refresh_finished) {
}
// refreshUpdatedAtValue();
}
}
/**
* 根据当前的状态来旋转箭头。
*/
private void rotateArrow() {
float pivotX = imageview.getWidth() / 2f;
float pivotY = imageview.getHeight() / 2f;
float fromDegrees = 0f;
float toDegrees = 0f;
if (currentStatus == status_pull_to_refresh) {
fromDegrees = 180f;
toDegrees = 360f;
} else if (currentStatus == status_refresh_to_refresh) {
fromDegrees = 0f;
toDegrees = 180f;
}
RotateAnimation animation = new RotateAnimation(fromDegrees, toDegrees, pivotX, pivotY);
animation.setDuration(800);
animation.setFillAfter(true);
imageview.startAnimation(animation);
}
/**
* 根据当前ListView的滚动状态来设定 {@link #ableToPull}
* 的值,每次都需要在onTouch中第一个执行,这样可以判断出当前应该是滚动ListView,还是应该进行下拉。
*
* @param event
*/
private void setIsAbleToPull(MotionEvent event) {
View firstChild = listview.getChildAt(0);
if (firstChild != null) {
int firstVisiblePos = listview.getFirstVisiblePosition();
// Log.i(TAG, "firstVisiblePos:" + firstVisiblePos);
// Log.i(TAG, "firstChild.getTop():" + firstChild.getTop());
if (firstVisiblePos == 0 && firstChild.getTop() == 0) {
if (!ableToPull) {
mY = event.getRawY();
}
// 如果首个元素的上边缘,距离父布局值为0,就说明ListView滚动到了最顶部,此时应该允许下拉刷新
ableToPull = true;
} else {
if (parentLayoutParams.topMargin != hideHeaderHeight) {
parentLayoutParams.topMargin = -hideHeaderHeight;
parentLayout.setLayoutParams(parentLayoutParams);
}
ableToPull = false;
}
} else {
// 如果ListView中没有元素,也应该允许下拉刷新
ableToPull = true;
}
}
class TopAsyncTask extends AsyncTask<Void, Integer, String[]>{
@Override
protected void onPreExecute() {
super.onPreExecute();
currentStatus = status_refreshing;
}
@Override
protected String[] doInBackground(Void... params) {
Log.i(TAG, "doInBackground");
int topMargin = parentLayoutParams.topMargin;
while (true) {
topMargin = topMargin + 1;
if (topMargin >= -hideHeaderHeight) {
topMargin = -hideHeaderHeight;
break;
}
publishProgress(topMargin);
sleep(10);
}
publishProgress(-hideHeaderHeight);
Log.i(TAG, "doInBackground end()");
return null;
}
@Override
protected void onPostExecute(String[] result) {
super.onPostExecute(result);
currentStatus = status_refresh_finished;
topStatus = status_top_start;
}
@Override
protected void onProgressUpdate(Integer... topMargin) {
parentLayoutParams.topMargin = topMargin[0];
parentLayout.setLayoutParams(parentLayoutParams);
data.add(0, "B--");data.add(1, "B--");data.add(2, "添加了两个B--");
adapter.notifyDataSetChanged();
}
}
private void sleep(int time) {
try {
Thread.sleep(time);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
这种效果有个弊端:滑动不够流畅,所以使用动画才最可靠。