我需要在滚动时通过放大来始终突出显示RecyclerView中心项。
你应该遵循这段代码,它帮助我缩放了回收视图中的居中项。
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
}
}
}
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();
}
return 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;
}
}
getDecoratedRight()
和 getDecoratedLeft()
这样的函数将包括它们返回的大小中的额外填充。这会破坏视图中点的计算,因此它不适用于第一个和最后一个项目。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的代码开头添加这个覆盖来使效果与第一个元素一起工作。
@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);
}
}
}