自定义下拉上拉带阻尼回弹的NestedScrollView
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.TranslateAnimation;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.widget.NestedScrollView;
public class BounceNestedScrollView extends NestedScrollView {
private View mInnerView;
private float mDownY;
private Rect mRect = new Rect();
private int offset;
private boolean isCount = false;
private int mWidth;
private int mHeight;
public BounceNestedScrollView(@NonNull Context context) {
this(context, null);
}
public BounceNestedScrollView(@NonNull Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public BounceNestedScrollView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
if (getChildCount() > 0) {
mInnerView = getChildAt(0);
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mWidth = MeasureSpec.getSize(widthMeasureSpec);
mHeight = MeasureSpec.getSize(heightMeasureSpec);
MarginLayoutParams lp = (MarginLayoutParams) mInnerView.getLayoutParams();
offset = mInnerView.getMeasuredHeight() - lp.topMargin - lp.bottomMargin - mHeight;
}
@Override
public boolean onTouchEvent(MotionEvent e) {
if (mInnerView != null) {
commOnTouchEvent(e);
}
return super.onTouchEvent(e);
}
public void commOnTouchEvent(MotionEvent e) {
switch (e.getAction()) {
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE:
final float preY = mDownY;
float nowY = e.getY();
int deltaY = (int) (preY - nowY);
if (!isCount) {
deltaY = 0;
}
mDownY = nowY;
if (isNeedMove()) {
if (mRect.isEmpty()) {
mRect.set(mInnerView.getLeft(), mInnerView.getTop(),
mInnerView.getRight(), mInnerView.getBottom());
}
mInnerView.layout(mInnerView.getLeft(), mInnerView.getTop() - deltaY / 2,
mInnerView.getRight(), mInnerView.getBottom() - deltaY / 2);
}
isCount = true;
break;
case MotionEvent.ACTION_UP:
if (isNeedAnimation()) {
translateAnimator();
isCount = false;
}
break;
}
}
public void translateAnimator() {
Animation animation = new TranslateAnimation(0, 0, mInnerView.getTop(), mRect.top);
animation.setDuration(200);
animation.setFillAfter(true);
mInnerView.startAnimation(animation);
mInnerView.layout(mRect.left, mRect.top, mRect.right, mRect.bottom);
mRect.setEmpty();
}
public boolean isNeedAnimation() {
return !mRect.isEmpty();
}
public boolean isNeedMove() {
if (getScrollY() == 0 || getScrollY() >= offset) {
return true;
}
return false;
}
}