簡體   English   中英

如何在android中滾動時放大Recycler View中心項目?

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

我需要在通過放大滾動時始終突出顯示回收器視圖中的中心項目。

您應該遵循此代碼,這有助於我在回收站視圖中放大中心項目。

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

    }
}

中心水平視圖在此處輸入圖片說明

我精簡了該解決方案並在onLayoutComplete期間添加了初始調整大小。 我不需要垂直滾動,所以我去掉了那部分。

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

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

作為Mayank Garg 的回答及其評論的附錄,它說它不適用於第一個或最后一個項目,當您使用此類並在開始和結束時向列表添加額外的填充時會發生這種情況,以便第一個出現的項目已經居中。 在這些情況下,像getDecoratedRight()getDecoratedLeft()這樣的函數將在它們返回的大小中包含額外的填充。 這會擾亂視圖中點的計算,因此它不適用於第一個和最后一個項目。

對此的解決方案是檢測布局管理器是否顯示列表的開頭,並使用條件來使用不同的計算,該計算使用裝飾錨之一作為原點,然后使用視圖的一半寬度來查找中點。

換句話說,在 Mayank 的代碼中,你有:

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

您可以將其替換為類似於以下內容的內容:

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

換句話說,這會檢查第一個子視圖是否是適配器中的第一個項目,如果是,則使用左側或右側裝飾的水平位置通過減去或添加項目的一半寬度來計算中點。

另一個更簡單的選擇是:

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

但話又說回來,您需要測試這是否符合您對布局/視圖可能具有的其他約束,因為 Mayank 使用getDecoratedLeft()而不是getX()可能是有原因的。

好吧,按照 Anarchofascist 和 Mayank,只需在 Mayank 代碼的 biggining 處添加此覆蓋,即可使效果與第一個元素一起使用。

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

    }
}

這個方法對我有用,我解決的問題是RecyclerView創建的時候,秤上的項目沒有應用到他們身上,我也說一下我用的數字是根據我的需要,你是根據Need自己調整比例

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

}

請務必在onLayoutCompleted調用 scaleChild 方法。 在創建 RecyclerView 布局時將比例應用於項目。 不僅僅是在滾動時。 這是一個重要的點

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

下一個重點是在returning super.scrollHorizontallyBy (dx, recycler, 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;
    }
}

最終,這成為了我的程序輸出在此處輸入圖片說明

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM