29

I have created a scrollview and an imageview is placed within it. I'd like on scroll for it to resize in the same fashion it is done in the image below but so far I've had little success.

In my attempts the image is resized on scroll but, there is space left over after the resize. How would you modify the below:

Image:

enter image description here

My Code so far:

activity_main.xml

<ImageView
            android:layout_gravity="center"
            android:adjustViewBounds="true"
            android:layout_width="601dp"
            android:layout_height="250dp"
            android:paddingTop="0dp"
            android:paddingLeft="0dp"
            android:paddingRight="0dp"
            android:scaleType="fitXY"
            android:id="@+id/contactPic"
            android:src="@drawable/stock"
            android:clickable="true"/>

MainActivity:

@Override
public void onScrollChanged(ObservableScrollView scrollView, int x, int y, int oldx, int oldy) {
    final ImageView contactPicture = (ImageView) findViewById(R.id.contactPic);
    final RelativeLayout contactLayout = (RelativeLayout) findViewById(R.id.ContactRLayout);
    if (scrollView == contactScrollView) {
        View view = (View) scrollView.getChildAt(scrollView.getChildCount() - 1);
        int distanceFromPageEnd = (view.getBottom() - (scrollView.getHeight() + scrollView.getScrollY()));

        Log.e("onScrollChanged", "distance from bottom = " + String.valueOf(distanceFromPageEnd));
        if (distanceFromPageEnd >= 1408)
        {
       contactPicture.getLayoutParams().height = (distanceFromPageEnd - 1408);
        contactPicture.requestLayout();
        }
    }

ScrollViewListener:

    public interface ScrollViewListener {

        void onScrollChanged(ObservableScrollView scrollView, int x, int y, int oldx, int oldy);

    }

ObservableScrollView:

public class ObservableScrollView extends ScrollView {

    private ScrollViewListener scrollViewListener = null;

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

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

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

    public void setScrollViewListener(ScrollViewListener scrollViewListener) {
        this.scrollViewListener = scrollViewListener;
    }

    public void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) {
        super.onOverScrolled(scrollX, scrollY, clampedX, clampedY);
    }

    @Override
    protected void onScrollChanged(int x, int y, int oldx, int oldy) {
        super.onScrollChanged(x, y, oldx, oldy);
        if(scrollViewListener != null) {
            scrollViewListener.onScrollChanged(this, x, y, oldx, oldy);
        }
    }

}
  • 1
    check iosched app source ... – Selvin Jan 19 '15 at 15:01
  • The project seems to fail when it builds and when I visit the xml you described all that is shown on the page is the word speakers with a divider above it. So far the project you suggested has been of no help. Do you not know how to solve my problem –  Jan 19 '15 at 15:27
  • That must be a parallax scrollview – surhidamatya Apr 16 '15 at 05:31
  • You shouldn't be resizing the ImageView, rather you should be scrolling or translating it in relation to the ScrollView. This way it will keep a consistent size. – Andrea Thacker Apr 16 '15 at 05:37
  • Try to use android:scaleType="centerCrop" – yoah Apr 16 '15 at 05:39
  • 1
    It is a parallax effect. You should use something like parallax scroll library https://github.com/nirhart/ParallaxScroll and fading action bar https://github.com/ManuelPeinado/FadingActionBar to get the exact effect – Amrut Bidri Apr 16 '15 at 05:46
  • [This resource](https://github.com/ksoichiro/Android-ObservableScrollView) has proven to be very valuable for me on different occasions. The Parallax ScrollView and Toolbar demo might be the one for you but the source code is easily understandable and customizable. – appoll Apr 20 '15 at 12:48
  • Check this out If it helps http://stackoverflow.com/questions/25424818/how-to-make-a-actionbar-like-google-play-that-fades-in-when-scrolling – Sagar Devanga Apr 22 '15 at 07:10
  • You can use this library https://github.com/ksoichiro/Android-ObservableScrollView – WISHY Apr 23 '15 at 04:59

1 Answers1

29

There is simple example below that shows how to achieve parallax effect.

First, put your ImageView and other views into FrameLayout:

<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/scrollView"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <FrameLayout
        android:id="@+id/flWrapper"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <ImageView
            android:id="@+id/contactPic"
            android:layout_width="match_parent"
            android:layout_height="@dimen/contact_photo_height"
            android:scaleType="centerCrop"
            android:src="@drawable/stock" />

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="@dimen/contact_photo_height">

            <!-- Other Views -->

        </LinearLayout>

    </FrameLayout>
</ScrollView>

LinearLayout's top margin is equal to the ImageViews's height (@dimen/contact_photo_height in our example).

Then we should listen scroll position of the ScrollView and change the position of ImageView:

@Override
protected void onCreate(Bundle savedInstanceState) {
    <...>

    mScrollView = (ScrollView) findViewById(R.id.scrollView);
    mPhotoIV = (ImageView) findViewById(R.id.contactPic);
    mWrapperFL = (FrameLayout) findViewById(R.id.flWrapper);

    mScrollView.getViewTreeObserver().addOnScrollChangedListener(new ScrollPositionObserver());

    <...>
}

private class ScrollPositionObserver implements ViewTreeObserver.OnScrollChangedListener {

    private int mImageViewHeight;

    public ScrollPositionObserver() {
        mImageViewHeight = getResources().getDimensionPixelSize(R.dimen.contact_photo_height);
    }

    @Override
    public void onScrollChanged() {
        int scrollY = Math.min(Math.max(mScrollView.getScrollY(), 0), mImageViewHeight);

        // changing position of ImageView
        mPhotoIV.setTranslationY(scrollY / 2);

        // alpha you could set to ActionBar background
        float alpha = scrollY / (float) mImageViewHeight;
    }
}
erakitin
  • 11,437
  • 5
  • 44
  • 49
  • I thought frame layout could only have one child. – Suragch Apr 18 '15 at 07:19
  • 1
    @Suragch frame layouts can have multiple children. thats the point of a frame layout, it layers every child layout on top of each other – Tomer Shemesh Apr 21 '15 at 21:04
  • Ok, I just hadn't used it this way before. From the documentation: "FrameLayout is designed to block out an area on the screen to display a single item. Generally, **FrameLayout should be used to hold a single child view**, because it can be difficult to organize child views in a way that's scalable to different screen sizes without the children overlapping each other. **You can, however, add multiple children to a FrameLayout** and control their position within the FrameLayout by assigning gravity to each child, using the android:layout_gravity attribute." – Suragch Apr 22 '15 at 00:14
  • Happy to see that this works, but it reduces frame rate by an unacceptable amount. Anybody know a way to improve upon this? – JillevdW Apr 09 '18 at 13:11