分享一个可下拉刷新的ScrollView

原理:就是动态改变ScrollView header的margin实现

主要的代码:

public class RefreshScrollView extends ScrollView {

	private final static int SCROLL_DURATION = 400;
	private final static float OFFSET_RADIO = 1.8f;
	private int headerHeight = 0;
	private boolean enableRefresh = true;
	private boolean refreshing = false;
	private int lastY;
	private Scroller scroller = null;
	private OnRefreshScrollViewListener listener = null;
	private LinearLayout scrollContainer = null;
	private ScrollViewHeader headerView = null;

	public RefreshScrollView(Context context) {
		super(context);
		// TODO Auto-generated constructor stub
		if (!isInEditMode()) {
			initView(context);
		}
	}

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

	public RefreshScrollView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		// TODO Auto-generated constructor stub
		if (!isInEditMode()) {
			initView(context);
		}
	}

	/**
	 * 初始化view
	 */
	private void initView(Context context) {
		scroller = new Scroller(context);
		headerView = new ScrollViewHeader(context);
		LinearLayout.LayoutParams headerViewParams = new LinearLayout.LayoutParams(
				LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
		//scrollview只允许嵌套一个子布局
		scrollContainer = new LinearLayout(context);
		scrollContainer.addView(headerView, headerViewParams);
		scrollContainer.setOrientation(LinearLayout.VERTICAL);
		addView(scrollContainer);
		//提前获取headerView的高度
		headerView.getViewTreeObserver().addOnGlobalLayoutListener(
				new OnGlobalLayoutListener() {

					@SuppressWarnings("deprecation")
					@Override
					public void onGlobalLayout() {
						// TODO Auto-generated method stub
						headerHeight = headerView.getHeight();
						headerView.updateMargin(-headerHeight);
						headerView.getViewTreeObserver()
								.removeGlobalOnLayoutListener(this);
					}
				});
	}

	/**
	 * 设置内容区域
	 * 
	 * @param context
	 * @param resId
	 */
	public void setupContainer(Context context, View containerView) {
		scrollContainer.addView(containerView);
	}

	/**
	 * 设置scroll是否可以刷新
	 * 
	 * @param enableRefresh
	 */
	public void setEnableRefresh(boolean enableRefresh) {
		this.enableRefresh = enableRefresh;
	}

	@Override
	public boolean onTouchEvent(MotionEvent ev) {
		// TODO Auto-generated method stub
		switch (ev.getAction()) {
		case MotionEvent.ACTION_DOWN:
			lastY = (int) ev.getY();
			break;
		case MotionEvent.ACTION_MOVE:
			int deltY = (int) (ev.getY() - lastY);
			lastY = (int) ev.getY();
			Logger.d("getScrollY:" + getScrollY());
			if (getScrollY() == 0
					&& (deltY > 0 || headerView.getTopMargin() > -headerHeight)) {
				updateHeader(deltY/OFFSET_RADIO);
				return true;
			} 
			break;
		default:
			//这里没有使用action_up的原因是,可能会受到viewpager的影响接收到action_cacel事件
			Logger.d("ev.getAction: " +ev.getAction());
			if (getScrollY() == 0) {
				Logger.d("topMargin():" + headerView.getTopMargin());
				if (headerView.getTopMargin() > 0 && enableRefresh && !refreshing) {
					refreshing = true;
					headerView.setState(ScrollViewHeader.STATE_REFRESHING);
					new Handler().postDelayed(new Runnable() {

						@Override
						public void run() {
							// TODO Auto-generated method stub
							if(listener != null) {
								listener.onRefresh();
								refreshing = false;
								ShowUtils.shortShow("更新成功");
								resetHeaderView();
							}
						}
					}, 3000);
				}
				Logger.d("resetHeaderView...");
				resetHeaderView();
			}
			break;
		}
		return super.onTouchEvent(ev);
	}

	/**
	 * 更新headerview的高度,同时更改状态
	 * 
	 * @param deltY
	 */
	public void updateHeader(float deltY) {
		int currentMargin = (int) (headerView.getTopMargin() + deltY);
		headerView.updateMargin(currentMargin);
		if(enableRefresh && !refreshing) {
			if (currentMargin > 0) {
				headerView.setState(ScrollViewHeader.STATE_READY);
			} else {
				headerView.setState(ScrollViewHeader.STATE_NORMAL);
			}
		}
	}

	/**
	 * 重置headerview的高度
	 */
	public void resetHeaderView() {
		int margin = headerView.getTopMargin();
		if(margin == -headerHeight) {
			return ;
		}
		if(margin < 0 && refreshing) {
			//当前已经在刷新,又重新进行拖动,但未拖满,不进行操作
			return ;
		}
		int finalMargin = 0;
		if(margin <= 0 && !refreshing) {
			finalMargin = headerHeight;
		}
		Logger.d("margin: " + margin);
		Logger.d("finalMargin: " + finalMargin);
		//松开刷新,或者下拉刷新,又松手,没有触发刷新
		scroller.startScroll(0, -margin, 0, finalMargin + margin, SCROLL_DURATION);
		
		invalidate();
	}
	
	/**
	 * 开始刷新
	 */
	public void startRefresh() {
		refreshing = true;
		headerView.setState(ScrollViewHeader.STATE_REFRESHING);
		if(listener != null) {
			Logger.d("xxx: " + headerHeight);
			scroller.startScroll(0, 0, 0, headerHeight, SCROLL_DURATION);
			invalidate();
			listener.onRefresh();
		}
	}
	
	/**
	 * 停止刷新
	 */
	public void stopRefresh() {
		if(refreshing) {
			refreshing = false;
			resetHeaderView();
		}
	}
	
	@Override
	public void computeScroll() {
		// TODO Auto-generated method stub
		if(scroller.computeScrollOffset()) {
			Logger.d("getCurrY: " + scroller.getCurrY()); 
			headerView.updateMargin(-scroller.getCurrY());
			//继续重绘
			postInvalidate();
 		}
		super.computeScroll();
	}
	
	public void setOnRefreshScrollViewListener(OnRefreshScrollViewListener listener) {
		this.listener = listener;
	}
	
	public interface OnRefreshScrollViewListener {
		public void onRefresh();
	}
}
代码其实还是比较容易,但是但是,自己还是花了很多时间,脑袋瓜不够灵活呀...

