package com.race604.flyrefresh;
import android.animation.Animator;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.OvalShape;
import android.support.annotation.Nullable;
import android.support.design.widget.FloatingActionButton;
import android.support.v4.view.MotionEventCompat;
import android.support.v4.view.NestedScrollingChild;
import android.support.v4.view.NestedScrollingChildHelper;
import android.support.v4.view.NestedScrollingParent;
import android.support.v4.view.NestedScrollingParentHelper;
import android.support.v4.view.VelocityTrackerCompat;
import android.support.v4.view.ViewCompat;
import android.support.v4.widget.ScrollerCompat;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.widget.ImageView;
import com.desmond.libs.R;
import com.race604.flyrefresh.internal.ElasticOutInterpolator;
import com.race604.flyrefresh.internal.SimpleAnimatorListener;
import com.race604.utils.UIUtils;
/**
* Created by Jing on 15/5/18.
*/
public class PullHeaderLayout extends ViewGroup implements NestedScrollingParent, NestedScrollingChild {
private static final String TAG = PullHeaderLayout.class.getCanonicalName();
private static final boolean D = true;
public static final int STATE_IDLE = 0;
public static final int STATE_DRAGE = 1;
public static final int STATE_FLING = 2;
public static final int STATE_BOUNCE = 3;
final static int ACTION_BUTTON_CENTER = UIUtils.dpToPx(40);
final static int ACTION_ICON_SIZE = UIUtils.dpToPx(32);
private final static int DEFAULT_EXPAND = UIUtils.dpToPx(300);
private final static int DEFAULT_HEIGHT = UIUtils.dpToPx(240);
private final static int DEFAULT_SHRINK = UIUtils.dpToPx(48);
private int mHeaderId = 0;
private int mContentId = 0;
private Drawable mActionDrawable;
private FloatingActionButton mActionView;
private ImageView mFlyView;
private View mHeaderView;
private IPullHeader mPullHeaderView;
protected View mContent;
protected HeaderController mHeaderController;
private VelocityTracker mVelocityTracker;
private ValueAnimator mBounceAnim;
private int mPullState = STATE_IDLE;
private static final int INVALID_POINTER = -1;
private int mActivePointerId = INVALID_POINTER;
private boolean mIsBeingDragged = false;
private int mLastMotionY = 0;
private int mTouchSlop;
private int mMinimumVelocity;
private int mMaximumVelocity;
private int mNestedYOffset;
private final int[] mScrollOffset = new int[2];
private final int[] mScrollConsumed = new int[2];
private final NestedScrollingParentHelper mParentHelper;
private final NestedScrollingChildHelper mChildHelper;
private ScrollerCompat mScroller;
private OnPullListener mPullListener;
public PullHeaderLayout(Context context) {
this(context, null);
}
public PullHeaderLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public PullHeaderLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
int headerHeight = DEFAULT_HEIGHT;
int headerExpandHeight = DEFAULT_EXPAND;
int headerShrinkHeight = DEFAULT_SHRINK;
if (attrs != null) {
TypedArray arr = context.obtainStyledAttributes(attrs, R.styleable.PullHeaderLayout);
headerHeight = arr.getDimensionPixelOffset(R.styleable.PullHeaderLayout_phl_header_height,
DEFAULT_HEIGHT);
headerExpandHeight = arr.getDimensionPixelOffset(R.styleable.PullHeaderLayout_phl_header_expand_height,
DEFAULT_EXPAND);
headerShrinkHeight = arr.getDimensionPixelOffset(R.styleable.PullHeaderLayout_phl_header_shrink_height,
DEFAULT_SHRINK);
mHeaderId = arr.getResourceId(R.styleable.PullHeaderLayout_phl_header, mHeaderId);
mContentId = arr.getResourceId(R.styleable.PullHeaderLayout_phl_content, mContentId);
mActionDrawable = arr.getDrawable(R.styleable.PullHeaderLayout_phl_action);
arr.recycle();
}
mHeaderController = new HeaderController(headerHeight, headerExpandHeight, headerShrinkHeight);
final ViewConfiguration conf = ViewConfiguration.get(getContext());
mParentHelper = new NestedScrollingParentHelper(this);
mChildHelper = new NestedScrollingChildHelper(this);
setNestedScrollingEnabled(true);
init();
}
private void init() {
mScroller = ScrollerCompat.create(getContext());
final ViewConfiguration configuration = ViewConfiguration.get(getContext());
mTouchSlop = configuration.getScaledTouchSlop();
mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
}
// NestedScrollingChild
@Override
public void setNestedScrollingEnabled(boolean enabled) {
mChildHelper.setNestedScrollingEnabled(enabled);
}
@Override
public boolean isNestedScrollingEnabled() {
return mChildHelper.isNestedScrollingEnabled();
}
@Override
public boolean startNestedScroll(int axes) {
return mChildHelper.startNestedScroll(axes);
}
@Override
public void stopNestedScroll() {
mChildHelper.stopNestedScroll();
}
@Override
public boolean hasNestedScrollingParent() {
return mChildHelper.hasNestedScrollingParent();
}
@Override
public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed,
int dyUnconsumed, int[] offsetInWindow) {
return mChildHelper.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed,
offsetInWindow);
}
@Override
public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) {
return mChildHelper.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow);
}
@Override
public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {
return mChildHelper.dispatchNestedFling(velocityX, velocityY, consumed);
}
@Override
public boolean dispatchNestedPreFling(float velocityX, float velocityY) {
return mChildHelper.dispatchNestedPreFling(velocityX, velocityY);
}
// NestedScrollingParent
@Override
public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {
return (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0;
}
@Override
public void onNestedScrollAccepted(View child, View target, int nestedScrollAxes) {
mParentHelper.onNestedScrollAccepted(child, target, nestedScrollAxes);
startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL);
}
@Override
public void onStopNestedScroll(View target) {
stopNestedScroll();
}
@Override
public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed,
int dyUnconsumed) {
final int myConsumed = moveBy(dyUnconsumed);
final int myUnconsumed = dyUnconsumed - myConsumed;
dispatchNestedScroll(0, myConsumed, 0, myUnconsumed, null);
}
@Override
public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {
if (dy > 0 && mHeaderController.canScrollUp()) {
final int delta = moveBy(dy);
consumed[0] = 0;
consumed[1] = delta;
//dispatchNestedScroll(0, myConsumed, 0, consumed[1], null);
}
}
@Override
public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) {
if (!consumed) {
flingWithNestedDispatch((int) velocityY);
return true;
}
return false;
}
private boolean flingWithNestedDispatch(int velocityY) {
final boolean canFling = (mHeaderController.canScrollUp() && velocityY > 0) ||
(mHeaderController.canScrollDown() && velocityY < 0);
if (!dispatchNestedPreFling(0, velocityY)) {
dispatchNestedFling(0, velocityY, canFling);
if (canFling) {
fling(velocityY);
}
}
return canFling;
}
@Override
public boolean onNestedPreFling(View target, float velocityX, float velocityY) {
return flingWithNestedDispatch((int) velocityY);
}
@Override
public int getNestedScrollAxes() {
return mParentHelper.getNestedScrollAxes();
}
public void setHeaderSize(int height, int maxHeight, int minHeight) {
mHeaderController.setSize(height, maxHeight, minHeight);
if (isLayoutRequested()) {
requestLayout();
}
}
public void setOnPullListener(OnPullListener listener) {
mPullListener = listener;
}
public void setActionDrawable(Drawable actionDrawable) {
mActionDrawable = actionDrawable;
if (mActionDrawable != null) {
if (mActionView == null) {
final int bgColor = UIUtils.getThemeColorFromAttrOrRes(getContext(), R.attr.colorAccent, R.color.accent);
final int pressedColor = UIUtils.darkerColor(bgColor, 0.8f);
final ShapeDrawable bgDrawable = new ShapeDrawable(new OvalShape());
bgDrawable.getPaint().setColor(bgColor);
mActionView = new FloatingActionButton(getContext());
mActionView.setRippleColor(pressedColor);
mActionView.setBackgroundDrawable(bgDrawable);
addView(mActionView, new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
}
if (mFlyView == null) {
mFlyView = new ImageView(getContext());
mFlyView.setScaleType(ImageView.ScaleType.FIT_XY);
addView(mFlyView, new LayoutParams(ACTION_ICON_SIZE, ACTION_ICON_SIZE));
mFlyView.bringToFront();
float elevation = ViewCompat.getElevation(mActionView);
ViewCompat.setElevation(mFlyView, elevation + 1);
}
mFlyView.setImageDrawable(mActionDrawable);
} else {
if (mActionView != null) {
removeView(mActionView);
removeView(mFlyView);
mActionView = null;
mFlyView = null;
}
}
}
@Nullable
public View getIconView() {
return mFlyView;
}
@Nullable
public FloatingActionButton getHeaderActionButton() {
return mActionView;
}
public void setHeaderView(View headerView, LayoutParams lp) {
if (mHeaderView != null) {
removeView(mHeaderView);
mPullHeaderView = null;
}
addView(headerView, 0, lp);
mHeaderView = headerView;
if (mHeaderView instanceof IPullHeader) {
mPullHeaderView = (IPullHeader) mHeaderView;
}
}
@Override
protected void onFinishInflate() {
final int childCount = getChildCount();
if (childCount > 2) {
throw new IllegalStateException("FlyRefreshLayout only can host 2 elements");
} else if (childCount == 2) {
if (mHeaderId != 0 && mHeaderView == null) {
mHeaderView = findViewById(mHeaderId);
}
if (mContentId != 0 && mContent == null) {
mContent = findViewById(mContentId);
}
// not specify header or content
if (mContent == null || mHeaderView == null) {
View child1 = getChildAt(0);
View child2 = getChildAt(1);
if (child1 instanceof IPullHeader) {
mHeaderView = child1;
mContent = child2;
mPullHeaderView = (IPullHeader) mHeaderView;
} else if (child2 instanceof IPullHeader) {
mHeaderView = child2;
mContent = child1;
mPullHeaderView = (IPullHeader) mHeaderView;
} else {
// both are not specified
if (mContent == null && mHeaderView == null) {
mHeaderView = child1;
mContent = child2;
}
// only one is specified
else {
if (mHeaderView == null) {
mHeaderView = mContent == child1 ? child2 : child1;
} else {
mContent = mHeaderView == child1 ? child2 : child1;
}
}
}
}
} else if (childCount == 1) {
mContent = getChildAt(0);
}
setActionDrawable(mActionDrawable);
super.onFinishInflate();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (mHeaderView != null) {
measureChildWithMargins(mHeaderView, widthMeasureSpec, 0, heightMeasureSpec, 0);
}
if (mContent != null) {
measureChildWithMargins(mContent, widthMeasureSpec, 0, heightMeasureSpec, mHeaderController.getMinHeight());
}
if (mActionView != null) {
measureChild(mActionView, widthMeasureSpec, heightMeasureSpec);
measureChild(mFlyView, widthMeasureSpec, heightMeasureSpec);
}
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
layoutChildren();
}
private void layoutChildren() {
int offsetY = mHeaderController.getCurPosition();
int paddingLeft = getPaddingLeft();
int paddingTop = getPaddingTop();
if (mHeaderView != null) {
MarginLayoutParams lp = (MarginLayoutParams) mHeaderView.getLayoutParams();
final int left = paddingLeft + lp.leftMargin;
final int top = paddingTop + lp.topMargin;
final int right = left + mHeaderView.getMeasuredWidth();
final int bottom = top + mHeaderView.getMeasuredHeight();
mHeaderView.layout(left, top, right, bottom);
}
if (mContent != null) {
MarginLayoutParams lp = (MarginLayoutParams) mContent.getLayoutParams();
final int left = paddingLeft + lp.leftMargin;
final int top = paddingTop + lp.topMargin + offsetY;
final int right = left + mContent.getMeasuredWidth();
final int bottom = top + mContent.getMeasuredHeight();
mContent.layout(left, top, right, bottom);
}
if (mActionView != null) {
final int center = ACTION_BUTTON_CENTER;
int halfWidth = (mActionView.getMeasuredWidth() + 1) / 2;
int halfHeight = (mActionView.getMeasuredHeight() + 1) / 2;
mActionView.layout(center - halfWidth , offsetY - halfHeight,
center + halfWidth, offsetY + halfHeight);
halfWidth = (mFlyView.getMeasuredWidth() + 1) / 2;
halfHeight = (mFlyView.getMeasuredHeight() + 1) / 2;
mFlyView.layout(center - halfWidth, offsetY - halfHeight,
center + halfWidth, offsetY + halfHeight);
}
}
private void obtainVelocityTracker(MotionEvent event) {
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
}
mVelocityTracker.addMovement(event);
}
private void initOrResetVelocityTracker() {
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
} else {
mVelocityTracker.clear();
}
}
private void releaseVelocityTracker() {
if (mVelocityTracker != null) {
mVelocityTracker.recycle();
mVelocityTracker = null;
}
}
private void onSecondaryPointerUp(MotionEvent ev) {
final int pointerIndex = (ev.getAction() & MotionEventCompat.ACTION_POINTER_INDEX_MASK) >>
MotionEventCompat.ACTION_POINTER_INDEX_SHIFT;
final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex);
if (pointerId == mActivePointerId) {
// This was our active pointer going up. Choose a new
// active pointer and adjust accordingly.
// TODO: Make this decision more intelligent.
final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
mLastMotionY = (int) MotionEventCompat.getY(ev, newPointerIndex);
mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex);
if (mVelocityTracker != null) {
mVelocityTracker.clear();
}
}
}
private void endDrag() {
mIsBeingDragged = false;
releaseVelocityTracker();
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
final int action = ev.getAction();
if ((action == MotionEvent.ACTION_MOVE) && (mIsBeingDragged)) {
return true;
}
if(!isEnabled()) {
return false;
}
switch (action & MotionEventCompat.ACTION_MASK) {
case MotionEvent.ACTION_MOVE: {
final int activePointerId = mActivePointerId;
if (activePointerId == INVALID_POINTER) {
// If we don't have a valid id, the touch down wasn't on content.
break;
}
final int pointerIndex = MotionEventCompat.findPointerIndex(ev, activePointerId);
if (pointerIndex == -1) {
Log.e(TAG, "Invalid pointerId=" + activePointerId
+ " in onInterceptTouchEvent");
break;
}
final int y = (int) MotionEventCompat.getY(ev, pointerIndex);
final int yDiff = Math.abs(y - mLastMotionY);
if (yDiff > mTouchSlop
&& (getNestedScrollAxes() & ViewCompat.SCROLL_AXIS_VERTICAL) == 0) {
mIsBeingDragged = true;
mLastMotionY = y;
obtainVelocityTracker(ev);
mNestedYOffset = 0;
final ViewParent parent = getParent();
if (parent != null) {
parent.requestDisallowInterceptTouchEvent(true);
}
}
break;
}
case MotionEvent.ACTION_DOWN: {
final int y = (int) ev.getY();
/*
* Remember location of down touch.
* ACTION_DOWN always refers to pointer index 0.
*/
mLastMotionY = y;
mActivePointerId = MotionEventCompat.getPointerId(ev, 0);
initOrResetVelocityTracker();
mVelocityTracker.addMovement(ev);
/*
* If being flinged and user touches the screen, initiate drag;
* otherwise don't. mScroller.isFinished should be false when
* being flinged.
*/
mIsBeingDragged = !mScroller.isFinished();
startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL);
break;
}
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
/* Release the drag */
mIsBeingDragged = false;
mActivePointerId = INVALID_POINTER;
endDrag();
stopNestedScroll();
break;
case MotionEventCompat.ACTION_POINTER_UP:
onSecondaryPointerUp(ev);
break;
}
return mIsBeingDragged;
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
final int actionMasked = MotionEventCompat.getActionMasked(ev);
if (actionMasked == MotionEvent.ACTION_UP || actionMasked == MotionEvent.ACTION_CANCEL) {
tryBounceBack();
}
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
MotionEvent vtev = MotionEvent.obtain(ev);
final int actionMasked = MotionEventCompat.getActionMasked(ev);
if (actionMasked == MotionEvent.ACTION_DOWN) {
mNestedYOffset = 0;
}
vtev.offsetLocation(0, mNestedYOffset);
switch (actionMasked) {
case MotionEvent.ACTION_DOWN: {
if ((mIsBeingDragged = !mScroller.isFinished())) {
final ViewParent parent = getParent();
if (parent != null) {
parent.requestDisallowInterceptTouchEvent(true);
}
}
/*
* If being flinged and user touches, stop the fling. isFinished
* will be false if being flinged.
*/
if (!mScroller.isFinished()) {
mScroller.abortAnimation();
}
// Remember where the motion event started
mLastMotionY = (int) ev.getY();
mActivePointerId = MotionEventCompat.getPointerId(ev, 0);
startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL);
break;
}
case MotionEvent.ACTION_MOVE:
final int activePointerIndex = MotionEventCompat.findPointerIndex(ev,
mActivePointerId);
if (activePointerIndex == -1) {
Log.e(TAG, "Invalid pointerId=" + mActivePointerId + " in onTouchEvent");
break;
}
final int y = (int) MotionEventCompat.getY(ev, activePointerIndex);
int deltaY = mLastMotionY - y;
if (dispatchNestedPreScroll(0, deltaY, mScrollConsumed, mScrollOffset)) {
deltaY -= mScrollConsumed[1];
vtev.offsetLocation(0, mScrollOffset[1]);
mNestedYOffset += mScrollOffset[1];
}
if (!mIsBeingDragged && Math.abs(deltaY) > mTouchSlop) {
final ViewParent parent = getParent();
if (parent != null) {
parent.requestDisallowInterceptTouchEvent(true);
}
mIsBeingDragged = true;
if (deltaY > 0) {
deltaY -= mTouchSlop;
} else {
deltaY += mTouchSlop;
}
}
if (mIsBeingDragged) {
// Scroll to follow the motion event
mLastMotionY = y - mScrollOffset[1];
final int scrolledDeltaY = moveBy(deltaY);
final int unconsumedY = deltaY - scrolledDeltaY;
if (dispatchNestedScroll(0, scrolledDeltaY, 0, unconsumedY, mScrollOffset)) {
mLastMotionY -= mScrollOffset[1];
vtev.offsetLocation(0, mScrollOffset[1]);
mNestedYOffset += mScrollOffset[1];
}
}
break;
case MotionEvent.ACTION_UP:
if (mIsBeingDragged) {
final VelocityTracker velocityTracker = mVelocityTracker;
velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
int initialVelocity = (int) VelocityTrackerCompat.getYVelocity(velocityTracker,
mActivePointerId);
if ((Math.abs(initialVelocity) > mMinimumVelocity)) {
flingWithNestedDispatch(-initialVelocity);
}
mActivePointerId = INVALID_POINTER;
endDrag();
}
break;
case MotionEvent.ACTION_CANCEL:
if (mIsBeingDragged && getChildCount() > 0) {
mActivePointerId = INVALID_POINTER;
endDrag();
}
break;
case MotionEventCompat.ACTION_POINTER_DOWN: {
final int index = MotionEventCompat.getActionIndex(ev);
mLastMotionY = (int) MotionEventCompat.getY(ev, index);
mActivePointerId = MotionEventCompat.getPointerId(ev, index);
break;
}
case MotionEventCompat.ACTION_POINTER_UP:
onSecondaryPointerUp(ev);
mLastMotionY = (int) MotionEventCompat.getY(ev,
MotionEventCompat.findPointerIndex(ev, mActivePointerId));
break;
}
if (mVelocityTracker != null) {
mVelocityTracker.addMovement(vtev);
}
vtev.recycle();
return true;
}
protected void onMoveHeader(int state, float progress) {}
/**
* Fling the scroll view
*
* @param velocityY The initial velocity in the Y direction. Positive
* numbers mean that the finger/cursor is moving down the screen,
* which means we want to scroll towards the top.
*/
public void fling(int velocityY) {
mPullState = STATE_FLING;
mScroller.abortAnimation();
mScroller.fling(0, mHeaderController.getScroll(), 0, velocityY, 0, 0,
mHeaderController.getMinScroll(), mHeaderController.getMaxScroll(),
0, 0);
ViewCompat.postInvalidateOnAnimation(this);
}
private int moveBy(float deltaY) {
int oldScroll = mHeaderController.getScroll();
int consumed = mHeaderController.move(deltaY);
final int delta = mHeaderController.getScroll() - oldScroll;
if (delta == 0) {
return 0;
}
if (mPullState != STATE_DRAGE) {
mPullState = STATE_DRAGE;
}
if (mContent != null) {
mContent.offsetTopAndBottom(-delta);
}
if (mActionView != null) {
mActionView.offsetTopAndBottom(-delta);
mFlyView.offsetTopAndBottom(-delta);
float percentage = mHeaderController.getMovePercentage();
onMoveHeader(mPullState, percentage);
if (mPullHeaderView != null) {
mPullHeaderView.onPullProgress(this, mPullState, percentage);
}
if (mPullListener != null) {
mPullListener.onPullProgress(this, mPullState, percentage);
}
}
return consumed;
}
@Override
public void computeScroll() {
if (mScroller.computeScrollOffset()) {
int oldY = mHeaderController.getScroll();
int y = mScroller.getCurrY();
if (oldY != y) {
moveBy(y - oldY);
}
ViewCompat.postInvalidateOnAnimation(this);
} else {
tryBounceBack();
}
}
private void tryBounceBack() {
if (mHeaderController.isOverHeight()) {
mBounceAnim = ObjectAnimator.ofFloat(mHeaderController.getScroll(), 0);
mBounceAnim.setInterpolator(new ElasticOutInterpolator());
mBounceAnim.setDuration(500);
mBounceAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float deltaY = (float) animation.getAnimatedValue() - mHeaderController.getScroll();
moveBy(deltaY);
}
});
mBounceAnim.addListener(new SimpleAnimatorListener() {
@Override
public void onAnimationEnd(Animator animation) {
mPullState = STATE_IDLE;
}
});
mBounceAnim.start();
mPullState = STATE_BOUNCE;
if (mHeaderController.needSendRefresh()) {
startRefresh();
}
} else {
mPullState = STATE_IDLE;
}
}
public void startRefresh() {}
@Override
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
return p instanceof LayoutParams;
}
@Override
protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
}
@Override
protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
return new LayoutParams(p);
}
@Override
public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
return new LayoutParams(getContext(), attrs);
}
public static class LayoutParams extends MarginLayoutParams {
public LayoutParams(Context c, AttributeSet attrs) {
super(c, attrs);
}
public LayoutParams(int width, int height) {
super(width, height);
}
@SuppressWarnings({"unused"})
public LayoutParams(MarginLayoutParams source) {
super(source);
}
public LayoutParams(ViewGroup.LayoutParams source) {
super(source);
}
}
public interface OnPullListener {
void onPullProgress(PullHeaderLayout view, int state, float progress);
}
}