百度一下不少阻尼的ScrollView,可是阻尼效果看起来比较生硬,不够圆润,在体验ios的时候,发现那边作的拖动效果貌似还不错,因此就百度了一个阻尼的ScrollView的代码,再改了一下,效果感受还能够,如今贴出来。java
效果图:android
代码:ios
import android.content.Context; import android.graphics.Rect; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import android.view.animation.Interpolator; import android.view.animation.TranslateAnimation; import android.widget.ScrollView; /** * Created by Admin on 2017/10/19. */ public class DampScrollView extends ScrollView { // y方向上当前触摸点的前一次记录位置 private int previousY = 0; // y方向上的触摸点的起始记录位置 private int startY = 0; // y方向上的触摸点当前记录位置 private int currentY = 0; // y方向上两次移动间移动的相对距离 private int deltaY = 0; // 第一个子视图 private View childView; // 用于记录childView的初始位置 private Rect topRect = new Rect(); //水平移动搞定距离 private float moveHeight; public DampScrollView(Context context) { super(context); } public DampScrollView(Context context, AttributeSet attrs) { super(context, attrs); } public DampScrollView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } @Override protected void onFinishInflate() { if (getChildCount() > 0) { childView = getChildAt(0); } } @Override public boolean dispatchTouchEvent(MotionEvent event) { if (null == childView) { return super.dispatchTouchEvent(event); } switch (event.getAction()) { case MotionEvent.ACTION_DOWN: startY = (int) event.getY(); previousY = startY; // 记录childView的初始位置 topRect.set(childView.getLeft(), childView.getTop(), childView.getRight(), childView.getBottom()); moveHeight = 0; break; case MotionEvent.ACTION_MOVE: currentY = (int) event.getY(); deltaY = currentY - previousY; previousY = currentY; //断定是否在顶部或者滑到了底部 if((!childView.canScrollVertically(-1)&&(currentY-startY)>0)||(!childView.canScrollVertically(1)&&(currentY-startY)<0)){ //计算阻尼 float distance = currentY - startY; if (distance < 0) { distance *= -1; } float damping = 0.5f;//阻尼值 float height = getHeight(); if (height != 0) { if (distance > height) { damping = 0; } else { damping = (height - distance) / height; } } if (currentY - startY < 0) { damping = 1 - damping; } //阻力值限制再0.3-0.5之间,平滑过分 damping *= 0.25; damping += 0.25; moveHeight = moveHeight + (deltaY * damping); childView.layout(topRect.left, (int) (topRect.top + moveHeight), topRect.right, (int) (topRect.bottom + moveHeight)); } break; case MotionEvent.ACTION_UP: if (!topRect.isEmpty()) { //开始回移动画 upDownMoveAnimation(); // 子控件回到初始位置 childView.layout(topRect.left, topRect.top, topRect.right, topRect.bottom); } //重置一些参数 startY = 0; currentY = 0; topRect.setEmpty(); break; } return super.dispatchTouchEvent(event); } // 初始化上下回弹的动画效果 private void upDownMoveAnimation() { TranslateAnimation animation = new TranslateAnimation(0.0f, 0.0f, childView.getTop(), topRect.top); animation.setDuration(600); animation.setFillAfter(true); //设置阻尼动画效果 animation.setInterpolator(new DampInterpolator()); childView.setAnimation(animation); } public class DampInterpolator implements Interpolator { @Override public float getInterpolation(float input) { //没看过源码,猜想是input是时间(0-1),返回值应该是进度(0-1) //先快后慢,为了更快更慢的效果,多乘了几回,如今这个效果比较满意 return 1 - (1 - input) * (1 - input) * (1 - input) * (1 - input) * (1 - input); } }