阻尼回弹-背景缩放

432 阅读4分钟
菜鸟的日常搬运

Java代码如下:

1.自定义ScrollView

package com.example.asus.myapplication;

import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.TranslateAnimation;
import android.widget.ImageView;
import android.widget.ScrollView;
import android.widget.Scroller;

/**
 * 阻尼效果的scrollview
 */

public class DampView extends ScrollView {
    private static final int LEN = 0xc8;
    private static final int DURATION = 500;
    private static final int MAX_DY = 200;
    private Scroller mScroller;
    TouchTool tool;
    int left, top;
    float startX, startY, currentX, currentY;
    int imageViewH;
    int rootW, rootH;
    ImageView imageView;
    boolean scrollerType;
    //移动因子, 是一个百分比, 比如手指移动了100px, 那么View就只移动50px
    //目的是达到一个延迟的效果
    private static final float MOVE_FACTOR = 0.5f;
    //松开手指后, 界面回到正常位置需要的动画时间
    private static final int ANIM_TIME = 100;
    //ScrollView的子View, 也是ScrollView的唯一一个子View
    private View contentView;
    //手指按下时的Y值, 用于在移动时计算移动距离
    //如果按下时不能上拉和下拉, 会在手指移动时更新为当前手指的Y值
    private float startYs;
    //用于记录正常的布局位置
    private Rect originalRect = new Rect();
    //手指按下时记录是否可以继续下拉
    private boolean canPullDown = false;
    //手指按下时记录是否可以继续上拉
    private boolean canPullUp = false;
    //在手指滑动的过程中记录是否移动了布局
    private boolean isMoved = false;
    public DampView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }
    public DampView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mScroller = new Scroller(context);
    }
    public DampView(Context context) {
        super(context);
    }
    public void setImageView(ImageView imageView) {
        this.imageView = imageView;
    }
    @Override
    protected int computeScrollDeltaToGetChildRectOnScreen(Rect rect) {
        return 0;
    }
    private int[] li = new int[2];
    private int[] li2 = new int[2];
    private float lastLy;
    private boolean startIsTop = true;
    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        super.dispatchTouchEvent(event);
        int action = event.getAction();
        if (!mScroller.isFinished()) {
            return super.onTouchEvent(event);
        }
        currentX = event.getX();
        currentY = event.getY();
        imageView.getLocationInWindow(li);
        getLocationOnScreen(li2);
        imageView.getTop();
        if (contentView == null) {
            return super.dispatchTouchEvent(event);
        }
        switch (action) {
            case MotionEvent.ACTION_DOWN:
                if (li[1] != li2[1]) {// 判断开始触摸时,imageview和窗口顶部对齐没
                    startIsTop = false;
                }
                left = imageView.getLeft();
                top = imageView.getBottom();
                rootW = getWidth();
                rootH = getHeight();
                imageViewH = imageView.getHeight();
                startX = currentX;
                startY = currentY;
                tool = new TouchTool(imageView.getLeft(), imageView.getBottom(), imageView.getLeft(),
                        imageView.getBottom() + LEN);
                //判断是否可以上拉和下拉
                canPullDown = isCanPullDown();
                canPullUp = false;
                //记录按下时的Y值
                startY = event.getY();
                break;
            case MotionEvent.ACTION_MOVE:
                if (!startIsTop && li[1] == li2[1]) {
                    startY = currentY;
                    startIsTop = true;
                }
                if (imageView.isShown() && imageView.getTop() >= 0) {
                    if (tool != null) {
                        int t = tool.getScrollY(currentY - startY);
                        if (!scrollerType && currentY < lastLy && imageView.getHeight() > imageViewH) {
                            scrollTo(0, 0);
                            imageView.getLocationInWindow(li);
                            getLocationOnScreen(li2);
                            android.view.ViewGroup.LayoutParams params = imageView.getLayoutParams();
                            params.height = t;
                            imageView.setLayoutParams(params);
                            if (imageView.getHeight() == imageViewH && li[1] == li2[1]) {
                                scrollerType = true;
                            }
                            if (startIsTop && li[1] != li2[1]) {
                                startIsTop = false;
                            }
                        }
                        if (t >= top && t <= imageView.getBottom() + LEN && li[1] == li2[1] && currentY > lastLy) {
                            android.view.ViewGroup.LayoutParams params = imageView.getLayoutParams();
                            params.height = t;
                            imageView.setLayoutParams(params);
                        }
                    }
                    scrollerType = false;
                }
                lastLy = currentY;
                //在移动的过程中, 既没有滚动到可以上拉的程度, 也没有滚动到可以下拉的程度
                if (!canPullDown && !canPullUp) {
                    startY = event.getY();
                    canPullDown = isCanPullDown();
                    canPullUp = false;
                    break;
                }
                //计算手指移动的距离
                float nowY = event.getY();
                int deltaY = (int) (nowY - startY);

                //是否应该移动布局
                boolean shouldMove =
                        (canPullDown && deltaY > 0)    //可以下拉, 并且手指向下移动
                                || (canPullUp && deltaY < 0)    //可以上拉, 并且手指向上移动
                                || (canPullUp && canPullDown); //既可以上拉也可以下拉(这种情况出现在ScrollView包裹的控件比ScrollView还小)
                if (shouldMove) {
                    //计算偏移量
                    int offset = (int) (deltaY * MOVE_FACTOR);

                    //随着手指的移动而移动布局
                    contentView.layout(originalRect.left, originalRect.top + offset,
                            originalRect.right, originalRect.bottom + offset);
                    isMoved = true;  //记录移动了布局
                }
                break;
            case MotionEvent.ACTION_UP:
                if (!isMoved) break;  //如果没有移动布局, 则跳过执行
                // 开启动画
                if (li[1] == li2[1]) {
                    scrollerType = true;
                    mScroller.startScroll(imageView.getLeft(), imageView.getBottom(), 0 - imageView.getLeft(),
                            imageViewH - imageView.getBottom(), DURATION);
                }
                TranslateAnimation anim = new TranslateAnimation(0, 0, contentView.getTop(),
                        originalRect.top);
                anim.setDuration(ANIM_TIME);
                anim.setInterpolator(new AccelerateDecelerateInterpolator());
                // 设置回到正常的布局位置
                contentView.startAnimation(anim);
                contentView.layout(originalRect.left, originalRect.top,
                        originalRect.right, originalRect.bottom);
                invalidate();
                startIsTop = true;
                //将标志位设回false
                canPullDown = false;
                canPullUp = false;
                isMoved = false;
                break;
        }
        return true;
    }
    @Override
    public void computeScroll() {
        super.computeScroll();
        if (mScroller.computeScrollOffset()) {
            int x = mScroller.getCurrX();
            int y = mScroller.getCurrY();
            imageView.layout(0, 0, x + imageView.getWidth(), y);
            invalidate();
            if (!mScroller.isFinished() && scrollerType && y > MAX_DY) {
                android.view.ViewGroup.LayoutParams params = imageView.getLayoutParams();
                params.height = y;
                imageView.setLayoutParams(params);
            }
        }
    }
    public class TouchTool {
        private int startX, startY;
        public TouchTool(int startX, int startY, int endX, int endY) {
            super();
            this.startX = startX;
            this.startY = startY;
        }
        public int getScrollX(float dx) {
            int xx = (int) (startX + dx / 2.5F);
            return xx;
        }
        public int getScrollY(float dy) {
            int yy = (int) (startY + dy / 2.5F);
            return yy;
        }
    }
    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        if (getChildCount() > 0) {
            contentView = getChildAt(0);
        }
    }
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        if (contentView == null) {
            return;
        }
        //ScrollView中的唯一子控件的位置信息, 这个位置信息在整个控件的生命周期中保持不变
        originalRect.set(contentView.getLeft(), contentView.getTop(), contentView
                .getRight(), contentView.getBottom());
    }
    //判断是否滚动到顶部
    private boolean isCanPullDown() {
        return getScrollY() == 0 ||
                contentView.getHeight() < getHeight() + getScrollY();
    }
    //判断是否滚动到底部
    private boolean isCanPullUp() {
        return contentView.getHeight() <= getHeight() + getScrollY();
    }
}

2.MainActivity主文件

public class MainActivity extends AppCompatActivity {
    DampView dampView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        dampView = findViewById(R.id.dv);
        dampView.setImageView((ImageView) findViewById(R.id.search_src_text));
    }

布局文件

            注意*:dampview只能有一个Child,当然子Child里面可以随便排列布局

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.example.asus.myapplication.MainActivity">
    <ImageView
        android:scaleType="centerCrop"
        android:id="@+id/search_src_text"
        android:src="@mipmap/ic_bg"
        android:layout_width="match_parent"
        android:layout_height="200dp" />
    <com.example.asus.myapplication.DampView
        android:id="@+id/dv"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    <RelativeLayout
       android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <LinearLayout
            android:layout_marginTop="50dp"
            android:gravity="center"
            android:orientation="vertical"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
            <TextView
                android:gravity="center"
                android:id="@+id/text"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@string/text"
                android:layout_gravity="center" />
        </LinearLayout>
    </RelativeLayout>
    </com.example.asus.myapplication.DampView>
</RelativeLayout>

效果图: