I have a list of posts built using a RecyclerView. The classes that I have for this scenario are a fragment, the adapter and the view holder. When the fragment is loaded I get the list of posts from an API call using okhttp and pass the list to the adapter.
For each item in the list I have a context menu which I'm defining in the view holder like this.
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
menu.setHeaderTitle("Actions");
if (!post.isRemoved()) {
menu.add(Menu.NONE, 1, 1, "Move to Trash")
.setOnMenuItemClickListener(onTrashMenu);
}
else {
menu.add(Menu.NONE, 1, 1, "Delete")
.setOnMenuItemClickListener(onDeleteMenu);
menu.add(Menu.NONE, 2, 2, "Restore")
.setOnMenuItemClickListener(onRestoreMenu);
}
}
Also, every listener is in the view holder class. For example:
private final MenuItem.OnMenuItemClickListener onDeleteMenu = item -> {
OkHttpClient client = new OkHttpClient();
String url = "SOME_URL";
Request request = new Request.Builder()
.url(url)
.delete()
.header("SOME_HEADER")
.build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(@NotNull Call call, @NotNull IOException e) {
Log.e("POSTS_VIEW_HOLDER", e.getMessage());
Snackbar.make(PostViewHolder.super.itemView, "Failed to remove post.", Snackbar.LENGTH_SHORT)
.show();
}
@Override
public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
Snackbar.make(PostViewHolder.super.itemView, "Post removed.", Snackbar.LENGTH_SHORT)
.show();
}
});
return true;
};
I've tried using an interface in the way described here.
Adapter's constructor requires the interface as an argument. And then I pass it further to the view holder when I create it.
public PostsAdapter(Post[] posts, AdapterInterface adapterInterface) {
this.posts = posts;
this.adapterInterface = adapterInterface;
}
@NonNull
@Override
public PostViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
View postItemView = LayoutInflater.from(viewGroup.getContext())
.inflate(R.layout.post_item, viewGroup, false);
return new PostViewHolder(postItemView, adapterInterface);
}
Passed from the fragment like this:
postsAdapter = new PostsAdapter(null, this::loadPosts);
The loadPosts() method uses okhttp and in the callback sets the adapter's data set and calls .notifyDataSetChanged().
When calling the method from the interface in the view holder I got this:
only the original thread that created a view hierarchy can touch its views
So I used a handler to get around it:
private Handler mHandler = new Handler(Looper.getMainLooper());
mHandler.post(() -> {
adapterInterface.dataSetChanged();
});
But now, whenever this method is called, nothing happens. I would like to know if what I've done makes some sense and also, if there's a better way (or, at least, one that's working) to go about this.
Edit
loadPosts() implementation:
public void loadPosts() {
progressBar.setVisibility(View.VISIBLE);
Request request = new Request.Builder()
.url(url)
.build();
this.client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.e(tag, e.getMessage());
}
@Override
public void onResponse(Call call, Response response) throws IOException {
final Post[] posts = gson.fromJson(response.body().string(), Post[].class);
mHandler.post(() -> {
postsAdapter.setPosts(posts);
postsAdapter.notifyDataSetChanged();
progressBar.setVisibility(View.INVISIBLE);
});
}
});
}