Update: One of the problems is solved: Now updateList is resolved, the problem was that I defined mAdapter as RecyclerView.Adapter instead of MyAdapter. But now even though I am getting data, nothing shows up on the list, it's empty
--------------------ORIGINAL POST--------------------
I want to update my RecyclerView using DiffUtil to prevent duplicates.
I have 4 classes: The User class, the Activity class where I set data, the Adapter class and the DiffUtil class. I am not sure I combine all these 4 correctly.
This is the User class:
public class User {
private String mUserId;
private Uri mImageUrl;
public User(String userId, String imageUrl) {
mUserId = userId;
mImageUrl = Uri.parse(imageUrl);
}
public String getUserId() {
return mUserId;
}
public Uri getImageUrl() {
return mImageUrl;
}
}
This is how I set data dynamically (I keep getting new Json arrays from the server containing user id's to be displayed, then I set the user image from Firebase storage): (It's a function invoked by an onClick listener:)
This is the method call from the fragment:
button.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
updateUsersList();
}
});
This is the function:
private void updateUsersList() {
#Override
public void onResponse(JSONArray response) { // the JSON ARRAY response of user ids ["uid1", "uid334", "uid1123"]
myDataset.clear(); // clear dataset to prevent duplicates
for (int i = 0; i < response.length(); i++) {
try {
String userKey = response.get(i).toString(); // the currently iterated user id
final DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
DatabaseReference userKeyRef = rootRef.child("users").child(userKey); // reference to currently iterated user
ValueEventListener listener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
myDataset.add(new User(dataSnapshot.getKey(), dataSnapshot.child("imageUrl").getValue().toString())); //add new user: id and image url
mAdapter.updateList(myDataset); // cannot resolve this method, why?
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
Log.d(TAG, databaseError.getMessage());
}
};
userKeyRef.addListenerForSingleValueEvent(listener);
}
catch (JSONException e) { Log.d(TAG, "message " + e); }
}
}
This is how my DiffUtil class looks like:
public class MyDiffUtilCallBack extends DiffUtil.Callback{
ArrayList<User> oldUsers;
ArrayList<User> newUsers;
public MyDiffUtilCallBack(ArrayList<User> newUsers, ArrayList<User> oldUsers) {
this.newUsers = newUsers;
this.oldUsers = oldUsers;
}
#Override
public int getOldListSize() {
return oldUsers.size();
}
#Override
public int getNewListSize() {
return newUsers.size();
}
#Override
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
return oldUsers.get(oldItemPosition).getUserId().equals( newUsers.get(newItemPosition).getUserId());
}
#Override
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
return oldUsers.get(oldItemPosition).equals(newUsers.get(newItemPosition));
}
#Nullable
#Override
public Object getChangePayload(int oldItemPosition, int newItemPosition) {
//you can return particular field for changed item.
return super.getChangePayload(oldItemPosition, newItemPosition);
}
}
And this is my adapter:
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
private ArrayList<User> mDataset;
private MyViewHolder myHolder;
private User user;
public static class MyViewHolder extends RecyclerView.ViewHolder {
public TextView singleItemTextView;
public ImageView singleItemImage;
public View layout;
public ConstraintLayout constraintLayout;
public MyViewHolder(View v) {
super(v);
layout = v;
singleItemImage = (ImageView) v.findViewById(R.id.icon);
singleItemTextView = (TextView) v.findViewById(R.id.singleitemtv);
constraintLayout = (ConstraintLayout) v.findViewById(R.id.nbConstraintLayout);
}
}
// Provide a suitable constructor (depends on the kind of dataset)
public MyAdapter(ArrayList<User> myDataset) {
mDataset = myDataset;
}
// Create new views (invoked by the layout manager)
#Override
public MyAdapter.MyViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
View v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.nb_image_view, parent, false);
MyViewHolder vh = new MyViewHolder(v);
return vh;
}
#Override
public void onBindViewHolder(final MyViewHolder holder, final int position) {
myHolder = holder;
user = mDataset.get(position);
Uri userImage = user.getImageUrl();
myHolder.singleItemTextView.setText(user.getUserId());
Glide.with(myHolder.itemView.getContext() /* context */)
.load(userImage)
.into(myHolder.singleItemImage);
myHolder.constraintLayout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Context context = v.getContext();
Intent intent = new Intent(v.getContext(), DisplayUserActivity.class);
context.startActivity(intent);
}
});
}
public void updateList(ArrayList<User> newList) {
DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new MyDiffUtilCallBack(this.mDataset, newList));
diffResult.dispatchUpdatesTo(this);
}
}
I am not sure I combine all the classes correctly (my first time using DiffUtil), and I also get cannot resolve method updateList(?)
What am I doing wrong?
This is how I define mAdapter in my Fragment:
public class MyFragment extends Fragment {
private ArrayList<User> myDataset;
private RecyclerView.Adapter mAdapter;
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
rootView = inflater.inflate(R.layout.fragment_lks, container, false);
mRecyclerView = (RecyclerView) rootView.findViewById(R.id.my_recycler_view);
myDataset = new ArrayList<User>();
mAdapter = new MyAdapter(myDataset);
The problem comes from definition of mAdapter. You defined it as RecyclerView.Adapter which is super class of your MyAdapter and it does not contain updateList(). You should change it as following:
private MyAdapter mAdapter;
Updated 1/13/2019:
I've revised your adapter with AsyncListDiffer which calculates the diffrence asynchronously then applies it to the adapter.
MyAdapter.java
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.constraint.ConstraintLayout;
import android.support.v7.recyclerview.extensions.AsyncListDiffer;
import android.support.v7.util.DiffUtil;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import com.bumptech.glide.Glide;
import java.util.List;
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
private AsyncListDiffer<User> mAsyncListDiffer;
public static class MyViewHolder extends RecyclerView.ViewHolder {
public TextView singleItemTextView;
public ImageView singleItemImage;
public View layout;
public ConstraintLayout constraintLayout;
public MyViewHolder(View v) {
super(v);
layout = v;
singleItemImage = (ImageView) v.findViewById(R.id.icon);
singleItemTextView = (TextView) v.findViewById(R.id.singleitemtv);
constraintLayout = (ConstraintLayout) v.findViewById(R.id.nbConstraintLayout);
}
}
// Provide a suitable constructor (depends on the kind of dataset)
public MyAdapter() {
DiffUtil.ItemCallback<User> diffUtilCallback = new DiffUtil.ItemCallback<User>() {
#Override
public boolean areItemsTheSame(#NonNull User newUser, #NonNull User oldUser) {
return newUser.getUserId().equals(oldUser.getUserId());
}
#Override
public boolean areContentsTheSame(#NonNull User newUser, #NonNull User oldUser) {
return newUser.equals(oldUser);
}
};
mAsyncListDiffer = new AsyncListDiffer<>(this, diffUtilCallback);
}
// Create new views (invoked by the layout manager)
#Override
public MyAdapter.MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.nb_image_view, parent, false);
MyViewHolder vh = new MyViewHolder(v);
return vh;
}
#Override
public void onBindViewHolder(final MyViewHolder holder, final int position) {
User user = mAsyncListDiffer.getCurrentList().get(position);
Uri userImage = user.getImageUrl();
holder.singleItemTextView.setText(user.getUserId());
Glide.with(holder.itemView.getContext() /* context */)
.load(userImage)
.into(holder.singleItemImage);
holder.constraintLayout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Context context = v.getContext();
Intent intent = new Intent(v.getContext(), DisplayUserActivity.class);
context.startActivity(intent);
}
});
}
#Override
public int getItemCount() {
return mAsyncListDiffer.getCurrentList().size();
}
public void updateList(List<User> newList) {
mAsyncListDiffer.submitList(newList);
}
}
User.java
public class User {
private String mUserId;
private Uri mImageUrl;
public User(String userId, String imageUrl) {
mUserId = userId;
mImageUrl = Uri.parse(imageUrl);
}
public String getUserId() {
return mUserId;
}
public Uri getImageUrl() {
return mImageUrl;
}
#Override
public boolean equals(Object other) {
if (other instanceof User) {
User user = (User) other;
return mUserId.equals(user.getUserId()) && mImageUrl.equals(user.getImageUrl());
} else {
return false;
}
}
}
In addition to #aminography's answer, I suggest you to use ListAdapter, a RecyclerView.Adapter implementation that makes it easier to update you RecyclerView with the correct animations. This class is included in the recyclerview support library.
Below is an example of usage based on your use case:
public class MyAdapter extends ListAdapter<User, UserViewHolder> {
public MyAdapter() {
super(new UserDiffCallback());
}
public UserViewHolder onCreateViewHolder(int position, int viewType) { ... }
public void onBindViewHolder(UserViewModel holder, int position) {
User userAtPosition = getItem(position); // getItem is a protected method from ListAdapter
// Bind user data to your holder...
}
}
public class UserDiffCallback extends DiffUtil.ItemCallback<User> {
#Override
public boolean areItemsTheSame(#NonNull User oldUser, #NonNull User newUser) {
return oldUser.getUserId().equals(newUser.getUserId());
}
#Override
public boolean areContentsTheSame(#NonNull User oldUser, #NonNull User newUser) {
// No need to check the equality for all User fields ; just check the equality for fields that change the display of your item.
// In your case, both impact the display.
return oldUser.getUserId().equals(newUser.getUserId())
&& (oldUser.getImageUrl() == null) ? newUser.getImageUrl() == null : oldUser.getImageUrl().equals(newUser.getImageUrl());
}
}
Then, when you need to update the list with new users, call myAdapter.submitList(newList). Juste like with AsyncListDiffer, the diff between the two list is calculated on a background Thread.
Modify your method:
public void updateList(ArrayList<User> newList) {
DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new MyDiffUtilCallBack(this.mDataset, newList));
this.mDataSet.clear()
this.mDataSet.addAll(newList)
diffResult.dispatchUpdatesTo(this);
}
Looks like
public void onBindViewHolder(#NonNull ViewHolder holder, int position, #NonNull List<Object> payloads) not implemented
Implement this to use DiffUtils properly, as this method will be called for the changes, and based on the payload you can update your recyclerview items instead of calling notifyDataSetChanged()
Related
When I click on an item, OnItemListener.onItemClick (see in Adapter code) works, and remove the
respective from LiveData<List> mListLivedata (see in ViewModel).
The problem is that this doesn't update recyclerView, despite the fact that there is an
Observer which subscribes on this LiveData. There is still 4 views in recyclerView, but if
if I click on the last item in view (after having already clicked any item before),
it crashes because there is already less by one object in Livedata<List<>> (cause previous click has
deleted one Object). So, the number of Objects in LiveData is reduced by one after click (which
is what I need), but the number of items in view stays the same (which is bug and I don't understant
where is my mistake).
Where is my mistake and what is the solution?
In ViewModel:
public class PersonViewModel extends AndroidViewModel {
private PersonRepository mRepository;
private CompositeDisposable composite = new CompositeDisposable();
private Single<List<Person>> mThreePersons;
private ArrayList<Person> mList = new ArrayList<>();
private MutableLiveData<List<Person>> mListLivedata = new MutableLiveData<>();
public PersonViewModel(#NonNull Application application) {
super(application);
mRepository = new PersonRepository(application);
mThreePersons = mRepository.getThreePersons();
mThreePersons.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new SingleObserver<List<Person>>() {
#Override
public void onSubscribe(Disposable d) {
Log.d(TAG, "onSubscribe(Disposable d): called");
composite.add(d);
}
#Override
public void onSuccess(List<Person> people) {
Log.d(TAG, "onSuccess: called");
mList.addAll(people);
mListLivedata.setValue(mList);
}
#Override
public void onError(Throwable e) {
Log.d(TAG, "onError: called");
Toast.makeText(application, "NO DATA", Toast.LENGTH_SHORT).show();
}
});
}
LiveData<List<Person>> getListLivedata() {
return mListLivedata;
}
public void removePerson(Person person) {
mList.remove(person);
mListLivedata.setValue(mList);
Log.d(TAG, "removePerson: called");
}
}
In Activity:
public class PersonRecyclerActivity extends AppCompatActivity implements PersonRecyclerAdapter.OnItemListener {
private PersonViewModel personRecyclerViewModel;
private PersonRecyclerAdapter personRecyclerAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.recycler_simple_layout);
RecyclerView personRecyclerView = findViewById(R.id.recycler_view);
personRecyclerAdapter =
new MudakRecyclerAdapter(new PersonRecyclerAdapter.PersonRecyclerDiff(), this);
personRecyclerView.setAdapter(personRecyclerAdapter);
personRecyclerView.setLayoutManager(new LinearLayoutManager(this));
personRecyclerViewModel = new ViewModelProvider(this,
ViewModelProvider.AndroidViewModelFactory.getInstance(this.getApplication()))
.get(PersonViewModel.class);
personRecyclerViewModel.getListLivedata().observe(this, personList -> personRecyclerAdapter.submitList(personList));
}
#Override
public void onItemClick(int position) {
Log.d(TAG, "onItemClick: called for " + personRecyclerAdapter.getPersonAt(position).getName() + ", at the position " + position);
personRecyclerViewModel.removePerson(personRecyclerAdapter.getPersonAt(position));
}
}
In Adapter:
public class PersonRecyclerAdapter extends ListAdapter<Person, PersonRecyclerAdapter.PersonRecyclerViewHolder> {
private OnItemListener mOnItemListener;
public interface OnItemListener {
void onItemClick(int position);
}
protected PersonRecyclerAdapter(#NonNull DiffUtil.ItemCallback<Person> diffCallback, OnItemListener onItemListener) {
super(diffCallback);
mOnItemListener = onItemListener;
}
public class PersonRecyclerViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
private final TextView mrName;
private final TextView mrStatus;
OnItemListener onItemListener;
public PersonRecyclerViewHolder(#NonNull View itemView, OnItemListener onItemListener) {
super(itemView);
mrName = itemView.findViewById(R.id.tv_rec_name);
mrStatus = itemView.findViewById(R.id.tv_rec_status);
this.onItemListener = onItemListener;
itemView.setOnClickListener(this);
}
#Override
public void onClick(View v) {
onItemListener.onItemClick(getAdapterPosition());
}
}
public Person getPersonAt(int position) {
return getItem(position);
}
#NonNull
#Override
public MudakRecyclerViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.recycler_simple_item, parent, false);
return new MudakRecyclerAdapter.MudakRecyclerViewHolder(view, mOnItemListener);
}
#Override
public void onBindViewHolder(#NonNull MudakRecyclerViewHolder holder, int position) {
Person currentPerson = getItem(position);
holder.mrName.setText(currentPerson.getName());
holder.mrStatus.setText(currentPerson.getStatus());
}
static class PersonRecyclerDiff extends DiffUtil.ItemCallback<Person> {
#Override
public boolean areItemsTheSame(#NonNull Person oldItem, #NonNull Person newItem) {
return oldItem == newItem;
}
#Override
public boolean areContentsTheSame(#NonNull Person oldItem, #NonNull Person newItem) {
return oldItem.getName().equals(newItem.getName());
}
}
}
ListAdapter won't do anything if you submit the same list instance to it that it was previously using, because it is designed to compare two different lists for differences between them. You should make the removePerson() method create a new List instance with the item removed and pass that to the LiveData.
Example:
public void removePerson(Person person) {
mList = new ArrayList(mList); // create a new list with same contents
mList.remove(person); // remove the item from the new list.
mListLivedata.setValue(mList);
Log.d(TAG, "removePerson: called");
}
i have recycler adapter like this
public class ShopUsersAdapter extends RecyclerView.Adapter<ShopUsersAdapter.MyViewHolder> implements View.OnClickListener {
private List<ShopUsersRecyclerModel> user_list;
private Context context;
private AdminCheckLocActivity activity;
private AdapterCallback mAdapterCallback;
public class MyViewHolder extends RecyclerView.ViewHolder {
public TextView user;
public TextView type;
private ImageButton btn_loc;
public MyViewHolder(View view) {
super(view);
user = (TextView) view.findViewById(R.id.txt_text);
type =(TextView)view.findViewById(R.id.txt_count);
btn_loc=(ImageButton)view.findViewById(R.id.btn_loc);
}
}
#Override
public ShopUsersAdapter.MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.map_pop_up_item, parent, false);
context=parent.getContext();
itemView.setOnClickListener(this);
return new ShopUsersAdapter.MyViewHolder(itemView);
}
#Override
public void onClick(View v) {
}
public ShopUsersAdapter(List<ShopUsersRecyclerModel> user_list, AdminCheckLocActivity activity) {
this.user_list = user_list;
this.activity=activity;
this.mAdapterCallback = ((AdapterCallback) context);
}
#Override
public void onBindViewHolder(final ShopUsersAdapter.MyViewHolder holder, int position) {
final ShopUsersRecyclerModel user = user_list.get(position);
holder.user.setText(user.getUser_info());
if (user.getUser_type()==3){holder.type.setText("TV Pro.");}
if (user.getUser_type()==4){holder.type.setText("B. Esya Pro.");}
holder.btn_loc.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mAdapterCallback.onMethodCallback(user.getUser_info());
}
});
}
#Override
public int getItemCount() {
return user_list.size();
}
public interface AdapterCallback {
void onMethodCallback(String userName);
}
}
but i get error
NullPointer at
mAdapterCallback.onMethodCallback(user.getUser_info());
my activity code;
#Override
public void onMethodCallback(final String userName) {
Toast.makeText(this,userName,Toast.LENGTH_LONG).show();
}
and i implement this like ;
implements ShopUsersAdapter.AdapterCallback
userInfo data is not null, i check this. but i need this callback all of my recyclers please help me.
I suggest changing your adapter's constructor to directly receive a callback:
public ShopUsersAdapter(List<ShopUsersRecyclerModel> user_list, AdminCheckLocActivity activity, AdapterCallback callback) {
this.user_list = user_list;
this.activity=activity;
this.mAdapterCallback = callback;
}
You can also make sure your callback isn't null when you're utilizing it:
holder.btn_loc.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// This should be optional for your case
// but it can be useful if you're ever in a situation
// where the callback isn't always available
if(mAdapterCallback != null){
mAdapterCallback.onMethodCallback(user.getUser_info());
}
}
});
And within your Activity, make sure to pass in the callback as an argument when you create your Adapter instance:
// Use 'this' since your Activity implements the callback
ShopUsersAdapter adapter = new ShopUsersAdapter(myList, myActivity, this);
I'm creating an Android app, the data that i'm sending through intent is being retrieved every time i click on the item.
I'm sending the retrieved data(which it's a subcollection that is being retrieved every time i click on item) through the intent,and all data receives in an arraylist, so the listener don't know if the same data existed in the arraylist,because the data are in the other activity.
when i click for the first time the data displayed normally in ItemMarkerActivity but when i go back and click again on the same item i see the data load again in the recycler view,and added to the previous same data, i'm using the technique of removing the data onStop but it didn't work perfectly,because i need to close all activities to see that the data removed, i tried to send the CollectionReference through intent but i couldn't do. so I need a way of removing the data immediately after closing the activity, and if anyone has another approach for solving this problem it would better.
Thanks in advance
adapter.setOnItemClickListener(new MarketAdapterRecyclerView.OnItemClickListener() {
#Override
public void onItemClick(DocumentSnapshot documentSnapshot, int position) {
CollectionReference path = documentSnapshot.getReference().collection("ShoppingItems");
listener = path.addSnapshotListener(new EventListener<QuerySnapshot>() {
#Override
public void onEvent(#Nullable QuerySnapshot queryDocumentSnapshots, #Nullable FirebaseFirestoreException e) {
if (e != null) {
return;
}
for (DocumentChange dc : queryDocumentSnapshots.getDocumentChanges()) {
if (dc.getType() == DocumentChange.Type.ADDED) {
item = dc.getDocument().toObject(Item.class);
itemList.add(item);
}
}
Intent intent = new Intent (shoppingActivity.this, ItemMarkerActivity.class);
Log.v(TAG,"###################################" + itemList.toString());
intent.putExtra("keyName", itemList);
startActivity(intent);
}
});
}
}
The Activity That Receives The data
The Manifest
public class ItemAdapter extends RecyclerView.Adapter<ItemAdapter.ViewHolder> implements Parcelable{
public ArrayList<Item> ItemList;
public Context mContext;
private onMallListener mOnMallListener;
private static final int NO_POSITION = -1;
public ItemAdapter(ArrayList<Item> ItemList, Context mContext, onMallListener mOnMallListener) {
this.ItemList = ItemList;
this.mContext = mContext;
this.mOnMallListener = mOnMallListener;
}
protected ItemAdapter(Parcel in) {
ItemList = in.createTypedArrayList(Item.CREATOR);
}
public static final Creator<ItemAdapter> CREATOR = new Creator<ItemAdapter>() {
#Override
public ItemAdapter createFromParcel(Parcel in) {
return new ItemAdapter(in);
}
#Override
public ItemAdapter[] newArray(int size) {
return new ItemAdapter[size];
}
};
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.activity_card_view_item, viewGroup, false);
ViewHolder viewHolder = new ViewHolder(view, mOnMallListener);
return viewHolder;
}
#Override
public void onBindViewHolder(#NonNull ViewHolder viewHolder, int i) {
Item item = ItemList.get(i);
viewHolder.itemType.setText(ItemList.get(i).getItemType());
Picasso.with(mContext)
.load(item.getImageUrl())
.fit()
.centerCrop().into(viewHolder.imageUrl);
}
#Override
public int getItemCount() {
return ItemList.size();
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeTypedList(ItemList);
}
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
View mView;
public TextView price;
public TextView description;
public TextView itemType;
public ImageView imageUrl;
onMallListener onMallListener;
public ViewHolder(#NonNull View itemView, onMallListener mOnMallListener) {
super(itemView);
mView = itemView;
itemType = (TextView) mView.findViewById(R.id.card_view_image_title);
imageUrl = (ImageView) mView.findViewById(R.id.card_view_image);
this.onMallListener = mOnMallListener;
mView.setOnClickListener(this);
}
#Override
public void onClick(View v) {
if(mOnMallListener != null){
int position = getAdapterPosition();
if (position != RecyclerView.NO_POSITION){
mOnMallListener.onMallClick(position);
}
}
}
}
public interface onMallListener{
void onMallClick(int position);
}
}
Save data using Room database in first activity and retrieve it in second.
In any place of your code (and in any activity's callback) you can clean db and all lists/recyclers which listen this data.
https://developer.android.com/training/data-storage/room
Hope it'll help
I have a RecyclerView Fragment to which I added a Filter Search in an edit text. It works, but when I click in a Card of the Recycler, it goes to the wrong detail. My best guess is that it's getting the wrong position from the getAdapterPosition since let's say I have this list {a,b,c,d,e,f,g,h}. when I filter and get two itmes left like {d,g}. If I click d it redirects to a, if I click g it redirects to b.
This is my RecyclerView
public class RecyclerProfile extends Fragment implements
Adapter.AdapterListener, com.example.cake.profiling.Adapter.SearchListener
{
private RecyclerListener recyclerListener;
private List<Profile> profiles = (new DAOProfile()).getProfile();
private Adapter recyclerAdapter= new Adapter(profiles, this);
public RecyclerProfile() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view= inflater.inflate(R.layout.fragment_recycler_profile,
container, false);
RecyclerView recyclerView = view.findViewById(R.id.recyclerProfile);
EditText editText = view.findViewById(R.id.editText);
editText.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int
count) {
}
#Override
public void afterTextChanged(Editable s) {
filter(s.toString());
}
});
recyclerView.setHasFixedSize(true);
RecyclerView.LayoutManager layoutManager = new
LinearLayoutManager(getContext(), LinearLayoutManager.VERTICAL, false);
recyclerView.setLayoutManager(layoutManager);
recyclerView.setAdapter(recyclerAdapter);
setHasOptionsMenu(true);
return view;
}
private void filter(String text){
ArrayList<Profile> filteredList = new ArrayList<>();
for (Profile profile: profiles){
if (profile.getName().toLowerCase().contains(text.toLowerCase())){
filteredList.add(profile);
}
}
recyclerAdapter.filterList(filteredList);
profiles = new ArrayList<>(filteredList);
}
#Override
public void listen(Profile profile, Integer position) {
recyclerListener.send(profile, position);
}
#Override
public void profileSelected(Profile profile) {
}
//INTERFACE
public interface RecyclerListener {
void send(Profile profile, Integer position);
}
//ON ATTACH
#Override
public void onAttach(Context context) {
super.onAttach(context);
this.recyclerListener = (RecyclerListener) context;
}
}
This is my Adapter
public class Adapter extends RecyclerView.Adapter<Adapter.ProfileViewHolder> {
private List<Profile> profiles;
private AdapterListener adapterListener;
//CONSTRUCTOR
public Adapter(List<Profile> profiles,AdapterListener adapterListener) {
this.profiles = profiles;
this.adapterListener = adapterListener;
}
#NonNull
#Override
public Adapter.ProfileViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
Context context = parent.getContext();
LayoutInflater layoutInflater = LayoutInflater.from(context);
View view = layoutInflater.inflate(R.layout.card_profile, parent, false);
ProfileViewHolder profileViewHolder = new ProfileViewHolder(view);
return profileViewHolder;
}
#Override
public void onBindViewHolder(#NonNull Adapter.ProfileViewHolder holder, int position) {
Profile profile = profiles.get(position);
holder.setter(profile);
}
#Override
public int getItemCount() {
return profiles.size();
}
public void filterList (ArrayList<Profile> filteredList){
profiles = filteredList;
notifyDataSetChanged();
}
//VIEWHOLDER
class ProfileViewHolder extends RecyclerView.ViewHolder{
private ImageView image;
private TextView name;
public ProfileViewHolder(View itemView) {
super(itemView);
name = itemView.findViewById(R.id.nameProfile);
image = itemView.findViewById(R.id.imageProfile);
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Profile profile = profile.get(getAdapterPosition());
adapterListener.receive(profile, getAdapterPosition());
}
});
}
public void setter (Profile profile){
name.setText(profile.getName());
image.setImageResource(profile.getImage());
}
}
public interface AdapterListener {
void receive(Profile profile, Integer position);
}
}
getAdapterPosition()
this will return the position of the item of current data set. This is what I'd do
class ProfileViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
private ImageView image;
private TextView name;
Profile mProfile;
public ProfileViewHolder(View itemView) {
super(itemView);
name = itemView.findViewById(R.id.nameProfile);
image = itemView.findViewById(R.id.imageProfile);
name.setOnClickListener(this);
image.setOnClickListener(this);
}
public void setter (Profile profile){
if(profile != null) {
mProfile = profile;
name.setText(mProfile.getName());
image.setImageResource(mProfile.getImage());
}
}
}
#Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.item_nameProfile:
case R.id.item_imageProfile:
for(int i = 0 ; i < profiles.size() ; i++) {
if(profiles.get(i).getName().equalsIgnoreCase(mProfile.getName()) {
adapterListener.receive(profiles.get(i), i);
break;
}
}
break;
}
}
public interface AdapterListener {
void receive(Profile profile, Integer position);
}
}
So I have a RecyclerView that displays a list of open games in a lobby. Each time the RecyclerView is updated, a button is added to the screen and I want to make it so that whenever a user clicks that button, it gets the ID of the section it is in.
So, games are added to database like this.
public void createLobbyGame () {
User current = new User(userName, 0, getSide());
String id = FCGames.push().getKey();
gameMaker = new GameMaker(current, null, 0, numGames, false, false);
FCGames.child(id)
.setValue(gameMaker).addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
if (task.isSuccessful()) {
Toast.makeText(FlipCoinLobby.this, "Game creation successful.", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(FlipCoinLobby.this, task.getException().getMessage(), Toast.LENGTH_SHORT).show();
}
}
});
}
And that creates a node in my database that looks like this:
So right now the ID is set at 0 but that's just because I forgot to make the ID equal to the name of the node.
So when something is added to my database, it is drawn in the app with a button. I am not sure how to make this button display the ID of the database node that it correlates to.
This is the clicklistener I have setup for the button in my ViewHolder class
Button joinButton = itemView.findViewById(R.id.joinRV);
joinButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(v.getContext(), gm.getId(), Toast.LENGTH_SHORT).show();
}
});
Just having the getId() method called does not work at all, and I wasn't expecting it to. But I am kind of lost on how to grab the correlating ID of the button whenever I click it
Adapter Class:
package com.example.brent.fifty_fifty;
import android.content.Intent;
import android.util.Log;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.content.Context;
import android.widget.Button;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import java.util.List;
import static android.view.LayoutInflater.*;
public class MyRecyclerViewAdapter extends RecyclerView.Adapter<MyRecyclerViewAdapter.ViewHolder> {
GameMaker gm;
private OnItemClickListener listener;
private List<String> mData;
private LayoutInflater mInflater;
private ItemClickListener mClickListener;
// data is passed into the constructor
MyRecyclerViewAdapter(Context context, List<String> data) {
this.mInflater = from(context);
this.mData = data;
}
public MyRecyclerViewAdapter(List<String> data, OnItemClickListener listener) {
this.mData = data;
this.listener = listener;
}
public interface OnItemClickListener {
void onItemClick(String id);
}
// inflates the row layout from xml when needed
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = mInflater.inflate(R.layout.recyclerview_row, parent, false);
return new ViewHolder(view);
}
// binds the data to the TextView in each row
#Override
public void onBindViewHolder(ViewHolder holder, final int position) {
String userName = mData.get(position);
//Double wager = Double.parseDouble(mData.get(position));
holder.userName.setText(userName);
//holder.wager.setText(wager.toString());
}
// total number of rows
#Override
public int getItemCount() {
return mData.size();
}
// stores and recycles views as they are scrolled off screen
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
TextView userName, wager;
RelativeLayout parentLayout;
ViewHolder(View itemView) {
super(itemView);
userName = itemView.findViewById(R.id.UsernameRV);
//wager = itemView.findViewById(R.id.wagerRV);
itemView.setOnClickListener(this);
Button joinButton = itemView.findViewById(R.id.joinRV);
joinButton.setOnClickListener(new View.OnClickListener() {
#Override public void onClick(View v) {
listener.onItemClick(data);
Toast.makeText(itemView.getContext(), "It worked", Toast.LENGTH_SHORT).show();
}
});
}
public void bind(final String data, final OnItemClickListener listener) {
}
public void onBindViewHolder(ViewHolder holder, int position) {
holder.bind(mData.get(position), listener);
}
#Override
public void onClick(View view) {
if (mClickListener != null) mClickListener.onItemClick(view, getAdapterPosition());
}
}
// convenience method for getting data at click position
String getItem(int id) {
return mData.get(id);
}
// allows clicks events to be caught
void setClickListener(ItemClickListener itemClickListener) {
this.mClickListener = itemClickListener;
}
// parent activity will implement this method to respond to click events
public interface ItemClickListener {
void onItemClick(View view, int position);
}
}
Please have a look at implementation of interface as Listener for recyclerview.
Please look this implementation. This is good one if you are using JAVA.
Here clicklistener is set on itemView you just need to replace itemView with your button.
public class MyRecyclerViewAdapter extends RecyclerView.Adapter< MyRecyclerViewAdapter.ViewHolder> {
public interface OnButtonClickListener {
void onButtonClick(String id);
}
private final List<String> items;
private final OnButtonClickListener listener;
public MyRecyclerViewAdapter(List<String> items, OnButtonClickListener listener) {
this.items = items;
this.listener = listener;
}
#Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout. recyclerview_row, parent, false);
return new ViewHolder(v);
}
#Override public void onBindViewHolder(ViewHolder holder, int position) {
holder.bind(items.get(position), listener);
}
#Override public int getItemCount() {
return items.size();
}
static class ViewHolder extends RecyclerView.ViewHolder {
private Button joinButton;
public ViewHolder(View itemView) {
super(itemView);
joinButton = (Button) itemView.findViewById(R.id. joinRV);
}
public void bind(final String id, final OnButtonClickListener listener) {
joinButton.setOnClickListener(new View.OnClickListener() {
#Override public void onClick(View v) {
listener.onItemClick(id);
}
});
}
}}
For your activity :
recycler.setAdapter(new MyRecyclerViewAdapter(items, new MyRecyclerViewAdapter.OnButtonClickListener() {
#Override public void onButtonClick(String id) {
Toast.makeText(this, id, Toast.LENGTH_LONG).show();
}}));