我在滚动或列表视图视差时看到了许多示例性视差背景示例,但我无法找到一个清晰的示例,当您在活动中滚动时如何对图像实现视差效果。
Airbnb应用程序中可以找到示例实现。当您向下滚动时,您可以看到更多图像的底部,当您向上滚动时,您可以看到更多图像的顶部。
有关如何创建此类效果的任何提示和提示?
答案 0 :(得分: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 :(得分:0)
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);
}
}
});
答案 2 :(得分:0)
@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
答案 3 :(得分: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
是您的ScrollView
,而banner
是您的ImageView
。
如果您不使用数据绑定,则显然可以使用findViewById。