Android ——ListView以及scrollView下拉刷新控件的实现

最近闲着对部分bug做了修改,好久没写java代码手生。

修改部分bug,提供listView和scrollView两个都实现的实例,算是完整版。

由于CSDN对资源的不能修改删除特性,把新代码部分移至github。

下面blog部分废话可以直接跳过到下载。

装了git的可以点击右边的clone,没有的直接下载zip文件。

下载地址:点击下载



-----修改于2013-09-10



Android下拉刷新,在目前好多应用被使用到。

比如微博,下拉刷新更多数据。

一般我们在运用的ListView,本身就实现了下拉获取更多数据。只是这个下拉刷新的操作时在listView拉到底端的监听。


对于ListView刷新,我们可以分为两种情况:

1.获取更多的数据,按服务器数据库时间顺序存储入情况,此刻我们是获取是显示在我们应用中的数据更早前的数据,这也是最常见的情况。

比如(微博获取更多信息,就是获取更多更早前的信息,然后动态的添加到已有的数据的下方);

2.获取更多的最新的数据,其实还是一种获取更多的操作方式。但是这里主要考虑到用户的操作习惯了。一般,用户的操作习惯分这么两种

第一种,获取下一页,第二种,类似于网页的F5刷新,停留在当前页面的刷新。

ListView刷新其实类似于网页。如果没用下拉刷新,那么用户得将ListView拖拉到最后(当然也可以是在界面顶端添加一个刷新按钮控件,但是,对于手机这样界面不是很大,这样的设计其实是不应太多的。),如果数据太多,那么用户要下拉到很下面才能执行刷新。而对于大多数用户习惯,获取更多的最新资讯后,希望他添加的时候是在界面最上面的显眼处的。也就是,用户还是喜欢的是懒操作,在同一个可显示界面完成所有操作。那么,下拉刷新是一个不错的设计。

效果图:正常状态


     下拉刷新:

基本效果就是这样。

自定义控件代码

/**
 * 刷新控制view
 * 
 * @author Nono
 * 
 */
public class RefreshableView extends LinearLayout {

	private static final String TAG = "LILITH";
	private Scroller scroller;
	private View refreshView;
	private ImageView refreshIndicatorView;
	private int refreshTargetTop = -60;
	private ProgressBar bar;
	private TextView downTextView;
	private TextView timeTextView;

	private RefreshListener refreshListener;

	private String downTextString;
	private String releaseTextString;

	private Long refreshTime = null;
	private int lastX;
	private int lastY;
	// 拉动标记
	private boolean isDragging = false;
	// 是否可刷新标记
	private boolean isRefreshEnabled = true;
	// 在刷新中标记
	private boolean isRefreshing = false;
	
	
	private Context mContext;
	public RefreshableView(Context context) {
		super(context);
		mContext = context;
		
	}
	public RefreshableView(Context context, AttributeSet attrs) {
		super(context, attrs);
		mContext = context;
		init();
		
	}
	private void init() {
		// TODO Auto-generated method stub
		//滑动对象,
		scroller = new Scroller(mContext);
		
		//刷新视图顶端的的view
		 refreshView = LayoutInflater.from(mContext).inflate(R.layout.refresh_top_item, null);
		//指示器view
		 refreshIndicatorView = (ImageView) refreshView.findViewById(R.id.indicator);
		//刷新bar
		bar = (ProgressBar) refreshView.findViewById(R.id.progress);
		//下拉显示text
		 downTextView = (TextView) refreshView.findViewById(R.id.refresh_hint);
		//下来显示时间
		 timeTextView = (TextView) refreshView.findViewById(R.id.refresh_time);
		
		LayoutParams lp = new LinearLayout.LayoutParams(LayoutParams.FILL_PARENT, -refreshTargetTop);
		lp.topMargin = refreshTargetTop;
		lp.gravity = Gravity.CENTER;
		addView(refreshView, lp);
		downTextString = mContext.getResources().getString(R.string.refresh_down_text);
		releaseTextString = mContext.getResources().getString(R.string.refresh_release_text);	
	}




