Parallax effect on each item in a recycler view?
Asked Answered
P

2

9

I'm trying to play with Parallax and getting some weird bugs, wondering if anyone can add some input to it. The only app I've seen implement parallax effectively is soundcloud. It's quite subtle, but each item has an image background and it has he parallax effect as you scroll.

I've created a custom RecyclerView to handle this, here is what I have so far:

public class ParallaxScrollListener extends RecyclerView.OnScrollListener {

private float scrollSpeed = 0.5f;

@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
    super.onScrolled(recyclerView, dx, dy);
    LinearLayoutManager layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();

    int firstVisible = layoutManager.findFirstVisibleItemPosition();
    int visibleCount = Math.abs(firstVisible - layoutManager.findLastVisibleItemPosition());

    Matrix imageMatrix;
    float tempSpeed = -100;

    if (dy > 0) {
        tempSpeed = scrollSpeed;
    } else if (dy < 0) {
        tempSpeed = -scrollSpeed;
    }

    for (int i = firstVisible; i < (firstVisible + visibleCount); i++) {
        ImageView imageView = ((MyClass.MyAdapter.MyViewHolder) recyclerView.getLayoutManager().findViewByPosition(i).getTag()).image;
        if (imageView != null) {
            imageMatrix = imageView.getImageMatrix();
            imageMatrix.postTranslate(0, tempSpeed);
            imageView.setImageMatrix(imageMatrix);
            imageView.invalidate();
        }
    }
}

In my RecyclerView Adapter's onBindView I have the following as well:

 Matrix matrix = viewHolder.image.getImageMatrix();
 matrix.postTranslate(0, 0);
 viewHolder.image.setImageMatrix(matrix);
 viewHolder.itemView.setTag(viewHolder);

Finally inside the onViewRecycled method I have the following:

@Override
    public void onViewRecycled(MyViewHolder viewHolder) {
        super.onViewRecycled(viewHolder);
        if (viewHolder.image != null) {
            viewHolder.image.setScaleType(ImageView.ScaleType.MATRIX);
            Matrix matrix = viewHolder.image.getImageMatrix();
            // this is set manually to show to the center
            matrix.reset();
            viewHolder.image.setImageMatrix(matrix);
        }
}

I been working with this code on Github to get the idea

So the parallax works, but but views in my RecyclerView move as well. I have a CardView beneath the image and it moves, creating big gaps between each item. Scrolling is what causes this, the more the scroll up and down the bigger the gaps get, and the images get smaller as the parallax moves them out of their bounds.

I've tried messing with the numbers like scrollSpeed in the OnScrollListener but while it reduces the bug it also reduces the parallax.

Has anyone got any ideas on how I can achieve a bug free parallax effect on each item in my RecyclerView? I feel like I'm getting somewhere with this but it's still very buggy and I don't know what the next step is.

P.s I've tried looking at 3rd party libraries but they all seem to only use header parallax like the CoordinatorLayout, I haven't found any that do it just on each item in a list.

I'm hoping this question gets a good discussion going even if I don't solve my problem because Parallax seems to be underused in Android and there's very little around about it.

Thanks for you time, appreciate any help.

Paries answered 18/4, 2016 at 9:29 Comment(2)
Is the image that should be parallaxed the same on every view? Or do you want / need to be able to set it for each item in onBindVH?Buoyage
Different images, so yeah they need to be set each time. I found a library that does it, will post my answer soon.Paries
P
4

I managed to get Parallax working with this library: https://github.com/yayaa/ParallaxRecyclerView

For anyone doing this themselves, it's still a good thing to play and see how it works.

Similar concept to my code but it actually works! haha.

Paries answered 22/4, 2016 at 9:21 Comment(0)
K
2

You are on the right track. You have to use a ScrollListener. Furtermore, you have to access RecyclerViews LayoutManager and iterate over all items that are visible and set translateY according to the amount of pixels scrolled.

The things get a little bit more complicated, because you can't use recyclerView.getChildAt(pos) because LayoutManager is responsible to layout elements and they might be in different order in the LayoutManager than getChildAt(pos).

So the algorithm basically should look like this (pseudo code, assuming LinearLayoutManager is used):

for (int i = layoutManager.findFirstVisibleItemPosition(); i <= layoutmanager.findLastVisibleItemPosition; i++){

   // i is the adapter position

   ViewHolder vh = recyclerView.findViewHolderForAdapterPosition(i);
   vh.imageView.setTranslationY( computedParalaxOffset ); // assuming ViewHolder has a imageView field on which you want to apply the parallax effect
}
Katiakatie answered 20/4, 2016 at 17:1 Comment(3)
Thanks for replying. Isn't this what I already have though? In my scroll listener I am using the LinearLayoutManager to compute the views. Using setTranslationY doesn't seem to do anything either. postTranslate creates the parallax and works but I still have the bug outlined in my question.Paries
You are right, I have overlooked that. my bad. So your question basically is how to compute computedParalaxOffset value, right?Katiakatie
I found a library in the end that was able to sort everything out. Seems like they required a custom imageview which I wasn't using which is probably part of my problem.Paries

© 2022 - 2024 — McMap. All rights reserved.