简体   繁体   中英

How to scale up Recycler View center item while scrolling in android?

我需要在通过放大滚动时始终突出显示回收器视图中的中心项目。

You should follow this code, this helped me to scale up the center item in recycler view.

public class CenterZoomLayoutManager extends LinearLayoutManager {

    private final float mShrinkAmount = 0.15f;
    private final float mShrinkDistance = 0.9f;

    public CenterZoomLayoutManager(Context context) {
        super(context);
    }

    public CenterZoomLayoutManager(Context context, int orientation, boolean reverseLayout) {
        super(context, orientation, reverseLayout);
    }

    @Override
    public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
        int orientation = getOrientation();
        if (orientation == VERTICAL) {
            int scrolled = super.scrollVerticallyBy(dy, recycler, state);
            float midpoint = getHeight() / 2.f;
            float d0 = 0.f;
            float d1 = mShrinkDistance * midpoint;
            float s0 = 1.f;
            float s1 = 1.f - mShrinkAmount;
            for (int i = 0; i < getChildCount(); i++) {
                View child = getChildAt(i);
                float childMidpoint =
                        (getDecoratedBottom(child) + getDecoratedTop(child)) / 2.f;
                float d = Math.min(d1, Math.abs(midpoint - childMidpoint));
                float scale = s0 + (s1 - s0) * (d - d0) / (d1 - d0);
                child.setScaleX(scale);
                child.setScaleY(scale);
            }
            return scrolled;
        } else {
            return 0;
        }
    }

    @Override
    public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, RecyclerView.State state) {
        int orientation = getOrientation();
        if (orientation == HORIZONTAL) {
            int scrolled = super.scrollHorizontallyBy(dx, recycler, state);

            float midpoint = getWidth() / 2.f;
            float d0 = 0.f;
            float d1 = mShrinkDistance * midpoint;
            float s0 = 1.f;
            float s1 = 1.f - mShrinkAmount;
            for (int i = 0; i < getChildCount(); i++) {
                View child = getChildAt(i);
                float childMidpoint =
                        (getDecoratedRight(child) + getDecoratedLeft(child)) / 2.f;
                float d = Math.min(d1, Math.abs(midpoint - childMidpoint));
                float scale = s0 + (s1 - s0) * (d - d0) / (d1 - d0);
                child.setScaleX(scale);
                child.setScaleY(scale);
            }
            return scrolled;
        } else {
            return 0;
        }

    }
}

center horizontal view在此处输入图片说明

I slimmed down that solution and added the initial resize during onLayoutComplete . I didn't need vertical scrolling, so I took that part out.

class CenterZoomLinearLayoutManager(
    context: Context,
    private val mShrinkDistance: Float = 0.9f,
    val mShrinkAmount: Float = 0.15f
) : LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false) {

    override fun onLayoutCompleted(state: RecyclerView.State?) {
        super.onLayoutCompleted(state)
        scaleChildren()
    }

    override fun scrollHorizontallyBy(dx: Int, recycler: RecyclerView.Recycler?, state: RecyclerView.State?): Int {
        return if (orientation == HORIZONTAL) {
            super.scrollHorizontallyBy(dx, recycler, state).also { scaleChildren() }
        } else {
            0
        }
    }

    private fun scaleChildren() {
        val midpoint = width / 2f
        val d1 = mShrinkDistance * midpoint
        for (i in 0 until childCount) {
            val child = getChildAt(i) as View
            val d = Math.min(d1, Math.abs(midpoint - (getDecoratedRight(child) + getDecoratedLeft(child)) / 2f))
            val scale = 1f - mShrinkAmount * d / d1
            child.scaleX = scale
            child.scaleY = scale
        }
    }
}

See https://github.com/pcholt/toy-card-carousel

As an addendum to Mayank Garg's answer and the comments it has saying that it does not work for the first or last items , this happens when you are using this class and also adding extra padding to the list at the beginning and end in order for the first item to appear already centered. In these situations, functions like getDecoratedRight() and getDecoratedLeft() will include the extra padding in the sizes they return. This messes up the calculation of the view's midpoint, and hence it won't work for the first and last items.

A solution to this is to detect if the layout manager is displaying the beginning of the list or not, and use a conditional to use a different calculation which uses one of the decorated anchors as origin but then uses the view's halved width in order to find the midpoint.

In other words, in Mayank's code you have:

childMidpoint =
   (getDecoratedRight(child) + getDecoratedLeft(child)) / 2.f;

You can replace this with something similar to the following:

if (findFirstVisibleItemPosition() == 0 && i == 0) {
   childMidPoint = getDecoratedRight(child) - child.getWidth() / 2.f;
} else {
   childMidPoint = getDecoratedLeft(child) + child.getWidth() / 2.f;
}

