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<Float> 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的布局文件如下:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="80dp"
android:layout_height="80dp">
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ImageView
android:id="@+id/item_img"
android:layout_width="35dp"
android:layout_gravity="center_horizontal"
android:layout_height="35dp"
android:padding="2dp"
android:scaleType="centerCrop" />
<TextView
android:id="@+id/item_text"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:textSize="12sp"
android:textColor="#FFF"
android:gravity="center_horizontal"/>
</LinearLayout>
</FrameLayout>