2

I have every layout which looks as card type embedded in Scrollview. When I scroll, how to move the card like spring type and then come down to original position.

It is like cards are bouncing. In my layout, the top of that is ScrollView, below that 5 to 6 layouts are integratted.

When I scroll how to move those added layouts little bit upper and come to it's position? You can see an example here. Just look the scrolling alone where cards get moved from it's position. Just see the screenshot notice the scrolling where cards get moved like spring type when scroll up and down ie spacing between the cards get reduce when scrolling. How to achieve that?

In currently applied CustomScrollView, it has overscroll but I couldn't achieve spring type moveable cards.

Here is code which I applied.

  <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;
}

}

So alternative, which I tried is,

 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..
               }

            }
        });
    }

But this is not getting applied during scrolling. There is some delay in the animation after scrolling and view goes up and not in a perfect one.

Shadow
  • 6,864
  • 6
  • 44
  • 93

1 Answers1

3

I found a solution:

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);
    }
}

MainActivity:

     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:

// 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>

top to bottom:

<?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>

That's it. :-) Can achieve without any library.

halfer
  • 19,824
  • 17
  • 99
  • 186
Shadow
  • 6,864
  • 6
  • 44
  • 93