android最基本的lsitvew实现下拉刷新,上拉加载更多的demo

       接着上次来讲,这次来动手写一下listview的下拉刷新功能和上拉加载更多功能。

       当然google在android4.0以上的API里面的提供了一个可以下拉加载更多的控件,这个小圆圈加载控件在豆瓣,知乎日报里面都有运用到,而我在下一篇博客也会提到。

       先来了解一下最基本的listview的的加载功能吧。

       首先是下拉刷新功能,我先说一下基本的思路。listveiw的面提供了一个addheader()方法,我们可以重写listview,然后用addheader方法加载我们自定义的加载布局。然后就是隐藏这个header,然后复写监听方法OnScrollListener()和OnTouch()方法,最后再提供一个接口方法来让用户实现加载数据。具体的我在代码里面都注释好了。

      再来说一下上拉加载,这个相比于下拉加载就简单多了,我们可以addfooter()方法添加布局,然后监听OnScrollListener就可以了,当最后一个可见的item等于总数量的item时,就可以加载数据了。具体在代码里面斗注释好了。


效果图:

技术分享

技术分享

先发布局文件:

<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="com.example.listview_pulltorefresh.MainActivity" >

    <com.example.listview_pulltorefresh.RefreshListview 
        android:id="@+id/listview"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        
    </com.example.listview_pulltorefresh.RefreshListview>
    
</RelativeLayout>

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="match_parent"
    android:orientation="vertical" >

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingBottom="10dp"
        android:paddingTop="10dp" >

        <LinearLayout
            android:id="@+id/layout"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:gravity="center"
            android:orientation="vertical" >

            <TextView
                android:id="@+id/tip"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="下拉刷新" />

            <TextView
                android:id="@+id/lastrefresh_time"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content" />
        </LinearLayout>

        <ImageView
            android:id="@+id/arrow"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="20dp"
            android:layout_toLeftOf="@id/layout"
            android:src="@drawable/pull_to_refresh_arrow" />
        <!-- android:indeterminateDrawable="@drawable/loading_anim" -->

        <ProgressBar
            android:id="@+id/progress"
            style="?android:attr/progressBarStyleSmall"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="20dp"
            android:layout_toLeftOf="@id/layout"
            android:visibility="gone" />
    </RelativeLayout>

</LinearLayout>


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="match_parent"
    android:orientation="vertical" >

    <LinearLayout
        android:id="@+id/load_footer"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:orientation="horizontal"
        android:paddingBottom="10dp"
        android:paddingTop="10dp" >

        <ProgressBar
            style="?android:attr/progressBarStyleSmall"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
        
        <TextView 
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="正在加载"/>
    </LinearLayout>

</LinearLayout>

listitem.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="50dp">

    <ImageView 
        android:id="@+id/image"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="10dp"
        android:src="@drawable/ic_launcher"/>

    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerVertical="true"
        android:layout_marginLeft="91dp"
        android:layout_toRightOf="@+id/image"
        android:text="数据列" />

</RelativeLayout>

自定义的listview方法:

package com.example.listview_pulltorefresh;

import java.sql.Date;
import java.text.SimpleDateFormat;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.RotateAnimation;
import android.widget.AbsListView;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;

public class RefreshListview extends ListView {
	private View header;// 顶部布局文件
	private View footer;// 底部布局
	private int headerHeight;// 顶部布局文件的高度
	private int firstVisibleItem;// 当前第一个可见item的位置
	private boolean isRemark;// 标记当前listviews是否最顶端摁下
	private int startY;// 开始的Y值

	private int mscrollState;// 当前listview的滚动状态
	private int state;// 当前状态
	private static final int NONE = 0;// 正常状态
	private static final int PULL = 1;// 下拉状态
	private static final int RELEASE = 2;// 松开状态
	private static final int REFRESHING = 3;// 刷新状态
	
	
	private int mtotalItemCount;//全部item的数量
	private int lastVisableItem;//最后一个可见的item
	private boolean isLoading=false;//是否正在加载

	public RefreshListview(Context context, AttributeSet attrs, int defStyleAttr) {
		super(context, attrs, defStyleAttr);
		// TODO Auto-generated constructor stub
		initView(context);
	}

