百度一下不少阻尼的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); } }