本文主要是介绍recyclerview滑动菜单两头大中间小 + 倒影,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
自定义了layoutManager+RecyclerView,直接上代码
public class MyLayoutManager extends RecyclerView.LayoutManager {private int mSumDx = 0;private int mTotalWidth = 0;private int mItemWidth, mItemHeight;private SparseArray<Rect> mItemRects = new SparseArray<>();/*** 记录Item是否出现过屏幕且还没有回收。true表示出现过屏幕上,并且还没被回收*/private SparseBooleanArray mHasAttachedItems = new SparseBooleanArray();@Overridepublic 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;@Overridepublic 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();}@Overridepublic boolean canScrollHorizontally() {return true;}@Overridepublic 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();//回收越界子Viewfor (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位置* <p>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);}}@Overridepublic 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() {@Overridepublic 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.94private 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;@Overridepublic 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 = shaderpaint.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);}@Overrideprotected void onDraw(Canvas canvas) {if (getDrawable() instanceof BitmapDrawable) {//super.onDraw(canvas); 不走superPaint 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 {//最小到哪个itemif (layoutManger.centerPosition > 0) {rv_menu.smoothScrollToPosition(layoutManger.centerPosition - 1)}}iv_right.setOnClickListener {//最大到哪个itemif (layoutManger.centerPosition < (rv_menu.adapter as MenuAdapter).itemCount - 1) {rv_menu.smoothScrollToPosition(layoutManger.centerPosition + 1)}}
这篇关于recyclerview滑动菜单两头大中间小 + 倒影的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!