Android自定義View--ScrollView實現回彈效果

這是要實現的效果

需要實現的功能

當下滑或上滑到盡頭時還能繼續滑動,釋放手指後能自動回彈到原來的位置

需要用到的知識點

  • getScrollY()
    1. getScrollY是當前view的左上角相對於母視圖(這裏是ScrollView)的左上角的Y軸偏移量,上拉值增加,反之亦然。
  • getMeasuredHeight()
  • getHeight()
    1.getMeasuredHeight()返回的是原始測量高度,與屏幕無關,getHeight()返回的是在屏幕上顯示的高度,項目利用子View的getMeasuredHeight()減去父類(ScrollView)的高度,判斷是否滑動到底部。

MyScrollView的實現

package rc.loveq.myscrollview;

import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.TranslateAnimation;
import android.widget.ScrollView;

/**
 * Author:Rc
 * Csdn:http://blog.csdn.net/loveqrc
 * 0n 2016/12/8 08:46
 * Email:[email protected]
 */

public class MyScrollView extends ScrollView {

    private static final String TAG ="MyScrollView" ;
    private static final long RESTORE_TIME = 300;//回彈的時間
    public static final int DRAG_RATE=2;//mChildView移動的距離=手指移動的的距離/DRAG_RATE
    private View mChildView;
    private float mDownY;
    private Rect mRect=new Rect();//用於保存子View的位置
    private boolean isRestoring=false;//是否正在回彈

    public MyScrollView(Context context) {
        super(context);
    }

    public MyScrollView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

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

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        if (getChildCount()>0){//保證要有子View
            mChildView = getChildAt(0);
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        if (!isRestoring){
            handleTouchEvent(ev);//監聽觸摸事件
        }
        //因爲我們要做的是當scrollView下滑或上滑到盡頭還能繼續滑動
        //所以只有這兩種情況我們才自己處理
        //其他情況交由父類默認實現

        Log.e(TAG, "onTouchEvent: getScrollY:"+getScrollY());
        return super.onTouchEvent(ev);
    }

    private void handleTouchEvent(MotionEvent ev) {
        switch (ev.getAction()){
            case MotionEvent.ACTION_DOWN:
                mDownY = ev.getY();
                break;
            case MotionEvent.ACTION_MOVE:
                float moveY = ev.getY();
                int deltaY= (int) (moveY-mDownY);
                if (isNeedLoadMore()){
                     if (mRect.isEmpty()){//如果沒有記錄子view位置,那麼我們記錄
                        mRect.set(mChildView.getLeft(), mChildView.getTop()
                                , mChildView.getRight(), mChildView.getBottom());
                     }
                    mChildView.layout(mChildView.getLeft(), mChildView.getTop()+deltaY/DRAG_RATE,
                            mChildView.getRight(), mChildView.getBottom()+deltaY/DRAG_RATE);
                }
                mDownY=moveY;//更新位置
                break;
            case MotionEvent.ACTION_UP://當手指釋放的時候,需要回彈到原來的位置
                mDownY=0;
                if (isNeedRestore()){
                    restoreState();
                }
                break;
        }
    }

    /**
     * 恢復到原來的位置
     */
    private void restoreState() {
        //TranslateAnimation移動的相對位置
        TranslateAnimation ta=new TranslateAnimation(0,0,0,mRect.top- mChildView.getTop());
        ta.setDuration(RESTORE_TIME);
        mChildView.startAnimation(ta);
        ta.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {
                    isRestoring=true;
            }

            @Override
            public void onAnimationEnd(Animation animation) {
                isRestoring=false;
                mChildView.clearAnimation();
                //保證回到原來的位置
                mChildView.layout(mRect.left,mRect.top,mRect.right,mRect.bottom);
                mRect.setEmpty();
            }

            @Override
            public void onAnimationRepeat(Animation animation) {

            }
        });


    }

    /**
     * 判斷是否需要回彈
     * @return
     */
    private boolean isNeedRestore() {
        return !mRect.isEmpty();//如果保存過位置,說明需要回彈
    }

    /**
     * 是否已經滑動盡頭了
     * @return
     */
    private boolean isNeedLoadMore() {
        int offset = mChildView.getMeasuredHeight() - getHeight();
        int scrollY = getScrollY();
        if (scrollY==0||offset==scrollY){//當scrollY==0表示向下滑滑到盡頭了
                return true;            // offset==scrollY 表示向上滑滑到盡頭了
        }
        return  false;
    }


}

佈局代碼

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
>
    <rc.loveq.myscrollview.MyScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <LinearLayout android:layout_width="match_parent"
                      android:layout_height="match_parent"
                        android:orientation="vertical">
        <TextView
            android:layout_width="match_parent"
            android:layout_height="60dp"
            android:textSize="36sp"
            android:text="這是第1個數據"/>
            <TextView
                android:layout_width="match_parent"
                android:layout_height="60dp"
                android:textSize="36sp"
                android:text="這是第2個數據"/>
            <TextView
                android:layout_width="match_parent"
                android:layout_height="60dp"
                android:textSize="36sp"
                android:text="這是第3個數據"/>
            <TextView
                android:layout_width="match_parent"
                android:layout_height="60dp"
                android:textSize="36sp"
                android:text="這是第4個數據"/>
            <TextView
                android:layout_width="match_parent"
                android:layout_height="60dp"
                android:textSize="36sp"
                android:text="這是第5個數據"/>
            <TextView
                android:layout_width="match_parent"
                android:layout_height="60dp"
                android:textSize="36sp"
                android:text="這是第6個數據"/>
            <TextView
                android:layout_width="match_parent"
                android:layout_height="60dp"
                android:textSize="36sp"
                android:text="這是第7個數據"/>
            <TextView
                android:layout_width="match_parent"
                android:layout_height="60dp"
                android:textSize="36sp"
                android:text="這是第8個數據"/>
            <TextView
                android:layout_width="match_parent"
                android:layout_height="60dp"
                android:textSize="36sp"
                android:text="這是第9個數據"/>
            <TextView
                android:layout_width="match_parent"
                android:layout_height="60dp"
                android:textSize="36sp"
                android:text="這是第10個數據"/>


        </LinearLayout>
    </rc.loveq.myscrollview.MyScrollView>
</RelativeLayout>

下載地址

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章