您的位置:首页 > 移动开发 > Android开发

android 安卓 listview 支持下拉刷新 上拉加载更多

2016-11-09 15:03 666 查看
【1】重写listView
import java.text.SimpleDateFormat;
import java.util.Date;

import com.example.testdddleapk.R;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.LinearInterpolator;
import android.view.animation.RotateAnimation;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;

public class RefreshListView extends ListView implements OnScrollListener {

private static final int DONE = 0;
private static final int PULL_TO_REFRESH = 1;
private static final int RELEASE_TO_REFRESH = 2;
private static final int REFRESHING = 3;
private static final float RATIO = 3;// 用来设置实际间距和上边距之间的比例
private int state;// 当前下拉刷新的状态
private int firstVisibleIndex;// 在listview中第一个可以看见的item
private View headView;
private ImageView headArrow;
private ProgressBar progressBar;
private TextView headTitle;
private TextView headLastUpdate;
private int headContentWidth;
private int headContentHeight;
private Animation animation;
private Animation reverseAnimation;
private OnRefreshListner refreshListner;// 刷新监听器
private boolean isRefreshable;
private boolean isRecored = false;// 用来记录第一次按下坐标点,在整个滑动的过程中 只记录一次
private float startY;
private boolean isBack = false;// 是从 松开刷新状态 来到的 下拉刷新状态

public RefreshListView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}

private void init(Context context) {
// listview 设置滑动时缓冲背景色
setCacheColorHint(0xcc000000);
headView = View.inflate(context, R.layout.listview_header, null);
headArrow = (ImageView) headView.findViewById(R.id.iv_listview_header_arrow);
progressBar = (ProgressBar) headView.findViewById(R.id.pb_listview_header);
headTitle = (TextView) headView.findViewById(R.id.tv_listview_header_last_update_time);
headLastUpdate = (TextView) headView .findViewById(R.id.tv_listview_header_state);
headArrow.setMinimumWidth(50);
headArrow.setMinimumHeight(70);
MeasureView(headView);
headContentWidth = headView.getMeasuredWidth();
headContentHeight = headView.getMeasuredHeight();
headView.setPadding(0, -1*headContentHeight, 0, 0);
// 为listView加入顶部View
addHeaderView(headView);
setOnScrollListener(this);
animation = new RotateAnimation(-180, 0, Animation.RELATIVE_TO_SELF,0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
animation.setDuration(250);
animation.setFillAfter(true);// 设定动画结束时,停留在动画结束位置 (保留动画效果)
animation.setInterpolator(new LinearInterpolator());// 匀速变化
reverseAnimation = new RotateAnimation(0, -180,Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,0.5f);
reverseAnimation.setDuration(200);
reverseAnimation.setFillAfter(true);// 设定动画结束时,停留在动画结束位置 (保留动画效果)
reverseAnimation.setInterpolator(new LinearInterpolator());// 匀速变化
// 设置当前headView的状态
state = DONE;
// 设置当前下拉刷新是否可用
isRefreshable = false;
}

/**
* 测量headView的 宽高
*/
private void MeasureView(View child) {
ViewGroup.LayoutParams lp = child.getLayoutParams();
if (null == lp) {
lp = new ViewGroup.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT);
}
int measureChildWidth = ViewGroup.getChildMeasureSpec(0, 0, lp.width);
int measureChildHeight;
if (lp.height > 0) {
measureChildHeight = MeasureSpec.makeMeasureSpec(lp.height, MeasureSpec.EXACTLY);
} else {
measureChildHeight = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
}
child.measure(measureChildWidth, measureChildHeight);
}

private float tempY=0;
private float downY=0;
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
downY=event.getY();
System.out.println("downY:"+downY);
if (firstVisibleIndex == 0 && !isRecored) {
startY = event.getY();
isRecored = true;
}
break;
case MotionEvent.ACTION_MOVE:

//			float tempY = event.getY();
tempY = event.getY();
System.out.println("tempy:"+tempY);
if (firstVisibleIndex == 0 && !isRecored) {
startY = tempY;
isRecored = true;
}
if (state != REFRESHING) {

if (state == PULL_TO_REFRESH) {
// 向下拉了 从下拉刷新的状态 来到 松开刷新的状态
if ((tempY - startY) / RATIO >= headContentHeight
&& (tempY - startY) > 0) {
state = RELEASE_TO_REFRESH;
changeHeadViewOfState();
}
// 向上推了 从下拉刷新的状态 来到 刷新完成的状态
else if ((tempY - startY) <= 0) {
state = DONE;
changeHeadViewOfState();
}
} else if (state == RELEASE_TO_REFRESH) {
// 向上推了 还没有完全将HEADVIEW 隐藏掉(可以看到一部分)
// 从松开刷新的状态 来到 下拉刷新的状态
if ((tempY - startY) / RATIO < headContentHeight
&& (tempY - startY) > 0) {
state = PULL_TO_REFRESH;
changeHeadViewOfState();
isBack = true;
}
// 向上推了 一下子推到了最上面 从松开刷新的状态 来到 刷新完成的状态 (数据不刷新的)
else if ((tempY - startY) <= 0) {
state = DONE;
changeHeadViewOfState();
}
} else if (state == DONE) {
// 刷新完成的状态 来到 下拉刷新的状态
if ((tempY - startY) > 0) {
state = PULL_TO_REFRESH;
changeHeadViewOfState();
}
}
if (state == PULL_TO_REFRESH) {
headView.setPadding(0,(int) ((tempY - startY) / RATIO - headContentHeight),0, 0);
}
if (state == RELEASE_TO_REFRESH) {
headView.setPadding(0, (int) ((tempY - startY) / RATIO - headContentHeight),0, 0);
}
}
break;
case MotionEvent.ACTION_UP:
if (state != REFRESHING) {
if (state == PULL_TO_REFRESH) {
// 松手
state = DONE;
changeHeadViewOfState();
}
else if (state == RELEASE_TO_REFRESH) {
// 松手
state = REFRESHING;
changeHeadViewOfState();
// 执行数据刷新方法
onRefresh();
}
}
isRecored = false;
isBack = false;
break;
}
return super.onTouchEvent(event);
}

