public class CircleLayoutManager extends RecyclerView.LayoutManager {
/**
* 默认每个item之间的角度
**/
private static float INTERVAL_ANGLE = 22.5f;
/**
* 滑动距离和角度的一个比例
**/
private static float DISTANCE_RATIO = 20f;
/**
* 默认的半径长度
**/
private static final int DEFAULT_RADIO = 100;
/**
* 滑动的方向
*/
private static int SCROLL_LEFT = 1;
private static int SCROLL_RIGHT = 2;
/**
* 半径默认为100
**/
private int mRadius;
/**
* 当前旋转的角度
**/
private float offsetRotate;
private int startLeft;
private int startTop;
/**
* 第一个的角度是为0
**/
private int firstChildRotate = 0;
//每个item之间的角度间隔
private float intervalAngle;
//最大和最小的移除角度
private int minRemoveDegree;
private int maxRemoveDegree;
//记录Item是否出现过屏幕且还没有回收。true表示出现过屏幕上,并且还没被回收
private SparseBooleanArray itemAttached = new SparseBooleanArray();
//保存所有的Item的上下左右的偏移量信息
private SparseArray itemsRotate = new SparseArray<>();
// 这里的每个item的大小都是一样的
private int mDecoratedChildWidth;
private int mDecoratedChildHeight;
public CircleLayoutManager() {
this(DEFAULT_RADIO, 5);
}
public CircleLayoutManager(int mRadius, int showCount) {
this.mRadius = mRadius;
offsetRotate = 0;
INTERVAL_ANGLE = 90.0f / (showCount - 1);
intervalAngle = INTERVAL_ANGLE;
minRemoveDegree = 180;
maxRemoveDegree = 270;
firstChildRotate = minRemoveDegree;
}
@Override
public RecyclerView.LayoutParams generateDefaultLayoutParams() {
return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
}
@Override
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
//如果没有item,直接返回
//跳过preLayout,preLayout主要用于支持动画
if (state.getItemCount() <= 0 || state.isPreLayout()) {
offsetRotate = 0;
if (state.getItemCount() == 0) {
removeAndRecycleAllViews(recycler);
}
return;
}
//得到子view的宽和高,这边的item的宽高都是一样的,所以只需要进行一次测量
View scrap = recycler.getViewForPosition(0);
addView(scrap);
measureChildWithMargins(scrap, 0, 0);
//计算测量布局的宽高
mDecoratedChildWidth = getDecoratedMeasuredWidth(scrap);
mDecoratedChildHeight = getDecoratedMeasuredHeight(scrap);
//确定起始位置,在右下角
startLeft = getHorizontalSpace() - mDecoratedChildWidth;
startTop = getVerticalSpace() - mDecoratedChildHeight;
//记录每个item旋转的角度
float rotate = firstChildRotate;
for (int i = 0; i < getItemCount(); i++) {
itemsRotate.put(i, rotate);
itemAttached.put(i, false);
rotate += intervalAngle;
}
//在布局之前,将所有的子View先Detach掉,放入到Scrap缓存中
detachAndScrapAttachedViews(recycler);
fixRotateOffset();
layoutItems(recycler, state);
}
/**
* 进行view的回收和显示
**/
private void layoutItems(RecyclerView.Recycler recycler, RecyclerView.State state) {
layoutItems(recycler, state, SCROLL_RIGHT);
}
/**
* 进行view的回收和显示的具体实现
**/
private void layoutItems(RecyclerView.Recycler recycler, RecyclerView.State state, int oritention) {
if (state.isPreLayout()) return;
//移除界面之外的view
for (int i = 0; i < getChildCount(); i++) {
View view = getChildAt(i);
int position = getPosition(view);
if (itemsRotate.get(position) - offsetRotate > maxRemoveDegree || itemsRotate.get(position) - offsetRotate < minRemoveDegree) {
itemAttached.put(position, false);
removeAndRecycleView(view, recycler);
}
}
//将要显示的view进行显示出来
int count = getItemCount();
for (int i = 0; i < count; i++) {
if (itemsRotate.get(i) - offsetRotate <= maxRemoveDegree + INTERVAL_ANGLE && itemsRotate.get(i) - offsetRotate >= minRemoveDegree - INTERVAL_ANGLE) {
if (!itemAttached.get(i)) {
ViewGroup scrap = (ViewGroup) recycler.getViewForPosition(i);
View childView = scrap.getChildAt(0);
measureChildWithMargins(scrap, 0, 0);
if (oritention == SCROLL_LEFT) {
addView(scrap, 0);
} else {
addView(scrap);
}
float rotate = itemsRotate.get(i);
if (count > 90 / INTERVAL_ANGLE + 1) {
rotate -= offsetRotate;
}
int left = calLeftPosition(rotate);
int top = calTopPosition(rotate);
scrap.setRotation(rotate);
layoutDecorated(scrap, startLeft + left, startTop + top, startLeft + left + mDecoratedChildWidth, startTop + top + mDecoratedChildHeight);
childView.setRotation(-rotate);
itemAttached.put(i, true);
}
}
}
}
@Override
public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, RecyclerView.State state) {
if (90.0f / INTERVAL_ANGLE + 1 >= getItemCount()) {
return 0;
}
int willScroll = -dx;
//每个item x方向上的移动距离
float theta = -dx / DISTANCE_RATIO;
float targetRotate = offsetRotate + theta;
//目标角度
if (targetRotate < 0) {
willScroll = (int) (-offsetRotate * DISTANCE_RATIO);
} else if (targetRotate > getMaxOffsetDegree()) {
willScroll = (int) ((getMaxOffsetDegree() - offsetRotate) * DISTANCE_RATIO);
}
theta = willScroll / DISTANCE_RATIO;
//当前移动的总角度
offsetRotate += theta;
//重新设置每个item的x和y的坐标
for (int i = 0; i < getChildCount(); i++) {
ViewGroup view = (ViewGroup) getChildAt(i);
View childView = view.getChildAt(0);
float newRotate = view.getRotation() - theta;
int offsetX = calLeftPosition(newRotate);
int offsetY = calTopPosition(newRotate);
view.setRotation(newRotate);
layoutDecorated(view, startLeft + offsetX, startTop + offsetY, startLeft + offsetX + mDecoratedChildWidth, startTop + offsetY + mDecoratedChildHeight);
childView.setRotation(-newRotate);
}
//根据dx的大小判断是左滑还是右滑
if (dx > 0) {
layoutItems(recycler, state, SCROLL_LEFT);
} else {
layoutItems(recycler, state, SCROLL_RIGHT);
}
return willScroll;
}
@Override
public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
return scrollHorizontallyBy(-dy, recycler, state);
}
@Override
public boolean canScrollHorizontally() {
return true;
}
@Override
public boolean canScrollVertically() {
return true;
}
/**
* 当前item的x的坐标
**/
private int calLeftPosition(float rotate) {
return (int) (mRadius * Math.cos(Math.toRadians(90 - rotate)));
}
/**
* 当前item的y的坐标
**/
private int calTopPosition(float rotate) {
return (int) (mRadius * Math.sin(Math.toRadians(90 - rotate)));
}
/**
* 设置滚动时候的角度
**/
private void fixRotateOffset() {
if (offsetRotate < 0) {
offsetRotate = 0;
}
if (offsetRotate > getMaxOffsetDegree()) {
offsetRotate = getMaxOffsetDegree();
}
}
/**
* 最大的角度
**/
private float getMaxOffsetDegree() {
return (getItemCount() - 1) * intervalAngle - 90;
}
private int getHorizontalSpace() {
return getWidth() - getPaddingRight() - getPaddingLeft();
}
private int getVerticalSpace() {
return getHeight() - getPaddingBottom() - getPaddingTop();
}
private PointF computeScrollVectorForPosition(int targetPosition) {
if (getChildCount() == 0) {
return null;
}
final int firstChildPos = getPosition(getChildAt(0));
final int direction = targetPosition < firstChildPos ? -1 : 1;
return new PointF(direction, 0);
}
@Override
public void scrollToPosition(int position) {//移动到某一项
if (position < 0 || position > getItemCount() - 1) return;
float targetRotate = position * intervalAngle;
if (targetRotate == offsetRotate) return;
offsetRotate = targetRotate;
fixRotateOffset();
requestLayout();
}
@Override
public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position) {//平滑的移动到某一项
LinearSmoothScroller smoothScroller = new LinearSmoothScroller(recyclerView.getContext()) {
@Override
public PointF computeScrollVectorForPosition(int targetPosition) {
return CircleLayoutManager.this.computeScrollVectorForPosition(targetPosition);
}
};
smoothScroller.setTargetPosition(position);
startSmoothScroll(smoothScroller);
}
@Override
public void onAdapterChanged(RecyclerView.Adapter oldAdapter, RecyclerView.Adapter newAdapter) {//adapter进行改变的时候
removeAllViews();
offsetRotate = 0;
}
}
RecyclerView的布局文件如下: