滚动时的Imageview视差

问题描述 投票:15回答:4

[在滚动或列表视图视差时,我看到了很多示例性视差背景,但是当您在活动中滚动时,我找不到清晰的示例如何在图像上实现视差效果。

可以在Airbnb应用中找到示例实现。向下滚动时,您可以看到更多图片的底部,而向上滚动时,您可以看到更多图片的顶部。

关于如何产生这种效果的任何提示和技巧?

<< img src =“ https://image.soinside.com/eyJ1cmwiOiAiaHR0cHM6Ly9pLnN0YWNrLmltZ3VyLmNvbS9pZjlpQy5wbmcifQ==” alt =“在此处输入图像描述”>“在此处输入图像描述”

android
4个回答
47
投票

有一些具有视差效果的库,如果它们对您的特定情况有用,则取决于您的应用程序,例如:

[Google是您的好朋友;)如果这些都不适合您的需要,那么您必须创建一个自定义ScrollView,但这是一个较长的故事,首先请他们尝试一下并发布您的结果。

编辑

如果这些都不适合您的要求,那么这就是您要做的:

首先,创建一个自定义ScrollView,以便您可以收听滚动更改。

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

显然,您需要在布局中使用它,而不是默认的ScrollView

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

此外,您还需要将ImageView包装在容器中以使视差正常工作:

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

最后将您的Activity设置为全新ObservableScrollView的听众,并让视差开始:

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

您可以根据所需的视差来修改0.5值。

编辑

如果您的ImageView位于活动顶部,则上述答案会很好。我在下面发布了一些代码以添加功能,以使ImageView在我成功制作的活动布局中的任何位置。这些是通用计算(可能会有一些错误),您只需稍作调整即可使其适用于您自己的情况。

对于此示例,我对图像容器200dp和图像240dp具有固定高度。主要目的是当图像容器在屏幕中间时没有视差效果,并且当用户向上或向下滚动以应用效果时。因此,随着图像容器越来越靠近屏幕顶部或距离屏幕底部,将会应用更多的视差效果。通过阅读下面的计算有些难以理解,因此请尝试使用纸上的实数作为示例。

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

我希望它可以帮助正在寻找类似功能的人。


1
投票
   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
投票

enter image description here

@Override
     public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,int totalItemCount)                                                                                             
              {
                if (listView.getFirstVisiblePosition() == 0) {
                View firstChild = listView.getChildAt(0);
                int topY = 0;
                if (firstChild != null) {
                    topY = firstChild.getTop();
                }

                int heroTopY = stickyViewSpacer.getTop();
                stickyView.setY(Math.max(0, heroTopY + topY));
                imageView.setY(topY * 0.5f);
            }
        }
    });

有关更多参考,请单击此处http://androiddhina.blogspot.in/2015/08/listview-header-parallax-with-sticky-view-in-android.html


0
投票

这是基于@DKIT的答案,但有一些改进,位于Kotlin。这也将应用Alpha动画,这似乎与AppBars的视差滚动非常接近。

   binding?.scroll?.viewTreeObserver?.addOnScrollChangedListener {
        val top = binding?.scroll?.scrollY ?: 0
        binding?.banner?.apply {
            val newTop = (top * .5f).toInt()
            this.top = if (newTop < 0) 0 else newTop
            this.alpha = 1 - (newTop.toFloat() / height)
        }
    }

其中scroll是您的ScrollViewbanner是您的ImageView。如果您不使用数据绑定,则显然可以使用findViewById。

© www.soinside.com 2019 - 2024. All rights reserved.