【问题标题】:How to scale up Recycler View center item while scrolling in android?如何在android中滚动时扩大Recycler View中心项目?
【发布时间】:2015-09-28 13:03:50
【问题描述】:

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

【问题讨论】:

  • 用更具体的问题更新您的问题,并添加一些您目前所拥有的代码。您会因这个问题而被否决,并失去再次提问的能力。
  • 类似这篇文章stackoverflow.com/questions/29487382/… ...我需要在水平回收器视图中突出显示中心项目。有什么方法请指导我吗?

标签: android android-recyclerview


【解决方案1】:

您应该遵循此代码,这有助于我在回收站视图中放大中心项目。

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

    }
}

居中水平视图

【讨论】:

  • 感谢完美的解决方案。但是如何在 RecyclerView init 上对第一个元素应用调整大小?
  • @fHate 你是怎么把它应用到第一个项目上的
  • 这也不适用于最后一项。
  • 感谢这个惊人的解决方案,问题是在创建回收站视图后中间项目不会变大,所以当你滚动它一点时,所有项目都会立即改变它们的大小,这很糟糕为用户查看,是否可以使用动画缩小项目?还是在创建回收站视图和适配器后立即更改大小?
  • 您好,第一次如何初始化中心缩放?手动将适配器滚动到 x 位置并中心缩放元素
【解决方案2】:

我精简了该解决方案并在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

【讨论】:

  • @fHate 我想这就是你要找的东西?
  • 很好的例子,但不幸的是第一次项目没有正确居中
  • 这很好用!为了使它也适用于第一个和最后一个项目,我们只需要添加一个项目装饰以在开头和之后添加额外的空间。像这样:gist.github.com/dadouf/38f5816447c78d020d894c5c7c0c0fae
  • 如果我只想缩放我的视图的一个孩子怎么办。例如。项目的文本视图,我该怎么做?目前卡在这个问题上。
【解决方案3】:

作为Mayank Garg's answer 的附录以及它所说的 它不适用于第一个或最后一个项目,当您使用这个类并且还添加额外的时会发生这种情况在列表的开头和结尾填充,以便第一项显示已经居中。在这些情况下,像 getDecoratedRight()getDecoratedLeft() 这样的函数将在它们返回的大小中包含额外的填充。这会打乱视图中点的计算,因此它对第一个和最后一个项目不起作用

对此的解决方案是检测布局管理器是否显示列表的开头,并使用条件来使用不同的计算,该计算使用一个装饰的 anchors 作为原点,但然后使用视图的一半宽度来找到中点。

换句话说,在 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;
}

换句话说,这会检查第一个子视图是否是适配器中的第一个项目,如果是,则使用左侧或右侧 decorated 水平位置来计算中点减去或加上项目的一半宽度。

另一个更简单的选择是:

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

但话又说回来,您需要测试这是否适合您的布局/视图中可能存在的其他约束,因为 Mayank 使用 getDecoratedLeft() 而不是 getX() 可能是有原因的。

【讨论】:

    【解决方案4】:

    好吧,跟随 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);
            }
    
        }
    }
    

    【讨论】:

    • @fHate 这就是你要找的东西?
    【解决方案5】:

    这个方法对我有用,我解决的问题是在创建RecyclerView的时候,秤上的item并没有应用到它们上,顺便说一下,我使用的数字是根据我的需要,而你根据需要自己调整比例

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

    最终,这成为了我的程序输出

    【讨论】:

      猜你喜欢
      • 2023-03-27
      • 2018-02-11
      • 2020-10-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多