I've created a simple app that fetches images from Pixabay and then displays them in a GridView
with infinite scroll.
My 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();
}
}
Code resposible for data handling:
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;
}
});
}
}
Everything works fine but I'd like to optimize that. 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.
The problem is, when I simply remove the first 300 items from list (as shown in clearIfNeeded()
method in IOnRepositoryDataReturn
implementation), the screen shifts. I no longer see items that I've seen before removal.
Example image. Situation if first row (items 1-2) from images
list is removed.
The black square is representing the GridView
.
I'd like to somehow adjust the viewing position in GridView
, so I'd still see the same images as before removal.
Layout 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>
Is it even possible with Grid View
or should i look for some other possibility?
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?
Here is my 2 cents. If I understand correctly, your concern seems to be preventing OutOfMemory exceptions and improving memory usage and performance. If you implement the grid of photos using RecyclerView
and GridLayoutManager
, it would reduce many of your problems with memory. Your approach of trying to remove 300 items which are off screen, seems pretty cumbersome and error prone. Here is an excerpt from the official doc titled Create a List with RecyclerView:
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:
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.
You can find many tutorials for this on quick search eg: Android GridLayoutManager with RecyclerView
You can optimize this by:
To reach scroll to bottom, I no longer use ScrollListener for better performance. I will provide you another option:
Call onBindViewHolder
:
@Override
protected void onBindContentViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
if (0 != position && position == getItemCount() - 1 && null != mCallback) {
mCallback.onReachLastItem();
}
}
Make your fragment or activity implement AdapterCallback to load more data.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With