	/**
	 * 刷新
	 * @param time
	 */
	private void setRefreshText(String time) {
		// TODO Auto-generated method stub
		//timeTextView.setText(time);
	}


	
	@Override
	public boolean onTouchEvent(MotionEvent event) {
		
		int y= (int) event.getRawY();
		
		
		switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN:
			//记录下y坐标
			lastY = y;
			break;

		case MotionEvent.ACTION_MOVE:
			Log.i(TAG, "ACTION_MOVE");
			//y移动坐标
			int m = y - lastY;
			if(((m < 6) && (m > -1)) || (!isDragging )){
				 doMovement(m);
			}
			//记录下此刻y坐标
			this.lastY = y;
			break;
			
		case MotionEvent.ACTION_UP:
			Log.i(TAG, "ACTION_UP");
			
			fling();
			
			break;
		}
		return true;
	}


	/**
	 * up事件处理
	 */
	private void fling() {
		// TODO Auto-generated method stub
		LinearLayout.LayoutParams lp = (LayoutParams) refreshView.getLayoutParams();
		Log.i(TAG, "fling()" + lp.topMargin);
		if(lp.topMargin > 0){//拉到了触发可刷新事件
			refresh();	
		}else{
			returnInitState();
		}
	}
	

	
	private void returnInitState() {
		// TODO Auto-generated method stub
		 LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)this.refreshView.getLayoutParams();
		 int i = lp.topMargin;
		 scroller.startScroll(0, i, 0, refreshTargetTop);
		 invalidate();
	}
	private void refresh() {
		// TODO Auto-generated method stub
		 LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)this.refreshView.getLayoutParams();
		 int i = lp.topMargin;
		 refreshIndicatorView.setVisibility(View.GONE);
		 bar.setVisibility(View.VISIBLE);
		 timeTextView.setVisibility(View.GONE);
		 downTextView.setVisibility(View.GONE);
		 scroller.startScroll(0, i, 0, 0-i);
		 invalidate();
		 if(refreshListener !=null){
			 refreshListener.onRefresh(this);
			 isRefreshing = true;
		 }
	}
	
	/**
	 * 
	 */
	@Override
	public void computeScroll() {
		// TODO Auto-generated method stub
		if(scroller.computeScrollOffset()){
			int i = this.scroller.getCurrY();
		      LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)this.refreshView.getLayoutParams();
		      int k = Math.max(i, refreshTargetTop);
		      lp.topMargin = k;
		      this.refreshView.setLayoutParams(lp);
		      this.refreshView.invalidate();
		      invalidate();
		}
	}
	/**
	 * 下拉move事件处理
	 * @param moveY
	 */
	private void doMovement(int moveY) {
		// TODO Auto-generated method stub
		LinearLayout.LayoutParams lp = (LayoutParams) refreshView.getLayoutParams();
		if(moveY > 0){
			//获取view的上边距
			float f1 =lp.topMargin;
			float f2 = moveY * 0.3F;
			int i = (int)(f1+f2);
			//修改上边距
			lp.topMargin = i;
			//修改后刷新
			refreshView.setLayoutParams(lp);
			refreshView.invalidate();
			invalidate();
		}
		timeTextView.setVisibility(View.VISIBLE);
		if(refreshTime!= null){
			setRefreshTime(refreshTime);
		}
		downTextView.setVisibility(View.VISIBLE);
		
		refreshIndicatorView.setVisibility(View.VISIBLE);
		bar.setVisibility(View.GONE);
		if(lp.topMargin >  0){
			downTextView.setText(R.string.refresh_release_text);
			refreshIndicatorView.setImageResource(R.drawable.refresh_arrow_up);
		}else{
			downTextView.setText(R.string.refresh_down_text);
			refreshIndicatorView.setImageResource(R.drawable.refresh_arrow_down);
		}
			
	}

	public void setRefreshEnabled(boolean b) {
		this.isRefreshEnabled = b;
	}

	public void setRefreshListener(RefreshListener listener) {
		this.refreshListener = listener;
	}

	/**
	 * 刷新时间
	 * @param refreshTime2
	 */
	private void setRefreshTime(Long time) {
		// TODO Auto-generated method stub
		
	}

	/**
	 * 结束刷新事件
	 */
	public void finishRefresh(){
		Log.i(TAG, "执行了=====finishRefresh");
		 LinearLayout.LayoutParams lp= (LinearLayout.LayoutParams)this.refreshView.getLayoutParams();
		    int i = lp.topMargin;
		    refreshIndicatorView.setVisibility(View.VISIBLE);
		    timeTextView.setVisibility(View.VISIBLE);
		    scroller.startScroll(0, i, 0, refreshTargetTop);
		    invalidate();
		    isRefreshing = false;  
	}

	
	/*该方法一般和ontouchEvent 一起用
	 * (non-Javadoc)
	 * @see android.view.ViewGroup#onInterceptTouchEvent(android.view.MotionEvent)
	 */
	@Override
	public boolean onInterceptTouchEvent(MotionEvent e) {
		// TODO Auto-generated method stub
		int action = e.getAction();
		int y= (int) e.getRawY();
		switch (action) {
		case MotionEvent.ACTION_DOWN:
			lastY = y;
			break;

		case MotionEvent.ACTION_MOVE:
			//y移动坐标
			int m = y - lastY;

			//记录下此刻y坐标
			this.lastY = y;
		     if(m > 6 &&  canScroll()){
		    	 return true;
		     }
			break;
		case MotionEvent.ACTION_UP:
			
			break;
			
	case MotionEvent.ACTION_CANCEL:
			
			break;
		}
		return false;
	}
	private boolean canScroll() {
		// TODO Auto-generated method stub
		View childView;
		if(getChildCount()>1){
			childView = this.getChildAt(1);
			if(childView instanceof ListView){
				int top =((ListView)childView).getChildAt(0).getTop(); 
				int pad =((ListView)childView).getListPaddingTop(); 
				if((Math.abs(top-pad)) < 3&&
						((ListView) childView).getFirstVisiblePosition() == 0){
					return true;
				}else{
					return false;
				}
			}else if(childView instanceof ScrollView){
				if(((ScrollView)childView).getScrollY() == 0){
					return true;
				}else{
					return false;
				}
			}
			
		}
		return false;
	}
	/**
	 * 刷新监听接口
	 * @author Nono
	 *
	 */
	public interface RefreshListener{
		public void onRefresh(RefreshableView view);
	}
}

