Is There a Callback for When Recyclerview Has Finished Showing Its Items After I'Ve Set It with an Adapter

Is there a callback for when RecyclerView has finished showing its items after I've set it with an adapter?

I've found a way to solve this (thanks to user pskink), by using the callback of LayoutManager:

final LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false) {
@Override
public void onLayoutChildren(final Recycler recycler, final State state) {
super.onLayoutChildren(recycler, state);
//TODO if the items are filtered, considered hiding the fast scroller here
final int firstVisibleItemPosition = findFirstVisibleItemPosition();
if (firstVisibleItemPosition != 0) {
// this avoids trying to handle un-needed calls
if (firstVisibleItemPosition == -1)
//not initialized, or no items shown, so hide fast-scroller
mFastScroller.setVisibility(View.GONE);
return;
}
final int lastVisibleItemPosition = findLastVisibleItemPosition();
int itemsShown = lastVisibleItemPosition - firstVisibleItemPosition + 1;
//if all items are shown, hide the fast-scroller
mFastScroller.setVisibility(mAdapter.getItemCount() > itemsShown ? View.VISIBLE : View.GONE);
}
};

The good thing here is that it works well and will handle even keyboard being shown/hidden.

The bad thing is that it gets called on cases that aren't interesting (meaning it has false positives), but it's not as often as scrolling events, so it's good enough for me.


EDIT: there is a better callback that was added later, which doesn't get called multiple times. Here's the new code instead of what I wrote above:

recyclerView.setLayoutManager(new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false) {
@Override
public void onLayoutCompleted(final State state) {
super.onLayoutCompleted(state);
final int firstVisibleItemPosition = findFirstVisibleItemPosition();
final int lastVisibleItemPosition = findLastVisibleItemPosition();
int itemsShown = lastVisibleItemPosition - firstVisibleItemPosition + 1;
//if all items are shown, hide the fast-scroller
fastScroller.setVisibility(adapter.getItemCount() > itemsShown ? View.VISIBLE : View.GONE);
}
});

callback on first/last visible item changed in RecyclerView?

You can use a RecyclerView.OnScrollListener to be notified any time the RecyclerView is scrolled, and you can use getChildAt() with getChildViewHolder() to retrieve the top/bottom views and their respective ViewHolders:

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

final TextView top = findViewById(R.id.top);
final TextView bottom = findViewById(R.id.bottom);
final RecyclerView recycler = findViewById(R.id.recycler);

recycler.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL));
recycler.setLayoutManager(new LinearLayoutManager(this));
recycler.setAdapter(new MyAdapter());
recycler.addOnScrollListener(new RecyclerView.OnScrollListener() {

@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
View firstChild = recycler.getChildAt(0);
MyViewHolder topHolder = (MyViewHolder) recycler.getChildViewHolder(firstChild);
top.setText("Topmost visible view: " + topHolder.value);

View lastChild = recycler.getChildAt(recycler.getChildCount() - 1);
MyViewHolder bottomHolder = (MyViewHolder) recycler.getChildViewHolder(lastChild);
bottom.setText("Bottommost visible view: " + bottomHolder.value);
}
});
}

Sample Image

Android recyclerview does not show any data

Your method is asynchronous, so the code in onResponse runs in the future, after your getTaskData method has already returned an empty list. You would need to modify your program flow to accommodate this.

Here is an example of how you could define your own custom callback to handle that, by setting up the RecyclerView empty at first and then adding the data once it arrives.

Step 1: define an interface somewhere (e.g. in the activity or retrofit request class)

interface OnTasksRetrieved {
void getResult(List<Task> result);
}

Step 2: change your getTasksData method to take a callback implementing that interface instead of returning a list

public static void getTasksData(OnTasksRetrieved callback) {
//...
call.enqueue(new Callback<List<Task>>() {
@Override
public void onResponse(Call<List<Task>> call, Response<List<Task>> response) {
// Call your callback once you have retrieved the data
callback.getResult(response.body());
}
@Override
public void onFailure(Call<List<Task>> call, Throwable t) {
}
});

// do not return anything
}

Step 3: when you start the async call in onCreate, create an instance of the callback interface to pass to it to handle the result

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_list_animation);
parent_view = findViewById(android.R.id.content);
initComponent();

// start the async fetch here - it won't finish until
// later in the future, when it will call the callback code
// below to update the RecyclerView data
getTasksData(
new OnTasksRetrieved() {
@Override
public void getResult(List<Task> result) {
// The code in here runs much later in the future - the adapter
// will already have been set up, but will be empty.
myAdapter.setItems(result); // add this method to the adapter
}
}
);
}

You will need to modify the adapter so it doesn't take the List as an input, but instead has a setItems method something like this, which will be called once the data is retrieved:

private List<Task> items = new ArrayList<>();

public AdapterTasks(Context context, int animation_type) {
ctx = context;
this.animation_type = animation_type;
}

public void setItems(List<Task> data) {
items.clear();
items.addAll(data);
notifyDataSetChanged();
}


Related Topics



Leave a reply



Submit