	public RefreshListview(Context context, AttributeSet attrs) {
		super(context, attrs);
		// TODO Auto-generated constructor stub
		initView(context);
	}

	public RefreshListview(Context context) {
		super(context);
		// TODO Auto-generated constructor stub
		initView(context);
	}

	/**
	 * 添加顶部布局文件
	 * 
	 * @param context
	 */
	private void initView(Context context) {
		LayoutInflater inflater = LayoutInflater.from(context);
		footer = inflater.inflate(R.layout.footer_loading, null, false);
		header = inflater.inflate(R.layout.header_layout, null, false);
		measureView(header);
		headerHeight = header.getMeasuredHeight();
		Log.i("test", "headHeight:" + headerHeight);
		topPadding(-headerHeight);
		this.addHeaderView(header);
		//先设置底部隐藏
		footer.setVisibility(View.GONE);
		this.addFooterView(footer);
		this.setOnScrollListener(new OnScrollListener() {

			@Override
			public void onScrollStateChanged(AbsListView view, int scrollState) {
				// TODO Auto-generated method stub
				mscrollState = scrollState;
				
				//最后一个可见的item是总数量,并且当前滚动状态停止,就加载数据
				if (mtotalItemCount==lastVisableItem&&scrollState==OnScrollListener.SCROLL_STATE_IDLE) {
					if (!isLoading) {
						//加载数据
						isLoading=true;
						footer.setVisibility(VISIBLE);
						mListener2.onReflashMore();
						
					}
					
					
				}
			}

			/**
			 * firstvisebleitem第一个可见的位置
			 */
			@Override
			public void onScroll(AbsListView view, int firstVisibleItem,
					int visibleItemCount, int totalItemCount) {
				// TODO Auto-generated method stub
				lastVisableItem=firstVisibleItem+visibleItemCount;
				mtotalItemCount=totalItemCount;
			}

		});

		this.setOnTouchListener(new OnTouchListener() {

			@Override
			public boolean onTouch(View v, MotionEvent event) {
				// TODO Auto-generated method stub
				switch (event.getAction()) {
				case MotionEvent.ACTION_DOWN:
					if (firstVisibleItem == 0) {
						isRemark = true;
						startY = (int) event.getY();
					}
				case MotionEvent.ACTION_MOVE:
					onMove(event);

					break;
				case MotionEvent.ACTION_UP:
					if (state == RELEASE) {

						state = REFRESHING;
						reflashViewByState();
						mListener.onrReflash(); // 加载数据
						// 在外部调用reflashcomplete
					} else if (state == PULL) {
						state = NONE;
						isRemark = false;
						reflashViewByState();
					}
					break;

				default:
					break;
				}
				return false;
			}
		});
	}

	/**
	 * 通过view获取layoutparams,然后初始化lp, 要调用measure()方法来设置子view的宽高
	 * measure的方法参数有变化的是用MeasureSpec.makeMeasureSpec设置
	 * 没有变化的用getChildMeasureSpec()方法设置
	 * 
	 * @param view
	 */

	public void measureView(View view) {
		ViewGroup.LayoutParams lParams = view.getLayoutParams();
		if (lParams == null) {
			lParams = new ViewGroup.LayoutParams(
					ViewGroup.LayoutParams.MATCH_PARENT,
					ViewGroup.LayoutParams.WRAP_CONTENT);
			// spec左右边距,padding内边距
			int width = ViewGroup.getChildMeasureSpec(0, 0, lParams.width);
			int height;
			int tempHeight = lParams.height;
			if (tempHeight > 0) {
				// 填充用exactly
				height = MeasureSpec.makeMeasureSpec(tempHeight,
						MeasureSpec.EXACTLY);

			} else {
				// 意思就是<=0时,则告诉父布局子view高度填充0
				height = MeasureSpec
						.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
			}
			view.measure(width, height);
		}

	}

	/**
	 * 设置header布局的上边距
	 * 
	 * @param topPadding
	 */
	public void topPadding(int topPadding) {
		header.setPadding(header.getPaddingLeft(), topPadding,
				header.getPaddingRight(), header.getPaddingBottom());
		header.invalidate();
	}

