ListView 下拉刷新 加载更多

来源:互联网 发布:单片机编程基础知识 编辑:程序博客网 时间:2024/05/01 06:29

1、首先继承ListView,利用addHeaderView和addFooterView对其添加头布局和脚布局,在根据View的setPadding(left, top, right, bottom)方法设置它的top属性,当top为负数时就在慢慢的隐藏布局。

2、获取头布局和脚布局的高度:由于view在onMeasure之后才能获取到它的高度,因此如果想在其之前获取高度可以调用measure(0, 0)进行测量,之后调用getMeasuredHeight就可以获取它的高度。

3、设置动画:这里使用旋转动画RotateAnimation,由于是逆时针在旋转,因此它的角度小于0。分别是0到-180和-180到-360。在下一次调用动画之前,可能上一次的动画没有结束,所以需要调用clearAnimation清除动画。

4、计算手指滑动的距离:需要重写onTouchEvent事件,在MotionEvent.ACTION_DOWN时记录下按下的坐标,然后与在移动时MotionEvent.ACTION_MOVE的坐标相减就得到移动的距离。

5、判断到达顶部和底部:需要实现OnScrollListener接口,需要重写下面两个方法

@Overridepublic void onScrollStateChanged(AbsListView view, int scrollState) {if(scrollState == SCROLL_STATE_IDLE || scrollState == SCROLL_STATE_FLING) {// 判断当前是否已经到了底部if(isScrollToBottom && !isLoadingMore) {isLoadingMore = true;}}}/** * 当滚动时调用 */@Overridepublic void onScroll(AbsListView view, int firstVisibleItem,int visibleItemCount, int totalItemCount) {firstVisibleItemPosition = firstVisibleItem; //firstVisibleItem等于0表示到达顶部 if(getLastVisiblePosition() == (totalItemCount - 1)) {//最后一个可见的位置和总共的条目减一相等表示到达底部isScrollToBottom = true;//是否到达底部} else {isScrollToBottom = false;}}

6、设置回调接口:定义一个接口 ,引入改接口的对象,在需要调用的地方进行调用接口里面的方法就可以。

public class RefreshListView extends ListView implements OnScrollListener {private static final String TAG = "RefreshListView";private int firstVisibleItemPosition;// 屏幕显示在第一个的item的索引private int downY;// 按下时y轴的偏移量private int headerViewHeight;// 头布局的高度private View headerView;// 头布局的对象private final int DOWN_PULL_REFRESH = 0;// 下拉刷新状态private final int RELEASE_REFRESH = 1;// 松开刷新private final int REFRESHING = 2;// 正在刷新中private int currentState = DOWN_PULL_REFRESH;// 头布局的状态: 默认为下拉刷新状态private Animation upAnimation;// 向上旋转的动画private Animation downAnimation; // 向下旋转的动画private ImageView ivArrow;// 头布局的剪头private ProgressBar mProgressBar; // 头布局的进度条private TextView tvState; // 头布局的状态private TextView tvLastUpdateTime; // 头布局的最后更新时间private OnRefreshListener mOnRefershListener;private boolean isScrollToBottom;// 是否滑动到底部private View footerView;// 脚布局的对象private int footerViewHeight; // 脚布局的高度private boolean isLoadingMore = false;// 是否正在加载更多中public RefreshListView(Context context, AttributeSet attrs) {super(context, attrs);initHeaderView();initFooterView();this.setOnScrollListener(this);}/** * 初始化脚布局 */private void initFooterView() {footerView = View.inflate(getContext(), R.layout.listview_footer, null);footerView.measure(0, 0);footerViewHeight = footerView.getMeasuredHeight();Log.i(TAG, "脚布局的高度: " + footerViewHeight);footerView.setPadding(0, -footerViewHeight, 0, 0);this.addFooterView(footerView);}/** * 初始化头布局 */private void initHeaderView() {headerView = View.inflate(getContext(), R.layout.listview_header, null);ivArrow = (ImageView) headerView.findViewById(R.id.iv_listview_header_arrow);mProgressBar = (ProgressBar) headerView.findViewById(R.id.pb_listview_header);tvState = (TextView) headerView.findViewById(R.id.tv_listview_header_state);tvLastUpdateTime = (TextView) headerView.findViewById(R.id.tv_listview_header_last_update_time);// 设置最后刷新时间tvLastUpdateTime.setText("最后刷新时间: " + getLastUpdateTime());headerView.measure(0, 0);// 系统会帮我们测量出headerView的高度headerViewHeight = headerView.getMeasuredHeight();Log.i(TAG, "测量后的高度: " + headerViewHeight);headerView.setPadding(0, -headerViewHeight, 0, 0); this.addHeaderView(headerView);// 向ListView的顶部添加一个view对象initAnimation();}/** * 获得系统的最新时间 * @return */private String getLastUpdateTime() {SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");return sdf.format(System.currentTimeMillis());}/** * 初始化动画 */private void initAnimation() {upAnimation = new RotateAnimation(0f, -180f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);upAnimation.setDuration(500);upAnimation.setFillAfter(true);// 动画结束后, 停留在结束的位置上downAnimation = new RotateAnimation(-180f, -360f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);downAnimation.setDuration(500);downAnimation.setFillAfter(true);// 动画结束后, 停留在结束的位置上}@Overridepublic boolean onTouchEvent(MotionEvent ev) {switch (ev.getAction()) {case MotionEvent.ACTION_DOWN:downY = (int) ev.getY();break;case MotionEvent.ACTION_MOVE:int moveY = (int) ev.getY();// 移动中的y - 按下的y = 间距.int diff = (moveY - downY) / 2;// -头布局的高度 + 间距 = paddingTopint paddingTop = -headerViewHeight + diff;// 如果: -头布局的高度  > paddingTop的值    执行super.onTouchEvent(ev);if(firstVisibleItemPosition == 0&& -headerViewHeight < paddingTop) {//Log.i(TAG, "当前在顶部滑动");if(paddingTop > 0 && currentState == DOWN_PULL_REFRESH) {// 完全显示了. 并且当前的状态是下拉刷新状态Log.i(TAG, "松开刷新");currentState = RELEASE_REFRESH;refreshHeaderView();} else if(paddingTop < 0 && currentState == RELEASE_REFRESH) {// 没有显示完全Log.i(TAG, "下拉刷新");currentState = DOWN_PULL_REFRESH;refreshHeaderView();}// 下拉头布局headerView.setPadding(0, paddingTop, 0, 0);return true;}break;case MotionEvent.ACTION_UP:// 判断当前的状态是松开刷新还是下拉刷新if(currentState == RELEASE_REFRESH) {Log.i(TAG, "刷新数据.");// 把头布局设置为完全显示状态headerView.setPadding(0, 0, 0, 0);// 进入到正在刷新中状态currentState = REFRESHING;refreshHeaderView();if(mOnRefershListener != null) {mOnRefershListener.onDownPullRefresh();// 调用使用者的监听方法}} else if(currentState == DOWN_PULL_REFRESH) {// 隐藏头布局headerView.setPadding(0, -headerViewHeight, 0, 0);}break;default:break;}return super.onTouchEvent(ev);}/** * 根据currentState刷新头布局的状态 */private void refreshHeaderView() {switch (currentState) {case DOWN_PULL_REFRESH:// 下拉刷新状态tvState.setText("下拉刷新");ivArrow.startAnimation(downAnimation);// 执行向下旋转break;case RELEASE_REFRESH:// 松开刷新状态tvState.setText("松开刷新");ivArrow.startAnimation(upAnimation);// 执行向上旋转break;case REFRESHING:// 正在刷新中状态ctrl + shift + X 大写  Y小写ivArrow.clearAnimation();ivArrow.setVisibility(View.GONE);mProgressBar.setVisibility(View.VISIBLE);tvState.setText("正在刷新中...");break;default:break;}}/** * 当滚动状态改变时回调 *  * SCROLL_STATE_IDLE  停滞状态 * SCROLL_STATE_TOUCH_SCROLL 按住时滚动的状态 * SCROLL_STATE_FLING 猛地一滑 */@Overridepublic void onScrollStateChanged(AbsListView view, int scrollState) {if(scrollState == SCROLL_STATE_IDLE || scrollState == SCROLL_STATE_FLING) {// 判断当前是否已经到了底部if(isScrollToBottom && !isLoadingMore) {isLoadingMore = true;// 当前到底部Log.i(TAG, "加载更多数据");footerView.setPadding(0, 0, 0, 0);this.setSelection(this.getCount());if(mOnRefershListener != null) {mOnRefershListener.onLoadingMore();}}}}/** * 当滚动时调用 * firstVisibleItem 当前屏幕显示在顶部的item的position * visibleItemCount 当前屏幕显示了多少个条目的总数. * totalItemCountListView的总条目的总数 *  * 20 + 10 = 30; *  */@Overridepublic void onScroll(AbsListView view, int firstVisibleItem,int visibleItemCount, int totalItemCount) {firstVisibleItemPosition = firstVisibleItem;//Log.i(TAG, "lastPosition: " + getLastVisiblePosition() + ", count: " + totalItemCount);if(getLastVisiblePosition() == (totalItemCount - 1)) {isScrollToBottom = true;} else {isScrollToBottom = false;}}/** * 设置刷新监听事件 * @param listener */public void setOnRefreshListener(OnRefreshListener listener) {mOnRefershListener = listener;}/** * 隐藏头布局 */public void hideHeaderView() {headerView.setPadding(0, -headerViewHeight, 0, 0);ivArrow.setVisibility(View.VISIBLE);mProgressBar.setVisibility(View.GONE);tvState.setText("下拉刷新");tvLastUpdateTime.setText("最后刷新时间: " + getLastUpdateTime());currentState = DOWN_PULL_REFRESH;}public void hideFooterView() {footerView.setPadding(0, -footerViewHeight, 0, 0);isLoadingMore = false;}}

public class MainActivity extends Activity implements OnRefreshListener {    protected static final String TAG = "MainActivity";private List<String> textList;private MyAdapter adapter;private RefreshListView mListView;@Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);                mListView = (RefreshListView) findViewById(R.id.refreshlistview);                textList = new ArrayList<String>();                for (int i = 0; i < 30; i++) {textList.add("ListView数据项 "+ i);}                adapter = new MyAdapter();mListView.setAdapter(adapter);                mListView.setOnRefreshListener(this);    }    class MyAdapter extends BaseAdapter {@Overridepublic int getCount() {return textList.size();}@Overridepublic Object getItem(int arg0) {// TODO Auto-generated method stubreturn null;}@Overridepublic long getItemId(int position) {// TODO Auto-generated method stubreturn 0;}@Overridepublic View getView(int position, View convertView, ViewGroup parent) {TextView tv = new TextView(MainActivity.this);tv.setText(textList.get(position));tv.setTextSize(18);tv.setTextColor(Color.BLACK);return tv;}        }@Overridepublic void onDownPullRefresh() {new AsyncTask<String, Integer, Void>() {@Overrideprotected Void doInBackground(String... params) {Log.i(TAG, "doInBackground");SystemClock.sleep(2000);textList.add(0, "这是下拉刷新出来的数据");return null;}@Overrideprotected void onPostExecute(Void result) {Log.i(TAG, "onPostExecute");adapter.notifyDataSetChanged();// 锟斤拷锟铰斤拷锟斤拷mListView.hideHeaderView();}}.execute(new String[]{});// 锟斤拷始执锟斤拷锟届步锟斤拷锟斤拷}@Overridepublic void onLoadingMore() {new AsyncTask<Void, Void, Void>() {@Overrideprotected Void doInBackground(Void... params) {SystemClock.sleep(5000);textList.add("这是加载更多的数据。。。");textList.add("这是加载更多的数据。。。");textList.add("这是加载更多的数据。。。");textList.add("这是加载更多的数据。。。");textList.add("这是加载更多的数据。。。");textList.add("这是加载更多的数据。。。");return null;}@Overrideprotected void onPostExecute(Void result) {adapter.notifyDataSetChanged();// 锟斤拷锟狡脚诧拷锟斤拷锟斤拷锟斤拷mListView.hideFooterView();}}.execute(new Void[]{});}}

/** * @author andong * 自动刷新ListView的刷新监听事件 */public interface OnRefreshListener {/** * 下拉刷新.加载完成后需要把头布局隐藏 */void onDownPullRefresh();/** * 加载更多, 加载完成后需要把脚布局隐藏 */void onLoadingMore();}


0 0
原创粉丝点击