In other words, this checks that the first child view is, or isn't, the first item in the adapter, and if so uses either the left or right decorated horizontal position to calculate the midpoint by subtracting or adding the item's halved width.

Another simpler alternative is:

childMidpoint = child.getX() + child.getWidth() / 2.0f

But then again, you need to test if this fits other constraints you may have on your layout/views, since there is probably a reason Mayank used getDecoratedLeft() instead of getX() .

Well, following Anarchofascist and Mayank, just add this override at the biggining of Mayank code to make the effect work with the first element.

@Override
public void onLayoutCompleted(RecyclerView.State state) {
    super.onLayoutCompleted(state);

    //aqui ele executa o codigo no estado inicial, originalmente ele nao aplicava no inicio
    //este codigo é em horizontal. Para usar em vertical apagar e copiar o codigo
    int orientation = getOrientation();
    if (orientation == HORIZONTAL) {

        float midpoint = getWidth() / 2.f;
        float d0 = 0.f;
        float d1 = mShrinkDistance * midpoint;
        float s0 = 1.f;
        float s1 = 1.f - mShrinkAmount;
        for (int i = 0; i < getChildCount(); i++) {
            View child = getChildAt(i);
            float childMidpoint =
                    (getDecoratedRight(child) + getDecoratedLeft(child)) / 2.f;
            float d = Math.min(d1, Math.abs(midpoint - childMidpoint));
            float scale = s0 + (s1 - s0) * (d - d0) / (d1 - d0);
            child.setScaleX(scale);
            child.setScaleY(scale);
        }

    } else {

        float midpoint = getHeight() / 2.f;
        float d0 = 0.f;
        float d1 = mShrinkDistance * midpoint;
        float s0 = 1.f;
        float s1 = 1.f - mShrinkAmount;
        for (int i = 0; i < getChildCount(); i++) {
            View child = getChildAt(i);
            float childMidpoint =
                    (getDecoratedBottom(child) + getDecoratedTop(child)) / 2.f;
            float d = Math.min(d1, Math.abs(midpoint - childMidpoint));
            float scale = s0 + (s1 - s0) * (d - d0) / (d1 - d0);
            child.setScaleX(scale);
            child.setScaleY(scale);
        }

    }
}

This method worked for me, and the problem I solved was that when RecyclerView was created, the items on the scale were not applied to them, and let me also say that the numbers I used were based on my needs, and you based on Need to adjust the scale yourself

public class CenterZoomLayoutManager extends LinearLayoutManager {
private final float mShrinkAmount = 0.25f;
private final float mShrinkDistance = 2.0f;

public CenterZoomLayoutManager(Context context) {
    super(context);
}

public CenterZoomLayoutManager(Context context, int orientation, boolean reverseLayout) {
    super(context, orientation, reverseLayout);
}

public CenterZoomLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
    super(context, attrs, defStyleAttr, defStyleRes);
}

@Override
public void onLayoutCompleted(RecyclerView.State state) {
    super.onLayoutCompleted(state);
    scaleChild();
}

@Override
public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, RecyclerView.State state) {
    int orientation = getOrientation();
    if (orientation == HORIZONTAL) {
        scaleChild();
        return super.scrollHorizontallyBy(dx, recycler, state);
    } else {
        return 0;
    }
}

private void scaleChild() {
    float midPoint = getWidth() / 2.f;
    float d1 = mShrinkDistance * midPoint;
    for (int i = 0; i < getChildCount(); i++) {
        View child = getChildAt(i);
        float childMidPoint = (getDecoratedRight(child) + getDecoratedLeft(child)) / 2f;
        float d = Math.min(d1, Math.abs(midPoint - childMidPoint));
        float scale = 1.05f - mShrinkAmount * d / d1;
        child.setScaleY(scale);
        child.setScaleX(scale);
    }
}

}

Be sure to call the scaleChild method in onLayoutCompleted . To apply the scale to items when the RecyclerView layout is created. Not just when scrolling. This is an important point

@Override
public void onLayoutCompleted(RecyclerView.State state) {
    super.onLayoutCompleted(state);
    scaleChild();
}

The next important point is to use the scaleChild() method before returning super.scrollHorizontallyBy (dx, recycler, state) . Call. Until scrolling is done. The scale is done each time on the item

@Override
public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, RecyclerView.State state) {
    int orientation = getOrientation();
    if (orientation == HORIZONTAL) {
        scaleChild();
        return super.scrollHorizontallyBy(dx, recycler, state);
    } else {
        return 0;
    }
}

Eventually, this became my program output在此处输入图片说明

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM