1. 程式人生 > >自定義scrollview實現吸附效果

自定義scrollview實現吸附效果

由於沒有製作gif的軟體所以現在只能上程式碼了

1.首先我們需要在自定義scrollview中重寫onFinishInflate方法找到相應的我們要控制的view

@Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        //設定不可過度滾動,否則上移後下拉會出現部分空白的情況
        setOverScrollMode(OVER_SCROLL_NEVER);
        View child = getChildAt(0);
        if (child != null
&& child instanceof ViewGroup) { mHeaderView = ((ViewGroup) ((ViewGroup) child).getChildAt(0)).getChildAt(0); //獲取預設第一個子View } }

我這裡寫的是在child的child的child獲取到的需要控制的佈局這裡根據自己需要在自己的層級上找到相應的view

2.接下來我們需要些onTouchEvent事件根據手指的觸控滑動以及離開控制view的變化

 @Override
    public boolean
onTouchEvent(MotionEvent ev) { if (mHeaderView == null) return super.onTouchEvent(ev); switch (ev.getAction()) { case MotionEvent.ACTION_MOVE: if (!mIsPulling) {//當滑到頂部時 getScrollY為0開始記錄手指按下的位置 if (getScrollY() != 0) break; mLastY = (int
) ev.getY(); } if (ev.getY() - mLastY < 0) { return super.onTouchEvent(ev); //之前邏輯如果上劃知識呼叫系統的上劃 } mIsPulling = true; setZoom((int) ((ev.getY() - mLastY) * mScaleRatio)); return true; case MotionEvent.ACTION_UP: mIsPulling = false; if (getScrollY() == 0) { replyView(); } soptionView(); break; } return super.onTouchEvent(ev); }

getScrollY():這個方法是獲取滑動的距離(注意不是某次滑動的距離 是整個控制元件滑動到現在的距離)
setZoom :該方法是控制view的height通過對view的height的控制試下下拉的時候view的高度隨之變高從而讓我們看到滑到頂的view還可以繼續滑動
replyView():如果是setZoom了那該方法的作用是讓view從鬆手的時刻起從劃過的高度變到0這裡我們用了一個屬性動畫去處理待會兒下面有程式碼
soptionView() :該方法是判斷如果view滑動的距離是在距離view高度0.3倍間的時候讓scrollview滑動到view的底部實現吸附的效果
下面是我寫的完整程式碼如有不明白歡迎提問

import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ScrollView;

/**
 * Created by Administrator on 2017/10/19.
 */
public class ZoomScrollView2 extends ScrollView {
    View mHeaderView;
    float mHeaderHeight;
    int mLastY;

    float mScaleRatio = 0.4f; //    滑動放大係數,係數越大,滑動時放大程度越大
    float mScaleTimes = 2.0f;//     最大的放大倍數
    float mReplyRatio = 0.5f; //    回彈時間係數,係數越小,回彈越快

    boolean mIsPulling = false;//是否正在滑動

    public ZoomScrollView2(Context context) {
        this(context, null);
    }

    public ZoomScrollView2(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public ZoomScrollView2(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        //設定不可過度滾動,否則上移後下拉會出現部分空白的情況
        setOverScrollMode(OVER_SCROLL_NEVER);
        View child = getChildAt(0);
        if (child != null && child instanceof ViewGroup) {
            mHeaderView = ((ViewGroup) ((ViewGroup) child).getChildAt(0)).getChildAt(0);   //獲取預設第一個子View
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        if (mHeaderView == null)
            return super.onTouchEvent(ev);
        switch (ev.getAction()) {
            case MotionEvent.ACTION_MOVE:
                if (!mIsPulling) {//當滑到頂部時 getScrollY為0開始記錄手指按下的位置
                    if (getScrollY() != 0) break;
                    mLastY = (int) ev.getY();
                }

                if (ev.getY() - mLastY < 0) {
                    return super.onTouchEvent(ev);   //之前邏輯如果上劃知識呼叫系統的上劃
                }

                mIsPulling = true;
                setZoom((int) ((ev.getY() - mLastY) * mScaleRatio));
                return true;
            case MotionEvent.ACTION_UP:
                mIsPulling = false;
                if (getScrollY() == 0) {
                    replyView();
                }
                soptionView();
                break;
        }
        return super.onTouchEvent(ev);
    }

    /**
     * 控制view的拉伸
     */
    private void setZoom(float s) {
        float scaleTimes = (float) ((mHeaderHeight + s) / (mHeaderHeight * 1.0));
        if (scaleTimes > mScaleTimes) return;// 如超過最大放大倍數,直接返回
        ViewGroup.LayoutParams layoutParams = mHeaderView.getLayoutParams();
        layoutParams.height = (int) (mHeaderHeight * ((mHeaderHeight + s) / mHeaderHeight));
        mHeaderView.setLayoutParams(layoutParams);

    }

    /**
     * 回彈
     */
    private void soptionView() {
        final int absY = getScrollY();
        //在 距離頂部0.7 或者大於頂部0.3距離後劃回
        if ((mHeaderHeight > absY && absY > (mHeaderHeight * 0.7)) || (absY > mHeaderHeight && absY < (mHeaderHeight * 1.3))) {
            scrollTo(0, (int) mHeaderHeight);
        }
    }


    /**
     * 回彈
     */
    private void replyView() {
        final float distance = mHeaderView.getMeasuredHeight() - mHeaderHeight;
        // 設定動畫
        ValueAnimator anim = ObjectAnimator.ofFloat(distance, 0.0F).setDuration((long) (distance * mReplyRatio));
        anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                setZoom((Float) animation.getAnimatedValue());
            }
        });
        anim.start();
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mHeaderHeight = mHeaderView.getMeasuredHeight();
    }
}