When I do adapter.notifyDataSetChanged() into
onProductDetailsResponse it seems that the callback stays pending and the Recycleview doesn't show anything
I'm upgrading the Google Play Billing Library on my app, form 3 highter, I've the same issue on 4.1 and 5.
I retrieve my Subscriptions as Google require:
Define my productId
List<String> skuList = Application.getSubscritionsSkuList();
for (String sku : skuList) {
List<Product> productList.add(Product.newBuilder()
.setProductId(sku)
.setProductType(BillingClient.ProductType.SUBS)
.build());
Define my RecycleView and set My adapter
RecyclerView recyclerView = getActivity().findViewById(R.id.SubscritionsList);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext(), LinearLayoutManager.VERTICAL, false));
SubscriptionAdapter subscriptionAdapter = new SubscriptionAdapter(
context.getApplicationContext(), getActivity(), inventoryAsyncSub);
recyclerView.setAdapter(subscriptionAdapter);
Then retrieve the Subscriptions form the Store
QueryProductDetailsParams params = QueryProductDetailsParams.newBuilder()
.setProductList(productList)
.build();
billingClient.queryProductDetailsAsync(
params,
new ProductDetailsResponseListener() {
public void onProductDetailsResponse(BillingResult billingResult, List<ProductDetails> productDetailsList) {
inventoryAsyncSub.addAll(productDetailsList);
subscriptionAdapter.notifyDataSetChanged();
}
}
);
The issue is: when I do subscriptionAdapter.notifyDataSetChanged() it seems that the callback from queryProductDetailsAsync stays pending and the Recycleview doesn't show anything till I scroll it and navigating out from this Fragment the app stays waiting something: no other callback works
First I thought that it depend form deprecated method querySkuDetailsAsync() so following the Google guide I migrated to queryProductDetailsAsync method, but the issue still persist.
My Adapter has got any of characteristic:
public class SubscriptionAdapter extends RecyclerView.Adapter<SubscriptionAdapter.Viewholder>{
public Object setOnItemClickListener;
private Context context;
private Activity activity;
private List<ProductDetails> InventoryAsyncSub;
private static ClickListener clickListener;
public SubscriptionAdapter(
Context context,
Activity activity,
List<ProductDetails> InventoryAsyncSub
) {
this.context = context;
this.activity = activity;
this.InventoryAsyncSub = InventoryAsyncSub;
}
#NonNull
#Override
public SubscriptionAdapter.Viewholder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.subscrition_card, parent, false);
return new Viewholder(view);
}
#Override
public void onBindViewHolder(#NonNull SubscriptionAdapter.Viewholder holder, int position) {
ProductDetails subScr = InventoryAsyncSub.get( position );
PurchaseHandlerTools.setItemTitle( activity, holder.name, subScr.getTitle() );
holder.description.setText( subScr.getDescription() );
holder.pricesBox.findViewById( R.id.item_price);
TextView price = holder.pricesBox.findViewById( R.id.item_price);
price.setText( subScr.getSubscriptionOfferDetails().get(0).getPricingPhases().getPricingPhaseList().get(0).getFormattedPrice() );
}
#Override
public int getItemCount() {
return InventoryAsyncSub.size();
}
// stores and recycles views as they are scrolled off screen
public class Viewholder extends RecyclerView.ViewHolder implements View.OnClickListener {
private TextView name;
private TextView description;
private LinearLayout pricesBox;
public Viewholder(#NonNull View itemView) {
super(itemView);
name = itemView.findViewById(R.id.submition_name);
description = itemView.findViewById(R.id.submition_description);
pricesBox = itemView.findViewById(R.id.purchase_btn);
itemView.setOnClickListener( this::onClick );
}
#Override
public void onClick(View v) {
clickListener.onItemClick( getAdapterPosition(), v );
}
}
public void setOnItemClickListener(ClickListener clickListener) {
SubscriptionAdapter.clickListener = clickListener;
}
public interface ClickListener{
void onItemClick( int position, View view );
}
}
Related
I am stuck need your kind help, i have an activity in android user sign up, and apart from general information i have a list of existing users/entities shown in recyclerview and each item of recyclerview list contains a follow button, i want to captures the position/ buttons that were clicked to check which users/entities the new user has followed. Any suggest how to do it?
I had customer adapter and recycler view, Check the attached image for clarity. I want to create an array list that will contain ids of each list item whose button is clicked.
RecyclerView containing list item and follow button
//this adapter use on admin
public class FollowLocalBusinessAdapter extends RecyclerView.Adapter {
AlertDialog.Builder alertDialogBuilder;
private Context context;
private List<FollowLocalBusinessMC> list;
private LayoutInflater layoutInflater;
private DatabaseReference mDatabase;//database reference
private FollowLocalBusinessMC localBusinessMC;
private ListAdapterListener mListener;
public FollowLocalBusinessAdapter(Context context, List<FollowLocalBusinessMC> list) {
this.context = context;
this.list = list;
layoutInflater = LayoutInflater.from(context);
alertDialogBuilder = new AlertDialog.Builder(context);
}
#Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int position) {
View view = layoutInflater.inflate(R.layout.list_item_local_business, parent, false);
MyViewHolder holder = new MyViewHolder(view);
return holder;
}
#Override
public void onBindViewHolder(final FollowLocalBusinessAdapter.MyViewHolder holder, final int position)//is called
{
localBusinessMC = list.get(position);
if (holder != null) {
if (holder.followBtn.getText().equals("Follow")) {
holder.followBtn.setBackgroundResource(R.drawable.follow_btn_bg);
holder.followBtn.setTextColor(context.getResources().getColor(R.color.colorBlueButton));
} else {
holder.followBtn.setBackgroundResource(R.drawable.following_btn_bg);
holder.followBtn.setTextColor(context.getResources().getColor(R.color.colorWhite));
}
holder.mainHeading.setText(localBusinessMC.getMainHeading());
holder.subHeading.setText(localBusinessMC.getSubHeading());
Glide.with(context).load(localBusinessMC.getIcon()).into(holder.icon);
holder.followBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Toast.makeText(context, "" + list.get(position).getId(), Toast.LENGTH_SHORT).show();
if (holder.followBtn.getText().equals("Follow")) {
holder.followBtn.setText("Following");
holder.followBtn.setTextColor(context.getResources().getColor(R.color.colorWhite));
holder.followBtn.setBackgroundResource(R.drawable.following_btn_bg);
} else {
holder.followBtn.setText("Follow");
holder.followBtn.setBackgroundResource(R.drawable.follow_btn_bg);
holder.followBtn.setTextColor(context.getResources().getColor(R.color.colorBlueButton));
}
}
});
}
}
#Override
public int getItemCount() {
return list.size();
}
public interface ListAdapterListener { // create an interface
void onClickAtOKButton(int position); // create callback function
}
class MyViewHolder extends RecyclerView.ViewHolder {
TextView mainHeading, subHeading;
ImageView icon;
Button followBtn;
MyViewHolder(View v) {
super(v);
mainHeading = v.findViewById(R.id.main_lbsr_id);
subHeading = v.findViewById(R.id.sub_lbsr_id);
icon = v.findViewById(R.id.icon_lbsr_id);
followBtn = v.findViewById(R.id.followBtn_lbsr_id);
}
}
}
create an interface in your Adapter with one method :
public interface CallBack{
void onItemClicked(int position , /*anyThing else*/);
}
then get an instance of interface in your Adapter :
private YourAdapter.CallBack;
in your Adpater constructor get object of CallBack :
public YourAdapter(Context context, List</* */> list, YourAdapter.CallBackcallback callBack) {
this.context = context;
this.list= list;
this.callback = callback;
}
#Override
public void onBindViewHolder(#NonNull ChoosenExerciseViewHolder holder, final int position) {
holder.View.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
callback.onItemClicked(
//pass any parameter youe defined
//for ex : position , ///);
}
});
}
then in your Activity/Fragment :
private YourAdapter.CallBack callback= (position, /* */ ) -> {
// do what you want
};
YourAdapter adapter = new YourAdapter(getContext(), calback, list);
receyclerview.setAdapter(adapter )
I am having a problem to update an image in RecylcerView.Adapter on change. I am having this class for my Adapter:
public class ShowMembersAdapter extends RecyclerView.Adapter<ShowMembersAdapter.ViewHolder> {
private ArrayList<User> users;
private Context context;
private ShowTeamsAdapter.OnItemClickListener mListener;
public void setOnItemClickListener(ShowTeamsAdapter.OnItemClickListener listener) {
mListener = listener;
}
public static class ViewHolder extends RecyclerView.ViewHolder {
private CircleImageView mImage;
public ViewHolder(View itemView, ShowTeamsAdapter.OnItemClickListener listener) {
super(itemView);
mImage = itemView.findViewById(R.id.image);
itemView.setOnClickListener(view -> {
if (listener != null) {
int position = getAdapterPosition();
if (position != RecyclerView.NO_POSITION) {
listener.onItemClick(position);
}
}
});
}
}
public ShowMembersAdapter(ArrayList<User> users, Context context) {
this.users = users;
this.context = context;
}
#NonNull
#Override
public ShowMembersAdapter.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_member_item, parent, false);
ShowMembersAdapter.ViewHolder viewHolder = new ShowMembersAdapter.ViewHolder(v, mListener);
return viewHolder;
}
#Override
public void onBindViewHolder(#NonNull ShowMembers
String imageUrl = "MYURL" + users.get(position).getEmail() + ".jpg";
Picasso.get().load(imageUrl).resize(200,200).error(R.mipmap.ic_team_member_no_photo).into(holder.mImage);
}
#Override
public int getItemCount() {
return users.size();
}
}
I am populating this class from my Fragment. It works perfectly when I start an application. However, whenever I update the picture from application, I guess that adapter won't change because the image URL is still the same. Do you have any other ideas, that I can do? Do you think I should call one more Volley request to get Images and save them to each User, so I don't call them directly from Adapter?
Thanks for your help!
After updating your picture, have you tried this:
yourAdapter.notifyDataSetChanged()
I have an Android To-do list app with SQLite database accessed using Room. Then I wanted to make a list of available lists in navigation drawer. So I used Paging Library. Now the RecyclerView is empty and I don't know why. BTW: none of my lists work. I am a beginner, so I stick to the official tutorial. I've already found out, that they are wrong and that ViewModel needs an empty constructor. Now the situation looks like this: the method, that sets up the adapter is called successfully (8), ViewModel is created (7) Adapter is created (1), the setList() method is called (9), but no Holder is being created (4), no onCreateViewHolder or onBindViewHolder are called (2,3), no DiffCalback methods are called (5,6) and just an empty Recycler view is displayed. The PagedList<SQLList> contains right information, but no information is being passed to the adapter. Has anybody any idea why?
//Sorry for my English.
Adapter class:
//this is my Adapter. SQLList is my object (Equivalent to the User object in
the tutorial)
public class ListsAdapter extends PagedListAdapter<SQLList,ListsAdapter.Holder> {
private Context context;
public ListsAdapter(Context context) {
//1
super(DIFF_CALLBACK);
this.context = context;
}
#Override
public Holder onCreateViewHolder(ViewGroup parent, int viewType) {
//2
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view = inflater.inflate(R.layout.list_item,null);
return new Holder(view);
}
#Override
public void onBindViewHolder(Holder holder, int position) {
//3
SQLList item = getItem(position);
if (item == null){
holder.clear();
}else {
holder.bindTo(item);
}
}
public static class Holder extends RecyclerView.ViewHolder {
public TextView textView;
public int id;
public TextView idView;
public Holder(View itemView) {
//4
super(itemView);
textView = itemView.findViewById(R.id.textView);
idView = itemView.findViewById(R.id.idTextView);
}
public void bindTo(SQLList list) {
textView.setText(list.getName());
id = list.getId();
idView.setText(Integer.toString(list.getId()));
}
public void clear() {
textView.setText("");
id = 0;
idView.setText("0");
}
}
private static final DiffCallback<SQLList> DIFF_CALLBACK = new DiffCallback<SQLList>() {
#Override
public boolean areItemsTheSame(#NonNull SQLList oldItem, #NonNull SQLList newItem) {
//5
return oldItem.getId() == newItem.getId();
}
#Override
public boolean areContentsTheSame(#NonNull SQLList oldItem, #NonNull SQLList newItem) {
//6
return oldItem.getName().equals(newItem.getName());
}
};
}
View Model:
public static class ListViewModel extends ViewModel {
public final LiveData<PagedList<SQLList>> listsList;
public ListViewModel(){
//7
listsList = new LivePagedListBuilder<>(DMO.getDao().loadListFactory(),20).build();
}
}
Part of Fragment the RecyclerView is placed in:
...
//8
ListViewModel listViewModel = ViewModelProviders.of(this).get(ListViewModel.class);
adapter = new ListsAdapter(context);
listViewModel.listsList.observe(this, new Observer<PagedList<SQLList>>() {
#Override
public void onChanged(#Nullable PagedList<SQLList> sqlLists) {
//9
adapter.setList(sqlLists);
}
});
recyclerView.setAdapter(adapter);
...
Problem found: I forgot to set layout manager!
I had the same problem but I accidentally added-
RecyclerView.setHasFixedSize(true);
After removing this, It worked.
Same issue I had and I just removed RecyclerView.setHasFixedSize(true) then it started working fine.
I had to set the adapter. recyclerView.setAdapter(adapter);
I'm trying to build a simple To Do List app using a RecyclerView with a custom Adapter. I've build a model data class to get and set my todos. My problem is that each time I click the button to add my item, nothing happens. I did call notifyDataSetChanged() method but it still doesn't work.
This is a practice app that I'm building to learn RecyclerViews and custom adapters so I'd appreciate it if someone could explain why it doesn't work and perhaps explain to me how I can fix it.
This is my code where I'm retrieving the user input from the EditText field and placing it in my data:
public class MainActivity extends AppCompatActivity {
private ToDoData toDoData;
private List<ToDoData> toDoList = new ArrayList<>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final ToDoAdapter toDoAdapter = new ToDoAdapter(toDoList, this);
toDoData = new ToDoData(this);
final EditText toDoInput = (EditText)findViewById(R.id.add_todo);
Button toDoAdd = (Button)findViewById(R.id.add_item);
RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(this);
RecyclerView toDoDisplay = (RecyclerView) findViewById(R.id.toDoDisplayRecyclerView);
toDoDisplay.setAdapter(toDoAdapter);
toDoDisplay.setHasFixedSize(true);
toDoDisplay.setLayoutManager(layoutManager);
toDoAdd.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
toDoData.setToDo(toDoInput.getText().toString());
toDoAdapter.notifyDataSetChanged();
}
});
}
}
This is my model class with getters and setters. The list-item has only 1 field so its rather small:
public class ToDoData {
private String toDoString;
public ToDoData(String todoString){
this.toDoString = todoString;
}
public ToDoData(Context context){}
public String getToDo() {
return toDoString;
}
public void setToDo(String toDoString) {
this.toDoString = toDoString;
}
}
And this is my custom adapter. I followed through a tutorial while building this but I do however think that my problem stems from the adapter:
public class ToDoAdapter extends RecyclerView.Adapter<ToDoAdapter.ViewHolder> {
private List<ToDoData> toDoList;
private Context context;
public ToDoAdapter(List<ToDoData> todoList, Context context) {
this.toDoList = todoList;
this.context = context;
}
public class ViewHolder extends RecyclerView.ViewHolder {
TextView todoView;
public ViewHolder(View itemView) {
super(itemView);
todoView = (TextView) itemView.findViewById(R.id.to_do_display);
}
}
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.to_do_list_item, parent, false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
ToDoData todo = toDoList.get(position);
holder.todoView.setText(todo.getToDo());
}
#Override
public int getItemCount() {
return (toDoList == null) ? 0 : toDoList.size();
}
}
Add one setter method in your adapter class for setting the collection and then invoke notifyDataSetChanged.
Declare the ToDoAdapter as class member and then invoke the setter function on Button click as follows,
adapter.setTodoList(/* pass collection */);
adapter.notifyDataSetchanged();
I having a recyclerview with GridLayoutManager, it populating the grid properly but not updating the data.
Below is my code,
public class AppsGridViewAdapter extends RecyclerView.Adapter<AppsGridViewAdapter.GridViewHolder>{
private ArrayList<App> appArrayList;
private Context context;
public AppsGridViewAdapter(Context context,ArrayList<App> appArrayList){
this.appArrayList = appArrayList;
this.context = context;
}
public static class GridViewHolder extends RecyclerView.ViewHolder{
CompoundAppButton compoundAppButton;
public GridViewHolder(CompoundAppButton itemView){
super(itemView);
compoundAppButton = (CompoundAppButton)itemView;
}
}
#Override
public GridViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
CompoundAppButton appButton = new CompoundAppButton(context,null);
return new GridViewHolder(appButton);
}
#Override
public void onBindViewHolder(GridViewHolder holder, int position) {
App app = appArrayList.get(position);
holder.compoundAppButton.updateButtonConfig(app);
}
#Override
public int getItemCount() {
return appArrayList.size();
}
}
Having valid values in the list, but not updating. Where CustomAppButton is a customview. Anyone can help me why it doesnt update.
UPDATE:
I calling recyclerview from my activity, below is the specific code
recyclerView = (RecyclerView)findViewById(R.id.recycler_view);
recyclerView.addItemDecoration(new MarginDecoration(this));
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(new GridLayoutManager(this, 3));
AppsGridViewAdapter adapter = new AppsGridViewAdapter(this,appList);
recyclerView.setAdapter(adapter);
The activity contains only recyclerview.
updateButtonConfig is a function in CustomAppButton class, this is where view is updated. getting data to this function, have checked the log.
public void updateButtonConfig(App app){
Picasso.with(context)
.load(app.getAppIcon())
.placeholder(R.drawable.app_icon)
.into(appIcon);
appName.setText(app.getAppName());
category.setText(app.getAppCategory());
price.setText(app.getAppPrice());
appPackage = app.getAppPackage();
}