最近需要实现一个可以水平滑动且每屏显示是个item 的效果,具体看下图:
滑动的显示效果: 具体子item布局:<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="10dp"
android:gravity="center"
android:orientation="vertical">
<ImageView
android:id="@+id/iv_honor_cup_pic"
android:layout_width="@dimen/base40dp"
android:layout_height="@dimen/base40dp"
android:layout_gravity="center_horizontal"
/>
<TextView
android:id="@+id/tv_honor_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/base6dp"
android:layout_gravity="center_horizontal"
android:textColor="@color/black"
/>
</LinearLayout>
复制代码
由于滑动使用RecycleView 做的,所以这里默认是针对 RecycleView 实现,代码如下:
public class HorizontalLayoutManager extends RecyclerView.LayoutManager {
@Override
public RecyclerView.LayoutParams generateDefaultLayoutParams() {
return null;
}
private int totalWidth = 0;
private int offsetY = 0;
private int offsetX = 0;
public HorizontalLayoutManager(int rows, int columns) {
this.rows = rows;
this.columns = columns;
this.onePageSize = rows * columns;
}
@Override
public boolean canScrollHorizontally() {
return true;
}
@Override
public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, RecyclerView.State state) {
detachAndScrapAttachedViews(recycler);
int newX = offsetX + dx;
int result = dx;
if (newX > totalWidth) {
result = totalWidth - offsetX;
} else if (newX < 0) {
result = 0 - offsetX;
}
offsetX += result;
offsetChildrenHorizontal(-result);
recycleAndFillItems(recycler, state);
return result;
}
private SparseArray<Rect> allItemFrames = new SparseArray<>();
private int getUsableWidth() {
return getWidth() - getPaddingLeft() - getPaddingRight();
}
private int getUsableHeight() {
return getHeight() - getPaddingTop() - getPaddingBottom();
}
private int rows = 0;
private int columns = 0;
private int pageSize = 0;
private int itemWidth = 0;
private int itemHeight = 0;
private int onePageSize = 0;
private int itemWidthUsed;
private int itemHeightUsed;
@Override
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
if (getItemCount() == 0) {
removeAndRecycleAllViews(recycler);
return;
}
if (state.isPreLayout()) {
return;
}
//获取每个Item的平均宽高
itemWidth = getUsableWidth() / columns;
itemHeight = getUsableHeight() / rows;
//计算宽高已经使用的量,主要用于后期测量
itemWidthUsed = (columns - 1) * itemWidth;
itemHeightUsed = (rows - 1) * itemHeight;
//计算总的页数
computePageSize(state);
//计算可以横向滚动的最大值
totalWidth = (pageSize - 1) * getWidth();
//分离view
detachAndScrapAttachedViews(recycler);
int count = getItemCount();
for (int p = 0; p < pageSize; p++) {
for (int r = 0; r < rows; r++) {
for (int c = 0; c < columns; c++) {
int index = p * onePageSize + r * columns + c;
if (index == count) {
//跳出多重循环
c = columns;
r = rows;
p = pageSize;
break;
}
View view = recycler.getViewForPosition(index);
addView(view);
//测量item
measureChildWithMargins(view, itemWidthUsed, itemHeightUsed);
int width = getDecoratedMeasuredWidth(view);
int height = getDecoratedMeasuredHeight(view);
//记录显示范围
Rect rect = allItemFrames.get(index);
if (rect == null) {
rect = new Rect();
}
int x = p * getUsableWidth() + c * itemWidth;
int y = r * itemHeight;
rect.set(x, y, width + x, height + y);
allItemFrames.put(index, rect);
}
}
//每一页循环以后就回收一页的View用于下一页的使用
removeAndRecycleAllViews(recycler);
}
recycleAndFillItems(recycler, state);
}
private void computePageSize(RecyclerView.State state) {
pageSize = state.getItemCount() / onePageSize + (state.getItemCount() % onePageSize == 0 ? 0 : 1);
}
@Override
public void onDetachedFromWindow(RecyclerView view, RecyclerView.Recycler recycler) {
super.onDetachedFromWindow(view, recycler);
offsetX = 0;
offsetY = 0;
}
private void recycleAndFillItems(RecyclerView.Recycler recycler, RecyclerView.State state) {
if (state.isPreLayout()) {
return;
}
Rect displayRect = new Rect(getPaddingLeft() + offsetX, getPaddingTop(), getWidth() - getPaddingLeft() - getPaddingRight() + offsetX, getHeight() - getPaddingTop() - getPaddingBottom());
Rect childRect = new Rect();
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
childRect.left = getDecoratedLeft(child);
childRect.top = getDecoratedTop(child);
childRect.right = getDecoratedRight(child);
childRect.bottom = getDecoratedBottom(child);
if (!Rect.intersects(displayRect, childRect)) {
removeAndRecycleView(child, recycler);
}
}
for (int i = 0; i < getItemCount(); i++) {
if (Rect.intersects(displayRect, allItemFrames.get(i))) {
View view = recycler.getViewForPosition(i);
addView(view);
measureChildWithMargins(view, itemWidthUsed, itemHeightUsed);
Rect rect = allItemFrames.get(i);
layoutDecorated(view, rect.left - offsetX, rect.top, rect.right - offsetX, rect.bottom);
}
}
}
@Override
public int computeHorizontalScrollRange(RecyclerView.State state) {
computePageSize(state);
return pageSize * getWidth();
}
@Override
public int computeHorizontalScrollOffset(RecyclerView.State state) {
return offsetX;
}
@Override
public int computeHorizontalScrollExtent(RecyclerView.State state) {
return getWidth();
}
}
复制代码