自定义了layoutManager+RecyclerView,直接上代码
public class MyLayoutManager extends RecyclerView.LayoutManager {
private int mSumDx = 0;
private int mTotalWidth = 0;
private int mItemWidth, mItemHeight;
private SparseArray mItemRects = new SparseArray<>();
/**
* 记录Item是否出现过屏幕且还没有回收。true表示出现过屏幕上,并且还没被回收
*/
private SparseBooleanArray mHasAttachedItems = new SparseBooleanArray();
@Override
public RecyclerView.LayoutParams generateDefaultLayoutParams() {
return new RecyclerView.LayoutParams(RecyclerView.LayoutParams.WRAP_CONTENT,
RecyclerView.LayoutParams.WRAP_CONTENT);
}
private int mIntervalWidth;
private int mStartX;
/**
* RecyclerView的Item回收器
*/
private RecyclerView.Recycler mRecycle;
/**
* RecyclerView的状态器
*/
private RecyclerView.State mState;
private ValueAnimator mAnimation;
/**
* 滑动的方向:左
*/
private static int SCROLL_LEFT = 1;
/**
* 滑动的方向:右
*/
private static int SCROLL_RIGHT = 2;
@Override
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
if (getItemCount() == 0) {//没有Item,界面空着吧
detachAndScrapAttachedViews(recycler);
return;
}
mRecycle = recycler;
mState = state;
mHasAttachedItems.clear();
mItemRects.clear();
detachAndScrapAttachedViews(recycler);
//将item的位置存储起来
View childView = recycler.getViewForPosition(0);
measureChildWithMargins(childView, 0, 0);
mItemWidth = getDecoratedMeasuredWidth(childView);
mItemHeight = getDecoratedMeasuredHeight(childView);
mIntervalWidth = getIntervalWidth();
mStartX = getWidth() / 2 - mItemWidth/2 ;
//定义水平方向的偏移量
int offsetX = 0;
for (int i = 0; i < getItemCount(); i++) {
Rect rect = new Rect(mStartX + offsetX, mItemWidth / 2, mStartX + offsetX + mItemWidth, mItemHeight);
mItemRects.put(i, rect);
mHasAttachedItems.put(i, false);
offsetX += mIntervalWidth;
}
layoutItems(recycler, SCROLL_RIGHT);
//如果所有子View的宽度和没有填满RecyclerView的宽度,
// 则将宽度设置为RecyclerView的宽度
mTotalWidth = Math.max(offsetX, getHorizontalSpace());
}
private int getHorizontalSpace() {
return getWidth() - getPaddingLeft() - getPaddingRight();
}
@Override
public boolean canScrollHorizontally() {
return true;
}
@Override
public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, RecyclerView.State state) {
if (getChildCount() <= 0) {
return dx;
}
int travel = dx;
//如果滑动到最顶部
if (mSumDx + dx < 0) {
travel = -mSumDx;
} else if (mSumDx + dx > getMaxOffset()) {
//如果滑动到最底部
travel = getMaxOffset() - mSumDx;
}
mSumDx += travel;
layoutItems(recycler, SCROLL_RIGHT);
return travel;
}
/**********************/
private void layoutItems(RecyclerView.Recycler recycler,int scrollDirection){
Rect visibleRect = getVisibleArea();
//回收越界子View
for (int i=0 ; i < getChildCount(); i++) {
View child = getChildAt(i);
int position = getPosition(child);
Rect rect = mItemRects.get(position);
if (!Rect.intersects(rect, visibleRect)) {
removeAndRecycleView(child, recycler);
mHasAttachedItems.put(position, false);
} else {
layoutDecoratedWithMargins(child, rect.left - mSumDx, rect.top, rect.right - mSumDx, rect.bottom);
handleChildView(child, rect.left - mStartX - mSumDx);
mHasAttachedItems.put(position, true);
}
}
int min = 0;
int max = getItemCount();
for (int i = min; i < max; i++) {
Rect rect = mItemRects.get(i);
if (Rect.intersects(visibleRect, rect) && !mHasAttachedItems.get(i)) {
View child = recycler.getViewForPosition(i);
if (scrollDirection == SCROLL_RIGHT) {
addView(child, 0);
} else {
addView(child);
}
measureChildWithMargins(child, 0, 0);
layoutDecoratedWithMargins(child, rect.left - mSumDx, rect.top, rect.right - mSumDx, rect.bottom);
handleChildView(child, rect.left - mStartX - mSumDx );
mHasAttachedItems.put(i, true);
}
}
}
/**
* 获取可见的区域Rect
*
* @return
*/
private Rect getVisibleArea() {
Rect result = new Rect(getPaddingLeft() + mSumDx, getPaddingTop(), getWidth() - getPaddingRight() + mSumDx, getHeight() - getPaddingBottom());
return result;
}
public int getIntervalWidth() {
return mItemWidth + mItemWidth / 5;
}
public int getCenterPosition() {
int pos = (int) (mSumDx / getIntervalWidth());
int more = (int) (mSumDx % getIntervalWidth());
if (more > getIntervalWidth() * 0.5f) pos++;
return pos;
}
/**
* 获取第一个可见的Item位置
* Note:该Item为绘制在可见区域的第一个Item,有可能被第二个Item遮挡
*/
public int getFirstVisiblePosition() {
if (getChildCount() <= 0) {
return 0;
}
View view = getChildAt(0);
int pos = getPosition(view);
return pos;
}
private View child;
private int moveX;
private void handleChildView(View child, int moveX) {
this.child = child;
this.moveX = moveX;
float radio = computeScale(moveX);
float rotation = computeRotationY(moveX);
child.setPivotY(child.getHeight() / 4);
child.setScaleX(radio);
child.setScaleY(radio);
if (isEnd && getPosition(child) == getCenterPosition()) {
child.setRotationY(0);
} else {
child.setRotationY(rotation);
}
}
@Override
public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position) {
int finalOffset = calculateOffsetForPosition(position);
if (mRecycle != null || mState != null) {//如果RecyclerView还没初始化完,先记录下要滚动的位置
startScroll(mSumDx, finalOffset);
}
}
/**
* 滚动到指定X轴位置
*
* @param from X轴方向起始点的偏移量
* @param to X轴方向终点的偏移量
*/
private void startScroll(int from, int to) {
if (mAnimation != null && mAnimation.isRunning()) {
mAnimation.cancel();
}
final int direction = from < to ? SCROLL_RIGHT : SCROLL_LEFT;
mAnimation = ValueAnimator.ofFloat(from, to);
mAnimation.setDuration(200);
mAnimation.setInterpolator(new DecelerateInterpolator());
mAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mSumDx = Math.round((float) animation.getAnimatedValue());
layoutItems(mRecycle,direction);
}
});
mAnimation.start();
}
/**
* 计算Item所在的位置偏移
*
* @param position 要计算Item位置
*/
public int calculateOffsetForPosition(int position) {
return Math.round(getIntervalWidth() * position);
}
/**
* 计算Item缩放系数
*
* @param x Item的偏移量
* @return 缩放系数
*/
private float s = 0.8f;
private float n = 0.1f;
private float s2 = s + n;//0.94
private float computeScale(int x) {
// float scale = 1 - Math.abs(x * 2.0f / (8f * getIntervalWidth()));
BigDecimal b = new BigDecimal(Math.abs(x * 2.0f / (11f * getIntervalWidth())) + s);
float scale = b.setScale(2, BigDecimal.ROUND_HALF_DOWN).floatValue();
if (scale < s2) scale = s2;
// if (scale > 2) scale = 2;
return scale;
}
/**
* 获取最大偏移量
*/
private int getMaxOffset() {
return (getItemCount() - 1) * getIntervalWidth();
}
private boolean isEnd = false;
@Override
public void onScrollStateChanged(int state) {
super.onScrollStateChanged(state);
switch (state) {
case RecyclerView.SCROLL_STATE_IDLE:
//滚动停止时
isEnd = true;
handleChildView(child, moveX);
break;
case RecyclerView.SCROLL_STATE_DRAGGING:
//拖拽滚动时
break;
case RecyclerView.SCROLL_STATE_SETTLING:
//动画滚动时
break;
}
}
/**
* 最大Y轴旋转度数
*/
private float M_MAX_ROTATION_Y = 15.0f;
private float computeRotationY(int x) {
float rotationY;
rotationY = -M_MAX_ROTATION_Y * x / getIntervalWidth();
if (Math.abs(rotationY) > M_MAX_ROTATION_Y) {
if (rotationY > 0) {
rotationY = M_MAX_ROTATION_Y;
} else {
rotationY = -M_MAX_ROTATION_Y;
}
}
return rotationY;
}
}
滑动带惯性并自动选择居中
LinearSnapHelper().attachToRecyclerView(recyclerview)
倒影:在adapter中加入以下方法设置bitmap
fun compoundBitmap(context: Context, img: Int): Bitmap? {
val originalBitmap = BitmapFactory.decodeResource(context.resources, img) //把资源图片变成一个Bitmap对象
//生成下面的一半图片
val matrix = Matrix()
matrix.setScale(1f, -1f)//翻转
val invertBitmap = Bitmap.createBitmap(originalBitmap, 0, 0, originalBitmap.width, originalBitmap.height, matrix, false)
//创建一个空的位图
val compoundBitmap = Bitmap.createBitmap(
originalBitmap.width,
// originalBitmap.height + invertBitmap.height + 10,
originalBitmap.height + invertBitmap.height,
Bitmap.Config.ARGB_8888
)
val canvas = Canvas(compoundBitmap)
canvas.drawBitmap(originalBitmap, 0f, 0f, null)
// canvas.drawBitmap(invertBitmap, 0f, (originalBitmap.height + 10).toFloat(), null)
canvas.drawBitmap(invertBitmap, 0f, (originalBitmap.height-2).toFloat(), null)//上下俩个图片的间距
val paint = Paint()
// 设置渐变颜色
val shader = LinearGradient(
0f,
// (originalBitmap.height + 10).toFloat(),
(originalBitmap.height).toFloat(),
0f,
compoundBitmap.height.toFloat(),
0x70ffffff,
0x00ffffff,
Shader.TileMode.CLAMP
)
paint.shader = shader
paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.DST_IN)
canvas.drawRect(
0f,
// (originalBitmap.height + 5).toFloat(),
(originalBitmap.height).toFloat(),
originalBitmap.width.toFloat(),
compoundBitmap.height.toFloat(),
paint
)
return compoundBitmap
}
这个时候基本可以了,但是在图片旋转的时候,会出现锯齿,继承ImageView
public class RotationImageView extends androidx.appcompat.widget.AppCompatImageView {
public RotationImageView(Context context) {
super(context);
}
public RotationImageView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public RotationImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onDraw(Canvas canvas) {
if (getDrawable() instanceof BitmapDrawable) {
//super.onDraw(canvas); 不走super
Paint paint = new Paint();
paint.setAntiAlias(true);//设置抗锯齿
paint.setDither(true);
Bitmap bitmap = ((BitmapDrawable) getDrawable()).getBitmap();
BitmapShader shader = new BitmapShader(bitmap,Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
paint.setShader(shader);
canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), paint);
}
}
}
要想点击左右箭头选择
iv_left.setOnClickListener {
//最小到哪个item
if (layoutManger.centerPosition > 0) {
rv_menu.smoothScrollToPosition(layoutManger.centerPosition - 1)
}
}
iv_right.setOnClickListener {
//最大到哪个item
if (layoutManger.centerPosition < (rv_menu.adapter as MenuAdapter).itemCount - 1) {
rv_menu.smoothScrollToPosition(layoutManger.centerPosition + 1)
}
}