/**
* 执行下拉刷新
*/
private void onRefresh() {
if (refreshListner != null) {
refreshListner.onRefresh();
}
}

/**
* HeadView的状态变化效果
*/
private void changeHeadViewOfState() {
switch (state) {
case PULL_TO_REFRESH:
headArrow.setVisibility(View.VISIBLE);
progressBar.setVisibility(View.GONE);
headTitle.setVisibility(View.VISIBLE);
headLastUpdate.setVisibility(View.VISIBLE);
headArrow.clearAnimation();
headTitle.setText("下拉可以刷新");
//由 松开刷新  到  下拉刷新
if(isBack){
headArrow.startAnimation(animation);
isBack = false;
}
break;
case RELEASE_TO_REFRESH:
headArrow.setVisibility(View.VISIBLE);
progressBar.setVisibility(View.GONE);
headTitle.setVisibility(View.VISIBLE);
headLastUpdate.setVisibility(View.VISIBLE);
headArrow.clearAnimation();
headArrow.startAnimation(reverseAnimation);
headTitle.setText("松开可以刷新");
break;
case REFRESHING:
headArrow.setVisibility(View.GONE);
progressBar.setVisibility(View.VISIBLE);
headTitle.setVisibility(View.VISIBLE);
headLastUpdate.setVisibility(View.VISIBLE);
headArrow.clearAnimation();
headTitle.setText("正在刷新...");
headView.setPadding(0, 0, 0, 0);
break;
case DONE:
headArrow.setVisibility(View.VISIBLE);
progressBar.setVisibility(View.GONE);
headTitle.setVisibility(View.VISIBLE);
headLastUpdate.setVisibility(View.VISIBLE);
headArrow.clearAnimation();
headTitle.setText("下拉可以刷新");
headView.setPadding(0, -1 * headContentHeight, 0, 0);
break;
}
}

private int lastPos;//最后一个可见的item的位置
private int count;//item总数,注意不是当前可见的item总数
private boolean hasFoot = false;//是否有了Foot

public void onScroll(AbsListView view, int firstVisibleItem,int visibleItemCount, int totalItemCount) {
firstVisibleIndex = firstVisibleItem;
lastPos = getLastVisiblePosition();
count = totalItemCount;
//因为刚进入的时候,lastPos=-1,count=0,这个时候不能让它执行onAddFoot方法
if(lastPos==count-1 && !hasFoot && lastPos != -1&&((tempY-startY)<0)){
hasFoot = true;
onAddFoot();
}
}
public void onScrollStateChanged(AbsListView view, int scrollState) {
if(isFootLoading) return;
if(lastPos==count-1 && lastPos != -1&&hasFoot&&footer!=null&&((tempY-downY)>0)){
this.removeFooterView(footer);
}
if(hasFoot && scrollState==SCROLL_STATE_IDLE&&((tempY-downY)<0)){
isFootLoading = true;
onFootLoading();
}
}
/**
* 设置下拉刷新监听
*/
public void setOnRefreshListner(OnRefreshListner listener) {
// 设置下拉刷新可用
isRefreshable = true;
refreshListner = listener;
}

//执行底部加载
public void onFootLoading(){
if(footLoadingListener!=null && isFootLoading &&hasFoot&&(tempY-downY)<0)  {
footLoadingListener.onFootLoading();  }
}

public void setOnAddFootListener(OnAddFootListener addFootListener){
onAddFootListener = addFootListener;
}

//执行添加foot
public void onAddFoot(){
if(onAddFootListener!=null && hasFoot)
onAddFootListener.addFoot();
}
//是否添加Foot的监听器,如果写在OnFootLoadingListener中会有延迟,效果不好
//应该是先进入添加Foot的状态,再进入FootLoading的状态
public OnAddFootListener onAddFootListener;
//是否进入从底部加载数据的状态的监听器
public OnFootLoadingListener footLoadingListener;
//正在加载底部数据
private boolean isFootLoading = false;
public void setOnFootLoadingListener(OnFootLoadingListener footLoading){
footLoadingListener = footLoading;
}

/**
* 下拉刷新监听器
*/
public interface OnRefreshListner {
// 下拉刷新的时候,在这里执行获取数据的过程
void onRefresh();
}

private View footer=null;
public void setFooter(View footer){
this.footer=footer;
}
/**
* 上拉刷新监听器
*/
public interface OnFootLoadingListener{
// 这里是执行后台获取数据的过程
void onFootLoading();
}

/**
* 添加Foot的监听器
*/
public interface OnAddFootListener{
// 这里是用户addFootView的操作
void addFoot();
}

/**
* 底部数据加载完成,用户需要加入一个removeFootView的操作
*/
public void onFootLoadingComplete(){
hasFoot = false;
isFootLoading = false;
}

/**
* 上拉刷新完成时 所执行的操作,更改状态,隐藏head
*/
public void onRefreshComplete() {
state = DONE;
changeHeadViewOfState();
headLastUpdate.setText("最后刷新时间: " + new Date().toLocaleString());
}

@Override
public void setAdapter(ListAdapter adapter) {
headLastUpdate.setText("最后刷新时间: " + new Date().toLocaleString());
super.setAdapter(adapter);
}
}


【2】下拉刷新头部布局文件listview_header.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="wrap_content"
android:orientation="horizontal" >

<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dip" >

<ImageView
android:id="@+id/iv_listview_header_arrow"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_gravity="center"
android:minWidth="30dip"
android:src="@drawable/arrow" />

<ProgressBar
android:id="@+id/pb_listview_header"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:indeterminateDrawable="@drawable/common_progressbar"
android:visibility="gone" />
</FrameLayout>

<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:gravity="center_horizontal"
android:orientation="vertical" >

<TextView
android:id="@+id/tv_listview_header_state"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="下拉刷新"
android:textColor="#FF0000"
android:textSize="18sp" />

<TextView
android:id="@+id/tv_listview_header_last_update_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="5dip"
android:text="最后刷新时间: 2014-10-10 12:56:12"
android:textColor="@android:color/white"
android:textSize="14sp" />
</LinearLayout>

</LinearLayout>
【3】上拉加载更多布局文件listview_footer.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="wrap_content"
android:orientation="vertical" >

<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_margin="10dip"
android:gravity="center_vertical"
android:orientation="horizontal" >

<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:indeterminateDrawable="@drawable/common_progressbar" />

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dip"
android:text="加载更多..."
android:textColor="#FF0000"
android:textSize="18sp" />
</LinearLayout>

</LinearLayout>
【4】progressBar--common_progressbar.xml
<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:fromDegrees="0"
android:pivotX="50%"
android:pivotY="50%"
android:toDegrees="360" >

<shape
android:innerRadiusRatio="3"
android:shape="ring"
android:useLevel="false" >
<gradient
android:centerColor="#FF6666"
android:endColor="#FF0000"
android:startColor="#FFFFFF"
android:type="sweep" />
</shape>

</rotate>
【5】使用方法
adapter=new myListAdapter(items,xxx.this);
listView.setAdapter(adapter);
/**
* 下拉刷新回调
*/
listView.setOnRefreshListner(new OnRefreshListner() {

public void onRefresh() {
new AsyncTask<Void, Void, ArrayList<String>>(){
@Override
protected ArrayList<String> doInBackground(Void... params) {
try {
//模拟从服务器获取数据的过程
Thread.sleep(1500);
} catch (InterruptedException e) {
e.printStackTrace();
}
return null;
}
//更新UI的方法,系统自动实现
@Override
protected void onPostExecute(ArrayList<String> result) {
data.addAll(0,result);//注意是往前添加数据
adapter.notifyDataSetChanged();
listView.onRefreshComplete();//完成下拉刷新,这个方法要调用
super.onPostExecute(result);
}
}.execute();
}
});

//上拉加载更多功能

final View footer = View.inflate(getActivity(), R.layout.listview_footer, null);
listView.setOnAddFootListener(new OnAddFootListener() {
public void addFoot() {
listView.setFooter(footer);
listView.addFooterView(footer);
}
});
listView.setOnFootLoadingListener(new OnFootLoadingListener() {
public void onFootLoading() {
new AsyncTask<Void, Void, ArrayList<String>>(){
@Override
protected ArrayList<String> doInBackground(Void... params) {
try {
//模拟从服务器获取数据的过程
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return null;
}
//在doInBackground后面执行
protected void onPostExecute(ArrayList<String> result) {
data.addAll(result);//这个是往后添加数据
adapter.notifyDataSetChanged();
listView.onFootLoadingComplete();//完成上拉刷新,就是底部加载完毕,这个方法要调用
//移除footer,这个动作不能少
listView.removeFooterView(footer);
super.onPostExecute(result);
}
}.execute();
}
});
【6】注意事项
listview设置项目点击事件
listView.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view,int position, long id) {
if(position==0||position==items.length()+1) return;
//获取每项的数据,position需减1,第0项为listview_head
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  下拉 安卓 上拉
相关文章推荐