Android ReboundScrollView仿IOS拖拽回彈效果_Android

來源:互聯網
上載者:User

初衷:

其實github上有很多這種ScrollView的項目,但是不得不說功能太多太亂了,我就只是想要一個簡單效果的ScrollView,另外監聽下滑動距離而已,想想還是自己寫了個。

這裡先說下思路吧,如果不願意看的朋友可以直接跳過這一步,看下面的代碼:

Android 原生的ScrollView是不支援拉出螢幕外,並且也沒有回彈效果的,方便使用度卻不不太好,不知道為什麼不那麼設計。
我想做的事情正如上面所述:

1.希望能拉出螢幕外
2.鬆手後希望控制項回彈

我的思路是對ScrollView的子View進行操作

所有View的滑動控制肯定都受著onTouchEvent控制,所以,理所應當的,我要關注的重點,也就是onTouchEvent這個方法。

回彈的效果,就牽涉到位置的計算,這裡我的想法就用簡單的TranslateAnimation來實現。

大體思路就是這樣了,先把思路確定了,然後代碼總是磨出來的。

貼代碼:

import android.content.Context;import android.graphics.Rect;import android.util.AttributeSet;import android.view.MotionEvent;import android.view.View;import android.view.animation.TranslateAnimation;import android.widget.ScrollView;public class ReboundScrollView extends ScrollView {  private static final float MOVE_DELAY = 0.3f;//當拉出螢幕時的拖拽係數  private static final int ANIM_TIME = 300;//回彈耗時  private static final int FLING = 2;//fling 係數  private View childView;  private boolean havaMoved;  private Rect originalRect = new Rect();  private float startY;  @Override  protected void onFinishInflate() {    super.onFinishInflate();    if (getChildCount() > 0) {      childView = getChildAt(0);    }  }  @Override  public void fling(int velocityY) {    super.fling(velocityY / 2);  }  @Override  protected void onLayout(boolean changed, int l, int t, int r, int b) {    super.onLayout(changed, l, t, r, b);    if (childView == null)      return;    originalRect.set(childView.getLeft(), childView.getTop(),        childView.getRight(), childView.getBottom());  }  public ReboundScrollView(Context context, AttributeSet attrs,               int defStyle) {    super(context, attrs, defStyle);  }  public ReboundScrollView(Context context, AttributeSet attrs) {    super(context, attrs);  }  public ReboundScrollView(Context context) {    super(context);  }  /**   * 在觸摸事件中, 處理上拉和下拉的邏輯   */  @Override  public boolean dispatchTouchEvent(MotionEvent ev) {    if (childView == null) {      return super.dispatchTouchEvent(ev);    }    int action = ev.getAction();    switch (action) {      case MotionEvent.ACTION_DOWN:        startY = ev.getY();        break;      case MotionEvent.ACTION_UP:      case MotionEvent.ACTION_CANCEL:        if (!havaMoved)          break;        TranslateAnimation anim = new TranslateAnimation(0, 0,            childView.getTop(), originalRect.top);        anim.setDuration(ANIM_TIME);        childView.startAnimation(anim);        // 將標誌位設回false        havaMoved = false;        resetViewLayout();        break;      case MotionEvent.ACTION_MOVE:        float nowY = ev.getY();        int deltaY = (int) (nowY - startY);        int offset = (int) (deltaY * MOVE_DELAY);        childView.layout(originalRect.left, originalRect.top + offset,            originalRect.right, originalRect.bottom + offset);        havaMoved = true;        break;      default:        break;    }    return super.dispatchTouchEvent(ev);  }  public void resetViewLayout() {    childView.layout(originalRect.left, originalRect.top,        originalRect.right, originalRect.bottom);  }}

把代碼貼出來後,再來分析具體的實現:
首先是拉出螢幕,在MOVE的過程中,對於超出部分代碼,我使用layout重設子View的位置。

第二個要實現的就是回彈了,拖出去總是要回來的:
這裡我定義了一個Rect,在onLayout(boolean changed, int l, int t, int r, int b)方法中,記錄下了ScrollView的初始位置,以便於重設回彈。

說了許多,看一下代碼裡的關鍵代碼
MOVE的代碼:

 float nowY = ev.getY();        int deltaY = (int) (nowY - startY);        int offset = (int) (deltaY * MOVE_DELAY);        childView.layout(originalRect.left, originalRect.top + offset,            originalRect.right, originalRect.bottom + offset);        havaMoved = true;

這是MotionEvent.ACTION_MOVE的時候要做的,對chlidView的位置重新設定也就是lauout方法,這是基於originalRect的值來的,設定了相應的滑動係數,不然感覺實在是太靈敏了。

回彈的代碼:

 if (!havaMoved)          break;        TranslateAnimation anim = new TranslateAnimation(0, 0,            childView.getTop(), originalRect.top);        anim.setDuration(ANIM_TIME);        childView.startAnimation(anim);        // 將標誌位設回false        havaMoved = false;        resetViewLayout();...... public void resetViewLayout() {    childView.layout(originalRect.left, originalRect.top,        originalRect.right, originalRect.bottom);  }

當手指 MotionEvent.ACTION_UP或者MotionEvent.ACTION_CANCEL的時候把clildView置於原位,然後設定動畫,這就是回彈了。

補充:

這是個非常簡單的回彈ScrollView,需要注意的是,我這裡沒有對在螢幕內正常移動和螢幕外移動作區分,那樣就需要判斷當前的移動是否需要我們重寫了,也就是是否處於上拉,下拉的部分。沒有做判斷,我現在的代碼就會導致你手勢移動的距離和控制項滾動的距離是不一樣的。我是感覺如果你正常使用是不會注意到這一點的。但是不排除有這種需求。

下拉其實不太好判斷,因為我這裡操作的是ChildView,ScrollView在上拉過程中getScrollY()肯定是0,所以這一點,我也還沒想好。不過上拉倒是比較簡單。當childView.height <= ScrollView.height + getScrollY的時候就是上拉出螢幕的點了,這個應該能想通吧。

git地址:https://github.com/cjhandroid/ReboundScrollView

源碼下載:http://xiazai.jb51.net/201611/yuanma/androidReboundScrollView(jb51.net).rar

以上就是本文的全部內容,希望對大家的學習有所協助,也希望大家多多支援雲棲社區。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.