我有一个GridView
,其中包含5x50内的项目。
我需要向所有方向滚动它们,而不是在从顶部/左侧开始到达终点时停止。
例如从左到右滚动
滚动前的
1 2 3 4 5
6 7 8 9 10
滚动到右边后
5 1 2 3 4
10 6 7 8 9
从上到下(或从下到上)
滚动到底部之前
1 2 3 4 5
6 7 8 9 10
11 12 13 14 15
滚动后
6 7 8 9 10
11 12 13 14 15
1 2 3 4 5
我尝试将其平滑滚动为GridView
原生滚动。
答案 0 :(得分:14)
在 activity_main.xml 中指定了以下视图层次结构:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
基本列表项 - TextView
,item.xml
:
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"/>
然后在活动中:
public class MainActivity extends AppCompatActivity {
private static final int spanCount = 5;
private static final int totalItemCount = 15;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
RecyclerView recyclerView = findViewById(R.id.recycler);
// Shamelessly stolen from devunwired bit.ly/2yCqVIp
recyclerView.addItemDecoration(new GridDividerDecoration(this));
recyclerView.setLayoutManager(new GridLayoutManager(this, spanCount, LinearLayoutManager.VERTICAL, false));
recyclerView.setAdapter(new MyAdapter(totalItemCount));
}
static class MyAdapter extends RecyclerView.Adapter<MyViewHolder> {
private final int totalSizeOfItems;
private boolean hasBeenSetup = false;
MyAdapter(int totalSizeOfItems) {
this.totalSizeOfItems = totalSizeOfItems;
}
@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false);
final int cellSize = parent.getMeasuredWidth() / spanCount;
view.setMinimumWidth(cellSize);
view.setMinimumHeight(cellSize);
setupRecyclerHeightIfNeeded(parent, cellSize);
return new MyViewHolder(view);
}
// We need to perform this operation once, not each time `onCreateViewHolder` is called
private void setupRecyclerHeightIfNeeded(View parent, int cellSize) {
if (hasBeenSetup) return;
hasBeenSetup = true;
ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) parent.getLayoutParams();
int numOfRows = (int) (totalItemCount / (double) spanCount);
params.height = numOfRows * cellSize;
new Handler().post(parent::requestLayout);
}
@Override
public void onBindViewHolder(MyViewHolder holder, int pos) {
int position = holder.getAdapterPosition() % totalSizeOfItems;
holder.textView.setText(Integer.toString(position + 1));
}
@Override
public int getItemCount() {
// this will result the list to be "infinite"
return Integer.MAX_VALUE;
}
}
static class MyViewHolder extends RecyclerView.ViewHolder {
TextView textView;
MyViewHolder(View itemView) {
super(itemView);
textView = (TextView) itemView;
}
}
}
在输出时,您将获得:
处理水平滚动需要进行少量更改:
GridLayoutManager
的方向应更改为HORIZONTAL
除此之外 - 一切都应该是相似的。
答案 1 :(得分:5)
此解决方案基于 @azizbekian 源代码(感谢有趣的示例)。找到与你问的内容很接近的东西真的很有趣( 同时 )。所以,这是我修改过的代码:
public class MainActivity extends AppCompatActivity {
private static final int spanCount = 5;
private static final int totalItemCount = 15;
private GridLayoutManager gridLayoutManager;
private RecyclerView recyclerView;
private int orientation = 1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
recyclerView = (RecyclerView) findViewById(R.id.recycler);
recyclerView.setAdapter(new MyAdapter(totalItemCount));
// Shamelessly stolen from devunwired bit.ly/2yCqVIp
recyclerView.addItemDecoration(new GridDividerDecoration(this));
gridLayoutManager = new GridLayoutManager(this, spanCount);
recyclerView.setLayoutManager(gridLayoutManager);
RecyclerView.OnScrollListener listener = new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recycler, int dx, int dy) {
super.onScrolled(recycler, dx, dy);
if (dx > 0) {
orientation = LinearLayoutManager.HORIZONTAL;
} else if (dx < 0) {
orientation = LinearLayoutManager.VERTICAL;
} else if (dy > 0) {
orientation = LinearLayoutManager.VERTICAL;
} else if (dy < 0) {
orientation = LinearLayoutManager.HORIZONTAL;
}
recycler.post(new Runnable() {
@Override
public void run() {
gridLayoutManager.setOrientation(orientation);
recyclerView.setLayoutManager(gridLayoutManager);
recyclerView.getAdapter().notifyDataSetChanged();
}
});
}
};
recyclerView.addOnScrollListener(listener);
}
static class MyAdapter extends RecyclerView.Adapter<MyViewHolder> {
private final int totalSizeOfItems;
private boolean hasBeenSetup = false;
MyAdapter(int totalSizeOfItems) {
this.totalSizeOfItems = totalSizeOfItems;
}
@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false);
final int cellSize = parent.getMeasuredWidth() / spanCount;
view.setMinimumWidth(cellSize);
view.setMinimumHeight(cellSize);
setupRecyclerHeightIfNeeded(parent, cellSize);
return new MyViewHolder(view);
}
// We need to perform this operation once, not each time `onCreateViewHolder` is called
private void setupRecyclerHeightIfNeeded(final View parent, int cellSize) {
if (hasBeenSetup) return;
hasBeenSetup = true;
ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) parent.getLayoutParams();
int numOfRows = (int) (totalItemCount / (double) spanCount);
params.height = numOfRows * cellSize + 100; // modified based on my phone height
new Handler().post(new Runnable() {
@Override
public void run() {
parent.requestLayout();
}
});
}
@Override
public void onBindViewHolder(MyViewHolder holder, int pos) {
int position = holder.getAdapterPosition() % totalSizeOfItems;
holder.textView.setText(Integer.toString(position + 1));
}
@Override
public int getItemCount() {
// this will result the list to be "infinite"
return Integer.MAX_VALUE;
}
}
static class MyViewHolder extends RecyclerView.ViewHolder {
TextView textView;
MyViewHolder(View itemView) {
super(itemView);
textView = (TextView) itemView;
}
}
}
布局向上滚动以显示垂直方向,向左滚动以显示水平方向。输出如下(抱歉丑陋的动画):
希望它会对你有所帮助。
答案 2 :(得分:3)
Here是另一种解决方案,但采用画布方法。
<强> activity_main.xml中强>
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/constraint_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<nice.fontaine.infinitescroll.CanvasView
android:id="@+id/canvas_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</android.support.constraint.ConstraintLayout>
<强> MainActivity.java 强>
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
CanvasView canvas = findViewById(R.id.canvas_view);
String[][] labels = new String[][] {
{"5", "8", "2"},
{"4", "7", "1"},
{"3", "6", "9"}
};
int columns = 3;
int rows = 3;
canvas.with(labels, columns, rows);
}
}
<强> CanvasView.java 强>
public class CanvasView extends View {
private final Panning panning;
private final GridManager gridManager;
private Rect bounds;
private Point current = new Point(0, 0);
private List<Overlay> overlays;
public CanvasView(Context context, AttributeSet attrs) {
super(context, attrs);
bounds = new Rect();
panning = new Panning();
overlays = new ArrayList<>();
gridManager = new GridManager(this);
init();
}
public void with(String[][] labels, int columns, int rows) {
gridManager.with(labels, columns, rows);
}
private void init() {
ViewTreeObserver observer = getViewTreeObserver();
observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
int width = getWidth();
int height = getHeight();
bounds.set(0, 0, width, height);
gridManager.generate(bounds);
getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
});
}
@Override
protected void onSizeChanged(int width, int height, int oldWidth, int oldHeight) {
super.onSizeChanged(width, height, oldWidth, oldHeight);
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
new Canvas(bitmap);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
bounds.offsetTo(-current.x, -current.y);
gridManager.generate(bounds);
canvas.translate(current.x, current.y);
for (Overlay overlay : overlays) {
if (overlay.intersects(bounds)) {
overlay.onDraw(canvas);
}
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
current = panning.handle(event);
invalidate();
return true;
}
public void addChild(Overlay overlay) {
this.overlays.add(overlay);
}
}
<强> GridManager.java 强>
class GridManager {
private final CanvasView canvas;
private int columns;
private int rows;
private String[][] labels;
private final Map<String, Overlay> cache;
GridManager(CanvasView canvas) {
this.canvas = canvas;
cache = new HashMap<>();
}
void with(String[][] labels, int columns, int rows) {
this.columns = columns;
this.rows = rows;
this.labels = labels;
}
void generate(Rect bounds) {
if (columns == 0 || rows == 0 || labels == null) return;
int width = bounds.width();
int height = bounds.height();
int overlayWidth = width / columns;
int overlayHeight = height / rows;
int minX = mod(floor(bounds.left, overlayWidth), columns);
int minY = mod(floor(bounds.top, overlayHeight), rows);
int startX = floorToMod(bounds.left, overlayWidth);
int startY = floorToMod(bounds.top, overlayHeight);
for (int j = 0; j <= rows; j++) {
for (int i = 0; i <= columns; i++) {
String label = getLabel(minX, minY, i, j);
int x = startX + i * overlayWidth;
int y = startY + j * overlayHeight;
String key = x + "_" + y;
if (!cache.containsKey(key)) {
Overlay overlay = new Overlay(label, x, y, overlayWidth, overlayHeight);
cache.put(key, overlay);
canvas.addChild(overlay);
}
}
}
}
private String getLabel(int minX, int minY, int i, int j) {
int m = mod(minX + i, columns);
int n = mod(minY + j, rows);
return labels[n][m];
}
private int floor(double numerator, double denominator) {
return (int) Math.floor(numerator / denominator);
}
private int floorToMod(int value, int modulo) {
return value - mod(value, modulo);
}
private int mod(int value, int modulo) {
return (value % modulo + modulo) % modulo;
}
}
<强> Panning.java 强>
class Panning {
private Point start;
private Point delta = new Point(0, 0);
private Point cursor = new Point(0, 0);
private boolean isFirst;
Point handle(MotionEvent event) {
final Point point = new Point((int) event.getX(), (int) event.getY());
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
press();
break;
case MotionEvent.ACTION_MOVE:
drag(point);
break;
}
return new Point(cursor.x + delta.x, cursor.y + delta.y);
}
private void press() {
isFirst = true;
}
private void drag(final Point point) {
if (isFirst) {
start = point;
cursor.offset(delta.x, delta.y);
isFirst = false;
}
delta.x = point.x - start.x;
delta.y = point.y - start.y;
}
}
<强> Overlay.java 强>
class Overlay {
private final String text;
private final int x;
private final int y;
private final Paint paint;
private final Rect bounds;
private final Rect rect;
private final Rect textRect;
Overlay(String text, int x, int y, int width, int height) {
this.text = text;
this.bounds = new Rect(x, y, x + width, y + height);
this.rect = new Rect();
this.textRect = new Rect();
paint = new Paint();
paint.setColor(Color.BLACK);
setTextSize(text);
this.x = x + width / 2 - textRect.width() / 2;
this.y = y + height / 2 + textRect.height() / 2;
}
boolean intersects(Rect r) {
rect.set(bounds.left, bounds.top, bounds.right, bounds.bottom);
return rect.intersect(r.left, r.top, r.right, r.bottom);
}
void onDraw(Canvas canvas) {
// rectangle
paint.setStrokeWidth(5);
paint.setStyle(Paint.Style.STROKE);
canvas.drawRect(bounds, paint);
// centered text
paint.setStrokeWidth(2);
paint.setStyle(Paint.Style.FILL);
canvas.drawText(text, x, y, paint);
}
private void setTextSize(String text) {
final float testTextSize = 100f;
paint.setTextSize(testTextSize);
paint.getTextBounds(text, 0, text.length(), textRect);
}
}