	/**
	 * 判断移动过程中的操作
	 * 
	 * @param event
	 */
	private void onMove(MotionEvent event) {
		// TODO Auto-generated method stub
		if (!isRemark) {
			return;
		}
		int tempY = (int) event.getY();// 获取当前y
		int space = tempY - startY;// 显示的高度
		int topPadding = space - headerHeight;// 因为是要用负值,所以减去高度
		switch (state) {
		case NONE:
			if (space > 0) {
				state = PULL;
				reflashViewByState();
			}

			break;
		case PULL:
			topPadding(topPadding);
			// 大于heigh+30且在滑动时,则是可以刷新
			if (space > headerHeight + 30
					&& mscrollState == OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
				state = RELEASE;
				reflashViewByState();
			}

			break;

		case RELEASE:
			topPadding(topPadding);
			// 为释放状态时,则可以回到下拉状态
			if (space < headerHeight + 30) {
				state = PULL;
				reflashViewByState();
			} else if (space <= 0) {
				state = NONE;
				isRemark = false;
				reflashViewByState();
			}

			break;

		case REFRESHING:

			break;

		default:
			break;
		}
	}

	/**
	 * 改变下拉过程中的header布局中的控件的内容
	 */
	public void reflashViewByState() {
		TextView tip = (TextView) header.findViewById(R.id.tip);
		ImageView arrow = (ImageView) header.findViewById(R.id.arrow);
		ProgressBar progressBar = (ProgressBar) header
				.findViewById(R.id.progress);
		// RotateAnimation旋转动画,旋转的角度,相对与自己,中心位置
		RotateAnimation animation = new RotateAnimation(0, 180,
				RotateAnimation.RELATIVE_TO_SELF, 0.5f,
				RotateAnimation.RELATIVE_TO_SELF, 0.5f);
		animation.setDuration(500);// 时间间隔
		animation.setFillAfter(true);// 保存状态
		RotateAnimation animation2 = new RotateAnimation(180, 0,
				RotateAnimation.RELATIVE_TO_SELF, 0.5f,
				RotateAnimation.RELATIVE_TO_SELF, 0.5f);
		animation2.setDuration(500);// 时间间隔
		animation2.setFillAfter(true);// 保存状态
		switch (state) {
		case NONE:
			arrow.clearAnimation();
			topPadding(-headerHeight);
			break;
		case PULL:
			arrow.setVisibility(View.VISIBLE);
			progressBar.setVisibility(GONE);
			arrow.clearAnimation();
			arrow.setAnimation(animation2);
			tip.setText("下拉可以刷新");
			break;
		case RELEASE:
			arrow.setVisibility(View.VISIBLE);
			progressBar.setVisibility(GONE);
			arrow.clearAnimation();
			arrow.setAnimation(animation);
			tip.setText("松开可以刷新");
			break;
		case REFRESHING:
			topPadding(50);
			arrow.clearAnimation();
			arrow.setVisibility(View.GONE);
			progressBar.setVisibility(VISIBLE);
			tip.setText("正在刷新");
			break;

		default:
			break;
		}
	}

	/**
	 * 获取完数据
	 */
	public void reflashComplete() {
		state = NONE;
		isRemark = false;
		reflashViewByState();
		TextView lastTimeReflash = (TextView) header
				.findViewById(R.id.lastrefresh_time);
		SimpleDateFormat format = new SimpleDateFormat("yyyy年MM月dd日 hh:mm:ss");
		Date date = new Date(System.currentTimeMillis());
		String timeString = format.format(date);
		lastTimeReflash.setText(timeString);
	}
	
	/**
	 * 底部加载完毕
	 */
	public void reflashFooterComplete() {
		isLoading=false;
		footer.setVisibility(GONE);
	}

	/**
	 * 刷新数据接口
	 * 
	 * @author nickming
	 *
	 */
	public interface OnReflashListener {
		public void onrReflash();
	}

	public OnReflashListener mListener;// 刷新数据的接口

	public void setOnReflashListener(OnReflashListener listener) {
		mListener = listener;
	}
	
	
	/**
	 * 加载更多接口
	 * @author nickming
	 *
	 */
	public interface OnReflashMoreListener{
		public void onReflashMore();
	}
	
	public OnReflashMoreListener mListener2;
	
	public void  setOnReflashMoreListener(OnReflashMoreListener listener) {
		mListener2=listener;
	}

}


