当你滚动时的图像视图视差效果

18

我看到很多例子演示了随着滚动或列表视图的视差背景,但是我找不到一个明确的例子如何在活动中实现图像的滚动视差效果。

Airbnb应用程序中可以找到一个实现示例。当您向下滚动时,可以看到更多图像底部的内容,而向上滚动时则可以看到更多图像顶部的内容。

如何创建这样的效果的任何提示和技巧?

输入图像描述 输入图像描述


1
搜索似乎提供了很多。 - user1932079
2个回答

47

有一些库可以实现视差效果,但它们是否适用于您的应用程序取决于您的特定情况,例如:

如果以上这些都不符合您的需求,那么谷歌是您的好朋友;) 您需要创建一个自定义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);
        }
    }
}

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


谢谢你的回答。我已经看到了它们,但是因为我无法清楚地看到我正在寻找的功能,所以我决定问一下,以防我错过了某个库!但你说得对,我必须花时间先尝试它们。 - xinaris
感谢您的帮助!我实际上有一个类似的代码,用于在活动顶部创建视差效果的图像,它运行良好。我的问题实际上是想要将视差效果应用于滚动视图中任何位置的imageview。您的代码对此无效,但您的指导很好。解决方案将类似于您的代码,但需要进行一些数学计算,以确定imageview在scrollview中的当前位置。 - xinaris
1
我在@peguerosdc的答案中添加了一些代码,以获得我所要求的功能。感谢您花费时间处理此事。 - xinaris
是的,我已经初始化了它,并且提供的ID是正确的。 - Sumit
非常感谢您提供的示例,易于理解并且完美运行! - LS_
显示剩余5条评论

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

当快速移动时,这会在ImageView顶部产生间隙。 - shkschneider
移除 top != 0 的检查将修复快速滚动时出现在顶部的间隙。 - jacoballenwood
移除顶部!=0后,当ImageView快速移动时仍会在顶部产生间隙。还有其他解决方案吗? - Neal

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接