android - android滚动时如何拉动类似于弹性类型的布局?

标签 android animation android-animation android-scrollview bounce

我的每个布局看起来都像嵌入在 Scrollview 中的卡片类型。当我滚动时,如何像 Spring 一样移动卡片,然后落到原始位置。

就像卡片在弹跳一样。在我的布局中,顶部是ScrollView,下面集成了5到6个布局。

当我滚动时,如何将这些添加的布局向上移动一点并到达它的位置?您可以在此处查看示例。看看单独滚动的地方 卡片从其位置移动。只需查看 screenshot请注意滚动,当向上和向下滚动时,卡片会像 Spring 类型一样移动,即滚动时卡片之间的间距会减小。如何实现这一目标?

在当前应用的CustomScrollView中,它具有过度滚动功能,但我无法实现 Spring 式可移动卡片。

这是我应用的代码。

  <android.support.design.widget.CoordinatorLayout     xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/coordinatorLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".LaunchActivity">
    <RelativeLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.mobile.ui.MyImageView
        android:id="@+id/backgroundImage_timeline"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@drawable/bg_dot_line"
        android:scaleType="centerCrop" />


    <com.mobile.ui.view.ObservableScrollView
        android:id="@+id/scrollView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".LaunchActivity">

        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <LinearLayout
                android:id="@+id/commonModules"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_below="@id/anotherModule"
                android:orientation="vertical">

                <com.mobile.ui.FirstModule //this is one layout
                    android:id="@+id/myFirstModule"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:visibility="visible" />
                <---second layout-----> and so on..
        </RelativeLayout>
    </com.mobile.ui.view.ObservableScrollView>

  </RelativeLayout>

 public class ObservableScrollView extends ScrollView {
private View inner;

private float y;

private Rect normal = new Rect();

private boolean isCount = false;

public interface OnOverScrolledListener {
    void onOverScrolled(android.widget.ScrollView scrollView,
                        int deltaX, int deltaY, boolean clampedX, boolean clampedY);
}

private OnOverScrolledListener mOnOverScrolledListener;

private int mOverScrollByDeltaX;
private int mOverScrollByDeltaY;

@Override protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) {
    this.mOverScrollByDeltaX = deltaX;
    this.mOverScrollByDeltaY = deltaY;
    final boolean result = super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent);
    return result;
};
@Override
protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) {
    super.onOverScrolled(scrollX, scrollY, clampedX, clampedY);
    if (mOnOverScrolledListener != null && (clampedX || clampedY)) {
        mOnOverScrolledListener.onOverScrolled(this, mOverScrollByDeltaX, mOverScrollByDeltaY, clampedX, clampedY);
    }
}

public OnOverScrolledListener getOnOverScrolledListener() {
    return mOnOverScrolledListener;
}

public void setOnOverScrolledListener(OnOverScrolledListener onOverScrolledListener) {
    this.mOnOverScrolledListener = onOverScrolledListener;
}

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

@SuppressLint("MissingSuperCall")
@Override
protected void onFinishInflate() {
    if (getChildCount() > 0) {
        inner = getChildAt(0);
    }
}

@Override
public boolean onTouchEvent(MotionEvent ev) {
    if (inner != null) {
        commOnTouchEvent(ev);
    }

    return super.onTouchEvent(ev);
}

public void commOnTouchEvent(MotionEvent ev) {
    int action = ev.getAction();
    switch (action) {
        case MotionEvent.ACTION_DOWN:
            break;
        case MotionEvent.ACTION_UP:
            if (isNeedAnimation()) {
                animation();
                isCount = false;
            }
            break;

        case MotionEvent.ACTION_MOVE:
            final float preY = y;
            float nowY = ev.getY();
            int deltaY = (int) (preY - nowY);
            if (!isCount) {
                deltaY = 0;
            }

            y = nowY;
            if (isNeedMove()) {
                if (normal.isEmpty()) {

                    normal.set(inner.getLeft(), inner.getTop(),
                            inner.getRight(), inner.getBottom());
                }
                inner.layout(inner.getLeft(), inner.getTop() - deltaY / 2,
                        inner.getRight(), inner.getBottom() - deltaY / 2);
            }
            isCount = true;
            break;

        default:
            break;
    }
}

public void animation() {
    TranslateAnimation ta = new TranslateAnimation(0, 0, inner.getTop(),
            normal.top);
    ta.setDuration(200);
    inner.startAnimation(ta);
    inner.layout(normal.left, normal.top, normal.right, normal.bottom);

    normal.setEmpty();

}

public boolean isNeedAnimation() {
    return !normal.isEmpty();
}

public boolean isNeedMove() {
    int offset = inner.getMeasuredHeight() - getHeight();
    int scrollY = getScrollY();
    if (scrollY == 0 || scrollY == offset) {
        return true;
    }
    return false;
}

}