下面是ScrollViewHeader的代码:

public class ScrollViewHeader extends RelativeLayout {
	
	public final static int STATE_NORMAL = 0;
	public final static int STATE_READY = 1;
	public final static int STATE_REFRESHING = 2;
	private final int ROTATE_ANIM_DURATION = 180;
	private int topMargin = 0;
	private int state = STATE_NORMAL;
	private TextView refreshTv = null;
	private TextView refreshTimeTv = null;
	private ProgressBar refreshProgress = null;
	private ImageView refreshArrow = null;
	private Animation animationUp = null;
	private Animation animationDown = null;
	
	public ScrollViewHeader(Context context) {
		super(context);
		// TODO Auto-generated constructor stub
		if(!isInEditMode()) 
			initView(context);
	}

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

	public ScrollViewHeader(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		// TODO Auto-generated constructor stub
		if(!isInEditMode())
			initView(context);
	}

	/**
	 * 初始化相关的view
	 */
	public void initView(Context context) {
		animationDown = new RotateAnimation(-180f, 0, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
		animationDown.setDuration(ROTATE_ANIM_DURATION);
		animationDown.setFillAfter(true);
		animationUp = new RotateAnimation(0, -180f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
		animationUp.setDuration(ROTATE_ANIM_DURATION);
		animationUp.setFillAfter(true);
		setPadding(10, 25, 10, 25);
		View view = LayoutInflater.from(context).inflate(R.layout.scrollview_header, this, true);
		refreshTv = (TextView) view.findViewById(R.id.refresh_text);
		refreshTimeTv = (TextView) view.findViewById(R.id.refresh_time);
		refreshProgress = (ProgressBar) view.findViewById(R.id.refresh_progress);
		refreshArrow = (ImageView) view.findViewById(R.id.refresh_arrow);
	}
	
	/**
	 * 设置scrollviewHeader的状态
	 * @param state
	 */
	public void setState(int state) {
		if(this.state == state) {
			return ;
		}
		switch (state) {
		case STATE_NORMAL:
			refreshTv.setText("下拉刷新");
			refreshArrow.setVisibility(View.VISIBLE);
			refreshProgress.setVisibility(View.INVISIBLE);
			if(this.state == STATE_READY) {
				refreshArrow.startAnimation(animationDown);
			} else if(this.state == STATE_REFRESHING) {
				refreshArrow.clearAnimation();
			}
			break;
		case STATE_READY:
			refreshTv.setText("松开刷新");
			refreshArrow.setVisibility(View.VISIBLE);
			refreshProgress.setVisibility(View.INVISIBLE);
			refreshArrow.startAnimation(animationUp);
			break;
		case STATE_REFRESHING:
			refreshTv.setText("正在加载...");
			refreshProgress.setVisibility(View.VISIBLE);
			refreshArrow.clearAnimation();
			refreshArrow.setVisibility(View.INVISIBLE);
			break;
		default:
			break;
		}
		this.state = state;
	}
	
	/**
	 * 更新header的margin
	 * @param margin
	 */
	public void updateMargin(int margin) {
		//这里用Linearlayout的原因是Headerview的父控件是scrollcontainer是一个linearlayout 
		LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) this.getLayoutParams();
		params.topMargin = margin;
		topMargin = margin;
		setLayoutParams(params);
	}
	
	/**
	 * 获取header的margin
	 * @return
	 */
	public int getTopMargin() {
		return topMargin;
	}
}
header的布局文件,scrollview_header

<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android" >

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

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

        <TextView
            android:id="@+id/refresh_time"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="5分钟前更新" />
    </LinearLayout>

    <ProgressBar
        android:id="@+id/refresh_progress"
        android:layout_width="30dip"
        android:layout_height="30dip"
        android:layout_centerVertical="true"
        android:layout_marginRight="10dip"
        android:layout_toLeftOf="@id/refresh_des"
        android:visibility="invisible" />

    <ImageView
        android:id="@+id/refresh_arrow"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerVertical="true"
        android:layout_marginRight="10dip"
        android:layout_toLeftOf="@id/refresh_des"
        android:src="@drawable/arrow" />

</merge>
好了,相关的源码就只有3个文件...



阅读更多
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页