MainActivity.class

package com.example.listview_pulltorefresh;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;

import com.example.listview_pulltorefresh.RefreshListview.OnReflashListener;
import com.example.listview_pulltorefresh.RefreshListview.OnReflashMoreListener;

public class MainActivity extends Activity {
	private RefreshListview mlistview;
	MyAdapter adapter;
	List<DataBean> mdata;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		mlistview = (RefreshListview) findViewById(R.id.listview);
		mdata = new ArrayList<DataBean>();
		for (int i = 0; i < 20; i++) {
			DataBean dataBean = new DataBean();
			dataBean.setTextString("数据列:" + i);
			mdata.add(dataBean);
		}
		adapter = new MyAdapter(this, mdata);
		mlistview.setAdapter(adapter);
		mlistview.setOnReflashListener(new OnReflashListener() {

			@Override
			public void onrReflash() {
				// TODO Auto-generated method stub
				// 模拟网络延时
				Handler mHandler = new Handler();
				mHandler.postDelayed(new Runnable() {

					@Override
					public void run() {
						// TODO Auto-generated method stub
						// 获取最新数据
						addHeaderData();
						// 通知布局显示
						adapter.notifyDataSetChanged();
						// listview刷新
						mlistview.reflashComplete();
					}
				}, 3000);

			}
		});
		
		mlistview.setOnReflashMoreListener(new OnReflashMoreListener() {
			
			@Override
			public void onReflashMore() {
				// TODO Auto-generated method stub
				Handler mHandler=new Handler();
				mHandler.postDelayed(new Runnable() {
					
					@Override
					public void run() {
						// TODO Auto-generated method stub
						addFooterData();
						adapter.notifyDataSetChanged();
						mlistview.reflashFooterComplete();
						
					}
				}, 2000);
			}
		});
	}

	public void addHeaderData() {
		for (int i = 0; i < 10; i++) {
			DataBean dataBean = new DataBean();
			String nameString = UUID.randomUUID().toString();
			dataBean.setTextString("最新数据:" + nameString);
			mdata.add(0, dataBean);// 放在最前面
		}
	}
	
	public void addFooterData() {
		for (int i = 0; i < 10; i++) {
			DataBean dataBean = new DataBean();
			String nameString = UUID.randomUUID().toString();
			dataBean.setTextString("最新数据:" + nameString);
			mdata.add( dataBean);// 放在最后面
		}
	}
}

adapter

package com.example.listview_pulltorefresh;

import java.util.List;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;

public class MyAdapter extends BaseAdapter {

	private Context mcontext;
	private List<DataBean> mData;
	LayoutInflater inflater;

	public MyAdapter(Context context, List<DataBean> mData) {
		super();
		this.mcontext = context;
		this.mData = mData;
		inflater=LayoutInflater.from(context);
	}

	@Override
	public int getCount() {
		// TODO Auto-generated method stub
		return mData.size();
	}

	@Override
	public Object getItem(int position) {
		// TODO Auto-generated method stub
		return mData.get(position);
	}

	@Override
	public long getItemId(int position) {
		// TODO Auto-generated method stub
		return position;
	}

	@Override
	public View getView(int position, View convertView, ViewGroup parent) {
		// TODO Auto-generated method stub
		viewHolder holder=null;
		if (convertView == null) {
			convertView = inflater.inflate(R.layout.listitem, parent,false);
			holder = new viewHolder();
			holder.textView = (TextView) convertView
					.findViewById(R.id.textView1);
			convertView.setTag(holder);

		}else {
			holder=(viewHolder) convertView.getTag();
		}
		holder.textView.setText(mData.get(position).getTextString());

		return convertView;
	}

	class viewHolder {
		TextView textView;
	}

}

DataBean.class

package com.example.listview_pulltorefresh;

import android.widget.TextView;

public class DataBean {
	
	String textString;
	
	public DataBean() {
		// TODO Auto-generated constructor stub
	}

	public DataBean(String textString) {
		super();
		this.textString = textString;
	}

	public String getTextString() {
		return textString;
	}

	public void setTextString(String textString) {
		this.textString = textString;
	}
	
	 
	
	

}

基本上没什么问题了,其实实现还是很简单的,不过就是要多多练习。


郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。