Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Parallax effect on each item in a recycler view?

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.

like image 387
RED_ Avatar asked Apr 18 '16 09:04

RED_


2 Answers

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.

like image 146
RED_ Avatar answered Oct 19 '22 23:10

RED_


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
}
like image 30
sockeqwe Avatar answered Oct 20 '22 01:10

sockeqwe