Imageview parallax as you scroll

后端 未结 2 2039
情书的邮戳
情书的邮戳 2020-12-13 03:42

I saw many examples demonstrative parallax background as you scroll or listview parallax but I cannot find a clear example how to implement a parallax effect on images as yo

相关标签:
2条回答
  • 2020-12-13 04:01
       scrollView.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {
            @Override
            public void onScrollChanged() {
                int top = scrollView.getScrollY(); // Increases when scrolling up ^
                if(top != 0) {
                    int newTop = (int) (top * .5f);
                    imageFrame.setTop(newTop < 0 ? 0 : newTop);
                }
            }
        });
    
    0 讨论(0)
  • 2020-12-13 04:03

    There are a few libraries that to a parallax effect, it depends on your app if they are useful for your particular case, for example:

    • ParallaxScroll
    • Paralloid

    Google is your friend pal ;) if none of these suits your needs then you have to create a custom ScrollView but that's a longer story, first give them a try and post your results.

    Edit

    If none of these fit your requeriments then this is what you have to do:

    First, create a custom ScrollView so you can listen to scroll changes.

    public class ObservableScrollView extends ScrollView {
    
        public interface OnScrollChangedListener {
            public void onScrollChanged(int deltaX, int deltaY);
        }
    
        private OnScrollChangedListener mOnScrollChangedListener;
    
        public ObservableScrollView(Context context) {
            super(context);
        }
    
        public ObservableScrollView(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    
        public ObservableScrollView(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
        }
    
        @Override
        protected void onScrollChanged(int l, int t, int oldl, int oldt) {
            super.onScrollChanged(l, t, oldl, oldt);
            if(mOnScrollChangedListener != null) {
                mOnScrollChangedListener.onScrollChanged(l - oldl, t - oldt);
            }
        }
    
        public void setOnScrollChangedListener(OnScrollChangedListener listener) {
            mOnScrollChangedListener = listener;
        }
    }
    

    Obviously you need to use this in your layout instead of the default ScrollView:

    <your.app.package.ObservableScrollView
            android:id="@+id/scroll_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
    

    Also you need to wrap your ImageView inside a container to make the parallax work:

    <FrameLayout
        android:id="@+id/img_container"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >
    
        <ImageView
            android:id="@+id/img"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:scaleType="centerCrop" />
    </FrameLayout>
    

    Finally set your Activity as a listener for your brand new ObservableScrollView and let the parallax begin:

    public class MyActivity extends Activity implements ObservableScrollView.OnScrollChangedListener {
        private ObservableScrollView mScrollView;
        private View imgContainer;
        @Override
        public void onCreate(Bundle savedInstanceState) {
            // Init your layout and set your listener
            mScrollView = (ObservableScrollView)findViewById(R.id.scroll_view);
            mScrollView.setOnScrollChangedListener(this);
            // Store the reference of your image container
            imgContainer = findViewById(R.id.img_container);
        }
    
         @Override
        public void onScrollChanged(int deltaX, int deltaY) {
            int scrollY = mScrollView.getScrollY();
            // Add parallax effect
            imgContainer.setTranslationY(scrollY * 0.5f);
        }
    }
    

    You can modify the 0.5 value depending on how much parallax you want.

    Edit

    The above answer works fine if your ImageView is in the top of the activity. I am posting some code below to add the functionality to have the ImageView anywhere in the activity layout which I successfully made to work. These are generic calculations (might have some mistakes) and with a little tweak you can have it working for your own case.

    For this example I have a fix height for the image container 200dp and for the image 240dp. The main purpose is when the image container is in the middle of the screen have no parallax effect and as the user scroll up or down to apply the effect. So as the image container get closer to the top of the screen or closer to the bottom of the screen the more of the parallax effect will be applied. The following calculations are a little hard to understand by reading them so try to make an example with real numbers in paper.

    public class MyActivity extends Activity implements ObservableScrollView.OnScrollChangedListener {
        private ObservableScrollView mScrollView;
        private View imgContainer;
        private ImageView mImageView;
        @Override
        public void onCreate(Bundle savedInstanceState) {
            // Init your layout and set your listener
            mScrollView = (ObservableScrollView)findViewById(R.id.scroll_view);
            mScrollView.setOnScrollChangedListener(this);
            // Store the reference of your image container
            imgContainer = findViewById(R.id.img_container);
            // Store the reference of your image
            mImageView =  findViewById(R.id.img);
        }
    
         @Override
        public void onScrollChanged(int deltaX, int deltaY) {
            // Get scroll view screen bound
            Rect scrollBounds = new Rect();
            mScrollView.getHitRect(scrollBounds);
    
            // Check if image container is visible in the screen
            // so to apply the translation only when the container is visible to the user
            if (imgContainer.getLocalVisibleRect(scrollBounds)) {
                Display display = getWindowManager().getDefaultDisplay();
                DisplayMetrics outMetrics = new DisplayMetrics ();
                display.getMetrics(outMetrics);
                // Get screen density
                float density  = getResources().getDisplayMetrics().density;
    
                // Get screen height in pixels            
                float dpHeight = outMetrics.heightPixels / density;
                int screen_height_pixels = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpHeight, getResources().getDisplayMetrics());
                int half_screen_height = screen_height_pixels/2;
    
                // Get image container height in pixels
                int container_height_pixels = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 200, getResources().getDisplayMetrics());
    
                // Get the location that consider a vertical center for the image (where the translation should be zero)
                int center = half_screen_height - (container_height_pixels/2);
    
                // get the location (x,y) of the image container in pixels
                int[] loc_screen = {0,0};
                imgContainer.getLocationOnScreen(loc_screen);
    
                // trying to transform the current image container location into percentage
                // so when the image container is exaclty in the middle of the screen percentage should be zero
                // and as the image container getting closer to the edges of the screen should increase to 100%
                int final_loc = ((loc_screen[1]-center)*100)/half_screen_height;
    
                // translate the inner image taking consideration also the density of the screen
                mImageView.setTranslationY(-final_loc * 0.4f * density);
            }
        }
    }
    

    I hope it can help someone that is looking for similar functionality.

    0 讨论(0)
提交回复
热议问题