原理:
addHeaderView里做的事:
1.测量出header的宽高,调用了measureView方法
2.设置LayoutParams,宽:MATCH_PARENT,高:10
3.设置topMargin的值为负的header的高度,即将header隐藏在屏幕最上方
onInterceptTouchEvent:
如果滑动距离为零,让onInterceptTouchEvent处理。屏幕没有滑动效果。
onTouchEvent:
如果滑动距离大于零,让onTouchEvent处理。在滑动过程中不断的计算header的topMargin,让屏幕有滑动效果。
代码:
/** * android回弹效果 by haidaye */ public class HaidayeView extends LinearLayout { private int mLastMotionY; /** * 头部view */ private View mHeaderView; private LayoutInflater mInflater; private ScrollView mScrollView; /** * 头部view的高 */ private int mHeaderViewHeight; public HaidayeView(Context context, AttributeSet attrs) { super(context, attrs); init(); } public HaidayeView(Context context) { super(context); init(); } private void init() { // Load all of the animations we need in code rather than through XML mInflater = LayoutInflater.from(getContext()); // header view 在此添加,保证是第一个添加到linearlayout的最上端 addHeaderView(); } private void addHeaderView() { // header view mHeaderView = mInflater.inflate(R.layout.header, this, false); measureView(mHeaderView); mHeaderViewHeight = mHeaderView.getMeasuredHeight(); LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, mHeaderViewHeight); // 设置topMargin的值为负的header View高度,即将其隐藏在最上方 params.topMargin = -(mHeaderViewHeight); addView(mHeaderView, params); } @Override protected void onFinishInflate() { super.onFinishInflate(); initContentAdapterView(); } private void initContentAdapterView() { mScrollView = (ScrollView) getChildAt(1); } private void measureView(View child) { ViewGroup.LayoutParams p = child.getLayoutParams(); if (p == null) { p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); } int childWidthSpec = ViewGroup.getChildMeasureSpec(0, 0 + 0, p.width); int lpHeight = p.height; int childHeightSpec; if (lpHeight > 0) { childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight, MeasureSpec.EXACTLY); } else { childHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); } child.measure(childWidthSpec, childHeightSpec); } @Override public boolean onInterceptTouchEvent(MotionEvent e) { int y = (int) e.getRawY(); switch (e.getAction()) { case MotionEvent.ACTION_DOWN: // 首先拦截down事件,记录y坐标 mLastMotionY = y; break; case MotionEvent.ACTION_MOVE: // deltaY > 0 是向下运动,< 0是向上运动 int deltaY = y - mLastMotionY; if (isRefreshViewScroll(deltaY)) { return true; } mLastMotionY = y; break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: break; } return false; } /* * 如果在onInterceptTouchEvent()方法中没有拦截(即onInterceptTouchEvent()方法中 return * false)则由PullToRefreshView 的子View来处理;否则由下面的方法来处理(即由HaidayeView自己来处理) */ @Override public boolean onTouchEvent(MotionEvent event) { int y = (int) event.getRawY(); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: // onInterceptTouchEvent已经记录 // mLastMotionY = y; break; case MotionEvent.ACTION_MOVE: int deltaY = y - mLastMotionY; headerPrepareToRefresh(deltaY); mLastMotionY = y; break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: int topMargin = getHeaderTopMargin(); setHeaderTopMargin(-mHeaderViewHeight); break; case MotionEvent.ACTION_POINTER_DOWN://设置多点触控 this.mLastMotionY = y; break; } return super.onTouchEvent(event); } /** * 是否应该到了父View,即PullToRefreshView滑动 * * @param deltaY , deltaY > 0 是向下运动,< 0是向上运动 * @return */ private boolean isRefreshViewScroll(int deltaY) { // 对于ScrollView if (mScrollView != null) { // 子scroll view滑动到最顶端 View child = mScrollView.getChildAt(0); if (deltaY > 0 && mScrollView.getScrollY() == 0) { return true; } else if (deltaY < 0 && child.getMeasuredHeight() <= getHeight() + mScrollView.getScrollY()) { return true; } } return false; } /** * header 准备刷新,手指移动过程,还没有释放 * * @param deltaY ,手指滑动的距离 */ private void headerPrepareToRefresh(int deltaY) { int newTopMargin = changingHeaderViewTopMargin(deltaY); } private int changingHeaderViewTopMargin(int deltaY) { LayoutParams params = (LayoutParams) mHeaderView.getLayoutParams(); float newTopMargin = params.topMargin + deltaY * 0.3f; params.topMargin = (int) newTopMargin; mHeaderView.setLayoutParams(params); invalidate(); return params.topMargin; } /** * 设置header view 的topMargin的值 */ private void setHeaderTopMargin(int topMargin) { LayoutParams params = (LayoutParams) mHeaderView.getLayoutParams(); params.topMargin = topMargin; mHeaderView.setLayoutParams(params); invalidate(); } private int getHeaderTopMargin() { LayoutParams params = (LayoutParams) mHeaderView.getLayoutParams(); return params.topMargin; } }
致敬海大爷