弹性scrollview 第一个子item是图片,随着下拉图片有弹性变化。
public class ReboundScrollView extends ScrollView {
String TAG = "ReboundScrollView";
/**
* 区分点击or滑动
*/
private int mScaledTouchSlop;
private int TOP_Y = 0;
/**
* 阻力
*/
private float damk = 0.5f;
/**
* 回弹延迟
*/
private int resetDelay = 200;
/**
* ScrollView的子View (ScrollView只能有一个子View)
*/
private View mInnerView;
private View boundView;
private float startY;
private int originHeight;
private Rect originRect = new Rect();
private int boundId;
public ReboundScrollView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
readStyleAttributes(context, attrs, 0);
}
public ReboundScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
readStyleAttributes(context, attrs, 0);
}
@Override
protected void onFinishInflate() {
if (getChildCount() == 0) {
return;
}
mInnerView = getChildAt(0);
if (boundId != 0) {
View viewById = findViewById(boundId);
setBoundView(viewById);
}
}
protected void readStyleAttributes(Context context, AttributeSet attrs, int defStyle) {
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.bound, 0, defStyle);
boundId = a.getResourceId(R.styleable.bound_bound_id, 0);
a.recycle();
mScaledTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
Log.i(TAG, "" + mScaledTouchSlop);
}
public void setBoundView(View view) {
refreshOriginHeight(view);
boundView = view;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
startY = ev.getY();
break;
case MotionEvent.ACTION_MOVE:
float currentY = ev.getY();
float scrollY = currentY - startY;
return Math.abs(scrollY) > mScaledTouchSlop;
}
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
if (mInnerView != null) {
computeMove(ev);
}
return super.onTouchEvent(ev);
}
private void computeMove(MotionEvent event) {
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_UP:
doReset();
break;
case MotionEvent.ACTION_MOVE:
doMove(event);
break;
}
}
private int computeDeltaY(MotionEvent event) {
float currentY = event.getY();
int deltaY = (int) ((startY - currentY) * damk);
startY = currentY;
return deltaY;
}
private void doMove(MotionEvent event) {
int deltaY = computeDeltaY(event);
if (!isNeedMove(deltaY)) {
return;
}
refreshNormalRect();
if (boundView != null) {
moveElasticView(deltaY);
} else {
moveInnerView(deltaY);
}
}
private void refreshOriginHeight(View view) {
if (boundView != null) {
android.view.ViewGroup.LayoutParams layoutParams = boundView.getLayoutParams();
layoutParams.height = originHeight;
boundView.setLayoutParams(layoutParams);
}
if (null != view) {
originHeight = view.getLayoutParams().height;
}
}
private void refreshNormalRect() {
if (!originRect.isEmpty()) {// 保存正常的布局位置
return;
}
originRect.set(mInnerView.getLeft(), mInnerView.getTop(), mInnerView.getRight(), mInnerView.getBottom());
}
private void moveInnerView(int deltaY) {
mInnerView.layout(mInnerView.getLeft(), mInnerView.getTop() - deltaY, mInnerView.getRight(), mInnerView.getBottom() - deltaY);
}
private void moveElasticView(int deltaY) {
android.view.ViewGroup.LayoutParams layoutParams = boundView.getLayoutParams();
layoutParams.height = Math.max(0, layoutParams.height - deltaY);
boundView.setLayoutParams(layoutParams);
}
// 是否需要还原
private boolean isNeedReset() {
if (boundView == null) {
return !originRect.isEmpty();
} else {
return originHeight != boundView.getLayoutParams().height;
}
}
private void doReset() {
boolean needReset = isNeedReset();
if (!needReset) {
return;
}
if (boundView != null) {
resetElasticView();
} else {
resetInnerView();
}
}
private void resetElasticView() {
ValueAnimator animator = ObjectAnimator.ofInt(boundView.getLayoutParams().height, originHeight);
animator.setDuration(resetDelay);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int value = (Integer) animation.getAnimatedValue();
android.view.ViewGroup.LayoutParams layoutParams = boundView.getLayoutParams();
layoutParams.height = value;
boundView.setLayoutParams(layoutParams);
}
});
animator.start();
}
private void resetInnerView() {
int moveY = mInnerView.getTop() - originRect.top;
ValueAnimator animator = ObjectAnimator.ofInt(moveY, 0);
animator.setDuration(resetDelay);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int value = (Integer) animation.getAnimatedValue();
mInnerView.layout(originRect.left, originRect.top + value, originRect.right, originRect.bottom + value);
}
});
animator.start();
}
// 是否需要移动布局
private boolean isNeedMove(int deltaY) {
return deltaY == 0 ? false : (deltaY < 0 ? isNeedMoveTop() : isNeedMoveBottom());
}
private boolean isNeedMoveTop() {
int scrollY = getScrollY();
return (scrollY == TOP_Y);
}
private boolean isNeedMoveBottom() {
int offset = mInnerView.getMeasuredHeight() - getHeight();
offset = (offset < 0) ? 0 : offset;
int scrollY = getScrollY();
return (scrollY == offset);
}
}