我尝试过的替代方案是,

 if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
        scrollView.setOnScrollChangeListener(new View.OnScrollChangeListener() {
            @Override
            public void onScrollChange(View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {
                View view = (View) scrollView.getChildAt(scrollView.getChildCount() - 1);
                int diff = (view.getBottom() - (scrollView.getHeight() + scrollView.getScrollY()));
                if(view.getTop()==scrollY){
                    // reaches the top end
                    Log.e("scrolled top","scrolling top");
                }


                // if diff is zero, then the bottom has been reached
                if (diff == 0) {
                    // do stuff L
                    Log.e("bottom reached","bottom reached");
                }
                else {
                    TranslateAnimation animation = new TranslateAnimation(0f, 0f, 0f, -100f);  // might need to review the docs
                    animation.setDuration(500); // set how long you want the animation
                    animation.setFillAfter(true);
                    myFirstModule.startAnimation(animation);
                     mySecondModule.startAnimation(animation);
       //till 10..
               }

            }
        });
    }

但这在滚动期间不会应用。滚动和 View 上升后动画有一些延迟,但效果并不完美。

最佳答案

我找到了解决方案:

public class ObservableScrollView extends ScrollView {
    private ScrollCallbacks mCallbacks;

    private static final int MAX_Y_OVERSCROLL_DISTANCE = 150;

    private Context mContext;
    private int mMaxYOverscrollDistance;

    public ObservableScrollView(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.mContext = context;
        initBounceScrollView();
    }

    @Override
    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
        super.onScrollChanged(l, t, oldl, oldt);
        if (mCallbacks != null) {
            mCallbacks.onScrollChanged(l, t, oldl, oldt);
        }
    }

    @Override
    public int computeVerticalScrollRange() {
        return super.computeVerticalScrollRange();
    }

    public void setCallbacks(ScrollCallbacks listener) {
        mCallbacks = listener;
    }

    private void initBounceScrollView() {
        // get the density of the screen and do some maths with it on the max
        // overscroll distance
        // variable so that you get similar behaviors no matter what the screen
        // size

        final DisplayMetrics metrics = mContext.getResources()
                .getDisplayMetrics();
        final float density = metrics.density;

        mMaxYOverscrollDistance = (int) (density * MAX_Y_OVERSCROLL_DISTANCE);

    }

    @Override
    public void draw(Canvas canvas) {
        super.draw(canvas);
    }

    public interface ScrollCallbacks {
        void onScrollChanged(int l, int t, int oldl, int oldt);
    }

    @SuppressLint("NewApi")
    @Override
    protected boolean overScrollBy(int deltaX, int deltaY, int scrollX,
                                   int scrollY, int scrollRangeX, int scrollRangeY,
                                   int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) {
        // This is where the magic happens, we have replaced the incoming
        // maxOverScrollY with our own custom variable mMaxYOverscrollDistance;
        return super.overScrollBy(deltaX, deltaY, scrollX, scrollY,
                scrollRangeX, scrollRangeY, maxOverScrollX,
                mMaxYOverscrollDistance, isTouchEvent);
    }
}

主要 Activity :

     scrollView.setCallbacks(new ObservableScrollView.ScrollCallbacks() {
                @Override
                public void onScrollChanged(int l, int t, int oldl, int oldt) {
                Rect scrollBounds = new Rect();
                    scrollView.getHitRect(scrollBounds);

   if (myFirstModule.getLocalVisibleRect(scrollBounds) ){
                    if(!myFirstModule.isAnim) {
                        if (myFirstModule.getY() < scrollBounds.top) {
                            myFirstModule.setAnimationView(2);
                        } else {
                            myFirstModule.setAnimationView(1);
                        }
                    }
                    }else{
                        myFirstModule.isAnim = false; //myFirstModule is layout. In my case, I used layout in separate class.
                    }

在第一个模块中:

// in firstModule i integrated layout which i skipped.. I am focusing only on main thing..

   public boolean isAnim = false;
    private Animation animSlideUp = null;
    private Animation animSlideDown = null;
    private void loadAnimation(){
        animSlideUp = AnimationUtils.loadAnimation(getContext(), R.anim.bottom_to_top);
        animSlideDown = AnimationUtils.loadAnimation(getContext(), R.anim.top_to_bottom);
    }
   //this i will use in mainactivity. 
    public void setAnimationView(int animType){
        if(isVisible()) {
            isAnim = true;
            if (animType == 1) {
                startAnimation(animSlideUp);
            } else {
                startAnimation(animSlideDown);
            }
        }
    }

bottom_to_top:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:shareInterpolator="@android:anim/decelerate_interpolator">
    <translate
        android:fromXDelta="0%" android:toXDelta="0%"
        android:fromYDelta="300" android:toYDelta="0"
        android:duration="400" />
</set>

从上到下:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:shareInterpolator="@android:anim/decelerate_interpolator">
    <translate
        android:fromXDelta="0%" android:toXDelta="0%"
        android:fromYDelta="-300" android:toYDelta="0"
        android:duration="400" />
</set>

就是这样。 :-) 无需任何库即可实现。

关于android - android滚动时如何拉动类似于弹性类型的布局?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40797810/

相关文章:

android - 将图像裁剪成圆形

swift - SwiftUI 中的动画绑定(bind)

android - 使用 AndEngine 播放动画

android - 在 Fling 事件上制作动画

Android ListView 滚动问题

android - 如何在android中创建一个微调器?

android - 双向滚动 ListView ?

ios - 我在动画结束后是否太早释放该对象?

c# - 如何在 Silverlight 中为 zIndex 属性设置动画?

Android动画TextView从页面中间到顶部