Android实现ListView的下拉刷新、上拉加载更多
在Andoird编程中经常使用的控件包括ListView,就个人而言,我还是很喜欢ListView的,在使用ListView的过程中,必不可少的就是下拉刷新、上拉加载更多。下面是我自己写出一个小Demo便于大家理解。希望大家可以理解。共同进步!
自己bb那么多,希望大家见谅,我直接上图说话
第一步:先定义一个自定义控件SlipListView继承与ListView,在SlipListView中增加ListView的头布局与脚布局并实现其滑动监听的功能(代码虽然多,但是原理很简单,聪明的你们肯定可以看明白的。)
public class SlipListView extends ListView implements OnScrollListener {
private Context mContext;
private int startY;
private int moveY;
private int mHeaderViewHeight;
private View mHeaderview;
/**
* 下拉刷新
*/
private static final int downRefresh = 0;
/**
* 正在刷新
*/
private static final int refreshing = 1;
/**
* 释放刷新
*/
private static final int upRefresh = 2;
private int currentState = downRefresh;
private RotateAnimation mUpAnimation;
private RotateAnimation mDownAnimation;
private TextView tvTiTile;
private ProgressBar pbUp;
private ImageView ivRefresh;
private RefreshListener mListener;
private View mFootView; // 脚布局
private boolean isLoadingMore; // 是否正在加载更多
private int mFootViewHeight;
public SlipListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
public SlipListView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public SlipListView(Context context) {
super(context);
init();
}
/**
* 初始化界面,及动画
*/
private void init() {
mContext = getContext();
setOnScrollListener(this);
// 初始化头布局
initHeaderView();
// 初始化脚布局
initFootView();
initAnimation();
}
/**
* 初始化脚布局
*/
private void initFootView() {
mFootView = View.inflate(mContext, R.layout.layout_foot, null);
mFootView.measure(0, 0);
mFootViewHeight = mFootView.getMeasuredHeight();
addFooterView(mFootView);
}
/**
* 初始化动画
*/
private void initAnimation() {
mUpAnimation = new RotateAnimation(0, -180, Animation.RELATIVE_TO_SELF,
0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
mUpAnimation.setDuration(300);
mUpAnimation.setFillAfter(true);
mDownAnimation = new RotateAnimation(-180, -360,
Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,
0.5f);
mDownAnimation.setDuration(300);
mDownAnimation.setFillAfter(true);
}
/**
* 初始化头布局
*/
private void initHeaderView() {
mHeaderview = View.inflate(mContext, R.layout.layout_header, null);
ivRefresh = (ImageView) mHeaderview.findViewById(R.id.iv_refresh);
pbUp = (ProgressBar) mHeaderview.findViewById(R.id.pb_up);
tvTiTile = (TextView) mHeaderview.findViewById(R.id.tv_title);
mHeaderview.measure(0, 0);
mHeaderViewHeight = mHeaderview.getMeasuredHeight();
// 隐藏头布局
mHeaderview.setPadding(0, -mHeaderViewHeight, 0, 0);
addHeaderView(mHeaderview);
}
// 监听listview的滑动事件
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
startY = (int) ev.getY();
break;
case MotionEvent.ACTION_MOVE:
moveY = (int) ev.getY();
if (currentState == refreshing) {
return super.onTouchEvent(ev);
}
int valueY = moveY - startY;
if (valueY > 0 && getFirstVisiblePosition() == 0) {
int result = -mHeaderViewHeight + valueY;
mHeaderview.setPadding(0, result, 0, 0);
// 判断状态
if (result < 0 && currentState != downRefresh) {
currentState = downRefresh;
updateHeader();
} else if (result >= 0 && currentState != upRefresh) {
currentState = upRefresh;
updateHeader();
}
return true;
}
break;
case MotionEvent.ACTION_UP:
// 松开
if (currentState == downRefresh) {
mHeaderview.setPadding(0, -mHeaderViewHeight, 0, 0);
} else if (currentState == upRefresh) {
mHeaderview.setPadding(0, 0, 0, 0);
currentState = refreshing;
updateHeader();
}
break;
default:
break;
}
return super.onTouchEvent(ev);
}
/**
* 更改头布局的状态
*/
private void updateHeader() {
switch (currentState) {
case downRefresh:
ivRefresh.startAnimation(mDownAnimation);
tvTiTile.setText("下拉刷新");
break;
case refreshing:
// 正在刷新
ivRefresh.clearAnimation();
ivRefresh.setVisibility(View.INVISIBLE);
pbUp.setVisibility(View.VISIBLE);
tvTiTile.setText("正在刷新...");
// 利用回调增加listview中的内容
if (mListener != null) {
mListener.onRefresh();
}
break;
case upRefresh:
// 释放刷新
ivRefresh.startAnimation(mUpAnimation);
tvTiTile.setText("释放刷新");
break;
}
}
public interface RefreshListener {
/**
* 刷新数据
*/
void onRefresh();
/**
* 加载更多
*/
void onLoadMore();
}
/**
* 刷新头布局的回调方法
*/
public void setOnRefreshListener(RefreshListener mListener) {
this.mListener = mListener;
}
/**
* 刷新结束
*/
public void setOnRefreshFinah() {
if (isLoadingMore) {
// 加载更多
mFootView.setPadding(0, -mFootViewHeight, 0, 0);
isLoadingMore = false;
}
currentState = downRefresh;
mHeaderview.setPadding(0, -mHeaderViewHeight, 0, 0);
ivRefresh.setVisibility(View.VISIBLE);
pbUp.setVisibility(View.INVISIBLE);
tvTiTile.setText("下拉刷新");
}
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
if (isLoadingMore) {
return;
}
// 最新状态是空闲状态, 并且当前界面显示了所有数据的最后一条. 加载更多
if (scrollState == SCROLL_STATE_IDLE
&& getLastVisiblePosition() >= (getCount() - 1)) {
isLoadingMore = true;
mFootView.setPadding(0, 0, 0, 0);
setSelection(getCount());
if (mListener != null) {
mListener.onLoadMore();
}
}
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
// 滑动过程中
}
}
第二步:在布局文件中使用此自定义控件(比较简单)
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="${relativePackage}.${activityClass}" >
<com.lx.pulllistview.ui.SlipListView
android:id="@+id/slv_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
第三步:定义ListView的Item的布局(里面我将ProgressBar自定义了一个style,你们可以使用默认的)
<?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="horizontal" >
<FrameLayout
android:layout_width="60dp"
android:layout_height="50dp"
android:layout_marginBottom="5dp"
android:layout_marginLeft="5dp"
android:layout_marginTop="5dp" >
<ProgressBar
android:id="@+id/pb_up"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:indeterminateDrawable="@drawable/progressbar_shape" />
</FrameLayout>
<TextView
android:id="@+id/tv_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:gravity="center"
android:text="加载更多..."
android:textColor="#ff0000"
android:textSize="16dp" />
</LinearLayout>
第四步:在MainActivity中调用(不难喲,加油)
public class MainActivity extends Activity {
private static final int PULL_DOWN = 0;
private static final int LOAD_MORE = 1;
private SlipListView slView;
private Context mContext;
private List<String> mTestList;
private MyAdapter mAdapter;
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case PULL_DOWN:
mAdapter.notifyDataSetChanged();
slView.setOnRefreshFinah();
break;
case LOAD_MORE:
break;
}
super.handleMessage(msg);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mContext = this;
// 初始化UI
initUI();
// 初始化数据
initData();
initEvent();
// 初始化数据适配器
initAdpater();
}
/**
* 初始化事件
*/
private void initEvent() {
slView.setOnRefreshListener(new RefreshListener() {
@Override
public void onRefresh() {
new Thread(new Runnable() {
@Override
public void run() {
SystemClock.sleep(2000);
mTestList.add(0, "我是刷新出来的测试数据");
mHandler.sendEmptyMessage(PULL_DOWN);
}
}).start();
}
@Override
public void onLoadMore() {
new Thread(new Runnable() {
@Override
public void run() {
SystemClock.sleep(2000);
mTestList.add("我是加载出来的测试数据1");
mTestList.add("我是加载出来的测试数据2");
mHandler.sendEmptyMessage(PULL_DOWN);
}
}).start();
}
});
}
/**
* 初始化ListView的适配器
*/
private void initAdpater() {
mAdapter = new MyAdapter();
slView.setAdapter(mAdapter);
}
/**
* 初始化listview的测试数据
*/
private void initData() {
mTestList = new ArrayList<String>();
for (int i = 0; i < 30; i++) {
mTestList.add("我是ListView的测试数据");
}
}
/**
* 初始化UI
*/
private void initUI() {
slView = (SlipListView) findViewById(R.id.slv_view);
}
/**
* 自定义适配器
*/
class MyAdapter extends BaseAdapter {
@Override
public int getCount() {
return mTestList.size();
}
@Override
public String getItem(int position) {
return mTestList.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// 由于item的布局就一个TextView并且数量较少,因此不使用优化了
TextView textView = new TextView(mContext);
textView.setText(mTestList.get(position));
textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 18);
return textView;
}
}
}
你是不是想说终于结束了,嘿嘿!当你阅读到这里的时候,你的知识与忍耐力已经更上一层楼了。废话不多说了,如果您有疑问请咨询我,最后谢谢大家的观看,你们的支持是给我最大的努力!