此控件自定义实现一个线性布局,内部包含一个第一个子控件,刷新显示的View。

因为对于ListView下拉刷新的例子网上挺多的,上次我朋友也做,说特么弄了一个礼拜发现一个问题,listview里面条目太少时,刷新的view就会显示出来。

我没具体看过那个代码,也不知道到底什么情况。

自个定义的稍微用了点小技巧。即,我将刷新view的topMargin设置为了该view的高度的负数,那么,他就刚好隐藏起来了。

对于上面那个对于条目太少而造成刷新的view显示的bug有所解决。

@Override
	public boolean onTouchEvent(MotionEvent event) {
		
		int y= (int) event.getRawY();
		
		
		switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN:
			//记录下y坐标
			lastY = y;
			break;

		case MotionEvent.ACTION_MOVE:
			Log.i(TAG, "ACTION_MOVE");
			//y移动坐标
			int m = y - lastY;
			if(((m < 6) && (m > -1)) || (!isDragging )){
				 doMovement(m);
			}
			//记录下此刻y坐标
			this.lastY = y;
			break;
			
		case MotionEvent.ACTION_UP:
			Log.i(TAG, "ACTION_UP");
			
			fling();
			
			break;
		}
		return true;
	}


	/*该方法一般和ontouchEvent 一起用
	 * (non-Javadoc)
	 * @see android.view.ViewGroup#onInterceptTouchEvent(android.view.MotionEvent)
	 */
	@Override
	public boolean onInterceptTouchEvent(MotionEvent e) {
		// TODO Auto-generated method stub
		int action = e.getAction();
		int y= (int) e.getRawY();
		switch (action) {
		case MotionEvent.ACTION_DOWN:
			lastY = y;
			break;

		case MotionEvent.ACTION_MOVE:
			//y移动坐标
			int m = y - lastY;

			//记录下此刻y坐标
			this.lastY = y;
		     if(m > 6 &&  canScroll()){
		    	 return true;
		     }
			break;
		case MotionEvent.ACTION_UP:
			
			break;
			
	case MotionEvent.ACTION_CANCEL:
			
			break;
		}
		return false;
	}
评论 23
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值