具有无限滚动优化的 GridView
GridView with infinite scroll optimization
我创建了一个简单的应用程序,它从 Pixabay 获取图像,然后在 GridView
中无限滚动显示它们。
我的OnScrollListener
:
public class BasicOnScrollListener implements AbsListView.OnScrollListener {
private IOnScroll onScroll;
public BasicOnScrollListener(IOnScroll action) {
this.onScroll = action;
}
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
if (firstVisibleItem + visibleItemCount >= totalItemCount - visibleItemCount)
onScroll.onReachedEnd();
}
}
负责数据处理的代码:
private List<Image> images = new ArrayList<>();
....
private void init() {
this.imageAdapter = new ImageAdapter(this, images);
this.gridView.setAdapter(imageAdapter);
populateGridView();
this.gridView.setOnScrollListener(new BasicOnScrollListener(() -> {
populateGridView();
}));
}
private void populateGridView() {
if (!isLoading) {
isLoading = true;
this.progressBar.setVisibility(View.VISIBLE);
imagesRepository.getImages(currentPage, PAGE_SIZE, new IOnRepositoryDataReturn<ImagesList>() {
@Override
public void onData(ImagesList data) {
clearIfNeeded();
images.addAll(data.images);
imageAdapter.notifyDataSetChanged();
onFinish();
}
@Override
public void onError(String error) {
Toast.makeText(getApplicationContext(), error, Toast.LENGTH_LONG).show();
}
private void clearIfNeeded() {
if (images.size() > 1000) {
images.subList(0, 300).clear();
}
}
private void onFinish() {
progressBar.setVisibility(View.INVISIBLE);
isLoading = false;
currentPage = currentPage + 1;
}
});
}
}
一切正常,但我想对其进行优化。当 GridView
中已经有超过 1000 个项目时,我想删除前 300 个项目,这样我就不会 运行 陷入内存不足的问题。
问题是,当我简单地从列表中删除前 300 项时(如 IOnRepositoryDataReturn
实现中的 clearIfNeeded()
方法所示),屏幕会发生变化。我不再看到我在删除之前看到的项目。
示例图片。 images
列表中的第一行(第 1-2 项)被删除的情况。
- 左图 - 删除前的网格
- 居中 - 删除后的网格(就目前而言,删除顶部的项目会将所有项目向上移动)
- 正确 - 我希望它如何表现(删除某处的项目不会影响所见)
黑色方块代表 GridView
。
我想以某种方式调整 GridView
中的查看位置,这样我仍然会看到与删除之前相同的图像。
布局xml:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<ProgressBar
android:id="@+id/ProgressSpinner"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="8dp"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
app:layout_constraintBottom_toBottomOf="@+id/GridView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/GridView" />
<GridView
android:id="@+id/GridView"
android:layout_width="match_parent"
android:layout_height="406dp"
android:layout_marginBottom="8dp"
android:numColumns="3"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
</GridView>
Grid View
有可能吗?还是我应该寻找其他可能性?
When there are already more than 1000 items in the GridView I'd like to remove the first 300 items, so I won't run into out of memory problems.
Is it even possible with Grid View or should I look for some other possibility?
这是我的 2 美分。如果我理解正确,您的关注点似乎是防止 OutOfMemory 异常并提高内存使用率和性能。如果您使用 RecyclerView
和 GridLayoutManager
实现照片网格,它会减少您的许多内存问题。您尝试删除屏幕外的 300 个项目的方法似乎非常麻烦且容易出错。这是标题为 Create a List with RecyclerView:
的官方文档的摘录
回收视图:
The RecyclerView uses a layout manager to position the individual items on the screen and determine when to reuse item views that are no longer visible to the user. To reuse (or recycle) a view, a layout manager may ask the adapter to replace the contents of the view with a different element from the dataset. Recycling views in this manner improves performance by avoiding the creation of unnecessary views or performing expensive findViewById() lookups.
网格布局管理器:
GridLayoutManager arranges the items in a two-dimensional grid, like the squares on a checkerboard. Using a RecyclerView with GridLayoutManager provides functionality like the older GridView layout.
您可以在快速搜索中找到很多这方面的教程,例如:Android GridLayoutManager with RecyclerView
您可以通过以下方式优化它:
- 使用 RecycleView + Grid Layout 而不是 GridView。 RecycleView 将帮助您的视图平滑滚动。
为了滚动到底部,我不再使用 ScrollListener 以获得更好的性能。我会为您提供另一种选择:
- 创建回调
public 接口 AdapterCallback {
void onReachLastItem();
}
致电onBindViewHolder
:
@覆盖
protected void onBindContentViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
if (0 != position && position == getItemCount() - 1 && null != mCallback) {
mCallback.onReachLastItem();
}
}
制作您的片段或activity实施 AdapterCallback 以加载更多数据。
我创建了一个简单的应用程序,它从 Pixabay 获取图像,然后在 GridView
中无限滚动显示它们。
我的OnScrollListener
:
public class BasicOnScrollListener implements AbsListView.OnScrollListener {
private IOnScroll onScroll;
public BasicOnScrollListener(IOnScroll action) {
this.onScroll = action;
}
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
if (firstVisibleItem + visibleItemCount >= totalItemCount - visibleItemCount)
onScroll.onReachedEnd();
}
}
负责数据处理的代码:
private List<Image> images = new ArrayList<>();
....
private void init() {
this.imageAdapter = new ImageAdapter(this, images);
this.gridView.setAdapter(imageAdapter);
populateGridView();
this.gridView.setOnScrollListener(new BasicOnScrollListener(() -> {
populateGridView();
}));
}
private void populateGridView() {
if (!isLoading) {
isLoading = true;
this.progressBar.setVisibility(View.VISIBLE);
imagesRepository.getImages(currentPage, PAGE_SIZE, new IOnRepositoryDataReturn<ImagesList>() {
@Override
public void onData(ImagesList data) {
clearIfNeeded();
images.addAll(data.images);
imageAdapter.notifyDataSetChanged();
onFinish();
}
@Override
public void onError(String error) {
Toast.makeText(getApplicationContext(), error, Toast.LENGTH_LONG).show();
}
private void clearIfNeeded() {
if (images.size() > 1000) {
images.subList(0, 300).clear();
}
}
private void onFinish() {
progressBar.setVisibility(View.INVISIBLE);
isLoading = false;
currentPage = currentPage + 1;
}
});
}
}
一切正常,但我想对其进行优化。当 GridView
中已经有超过 1000 个项目时,我想删除前 300 个项目,这样我就不会 运行 陷入内存不足的问题。
问题是,当我简单地从列表中删除前 300 项时(如 IOnRepositoryDataReturn
实现中的 clearIfNeeded()
方法所示),屏幕会发生变化。我不再看到我在删除之前看到的项目。
示例图片。 images
列表中的第一行(第 1-2 项)被删除的情况。
- 左图 - 删除前的网格
- 居中 - 删除后的网格(就目前而言,删除顶部的项目会将所有项目向上移动)
- 正确 - 我希望它如何表现(删除某处的项目不会影响所见)
黑色方块代表 GridView
。
我想以某种方式调整 GridView
中的查看位置,这样我仍然会看到与删除之前相同的图像。
布局xml:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<ProgressBar
android:id="@+id/ProgressSpinner"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="8dp"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
app:layout_constraintBottom_toBottomOf="@+id/GridView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/GridView" />
<GridView
android:id="@+id/GridView"
android:layout_width="match_parent"
android:layout_height="406dp"
android:layout_marginBottom="8dp"
android:numColumns="3"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
</GridView>
Grid View
有可能吗?还是我应该寻找其他可能性?
When there are already more than 1000 items in the GridView I'd like to remove the first 300 items, so I won't run into out of memory problems. Is it even possible with Grid View or should I look for some other possibility?
这是我的 2 美分。如果我理解正确,您的关注点似乎是防止 OutOfMemory 异常并提高内存使用率和性能。如果您使用 RecyclerView
和 GridLayoutManager
实现照片网格,它会减少您的许多内存问题。您尝试删除屏幕外的 300 个项目的方法似乎非常麻烦且容易出错。这是标题为 Create a List with RecyclerView:
回收视图:
The RecyclerView uses a layout manager to position the individual items on the screen and determine when to reuse item views that are no longer visible to the user. To reuse (or recycle) a view, a layout manager may ask the adapter to replace the contents of the view with a different element from the dataset. Recycling views in this manner improves performance by avoiding the creation of unnecessary views or performing expensive findViewById() lookups.
网格布局管理器:
GridLayoutManager arranges the items in a two-dimensional grid, like the squares on a checkerboard. Using a RecyclerView with GridLayoutManager provides functionality like the older GridView layout.
您可以在快速搜索中找到很多这方面的教程,例如:Android GridLayoutManager with RecyclerView
您可以通过以下方式优化它:
- 使用 RecycleView + Grid Layout 而不是 GridView。 RecycleView 将帮助您的视图平滑滚动。
为了滚动到底部,我不再使用 ScrollListener 以获得更好的性能。我会为您提供另一种选择:
- 创建回调 public 接口 AdapterCallback { void onReachLastItem(); }
致电
onBindViewHolder
: @覆盖 protected void onBindContentViewHolder(RecyclerView.ViewHolder viewHolder, int position) { if (0 != position && position == getItemCount() - 1 && null != mCallback) { mCallback.onReachLastItem(); } }制作您的片段或activity实施 AdapterCallback 以加载更多数据。