Updating list when it should be clearing it - android

I am currently working on inviting clients as guests to use my app. The invitation will be sent by email and their email address will appear in a RecyclerView until they have accepted it. I have made a clear button which will clear all emails within the ListView but for some reason once I selected the clear button, it clears the emails okay, but if I go to input a new email, then all emails previously in the list appear again.
Here is the code used.
public class InviteAdapter extends RecyclerView.Adapter<InviteAdapter.UserViewHolder> {
private static final String TAG = "Invite";
private List <Invites> invites;
public InviteAdapter(List<Invites> invites){
this.invites = invites;
}
#Override
public UserViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
final View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.row_email_invite, parent, false);
return new UserViewHolder(itemView);
}
#Override
public void onBindViewHolder(UserViewHolder holder, int position) {
Invites invite = invites.get(position);
holder.emailAddress.setText(invite.getEmail_address());
}
Override
public int getItemCount() {
return invites.size();
}
public class UserViewHolder extends RecyclerView.ViewHolder{
TextView emailAddress;
public UserViewHolder(View v) {
super(v);
emailAddress = (TextView) v.findViewById(R.id.row_email);
}
}
public void setInvites(List<Invites> invites){
this.invites = invites;
notifyDataSetChanged();
}
public void delete() {
Log.i(TAG, "Invites deleted");
invites.clear();
notifyDataSetChanged();
}
}
Also have put an image of how the client invite XML looks:

Only clearing your list will not help you get rid of the data as data of your list still exists in your original data source and when you will fetch data from that source again you will get the data you removed from the list. To get rid of that also remove data from your source also.
For eg. If you are creating your list from database then when you clear your list also delete all records from your database too using delete query.

Related

how to set background color of specific recyclerview card (via method) when clicked

i am trying to set the background of the card i have clicked on in my recycle view (to reflect the connection status of bluetooth device).
i have got all the steps needed for the itemclick (adapter, set onitemclickloistener, etc)
but cannot find a way to set the background? (i am only able to find the OBJECT of the card ive clicked not the VIEW to set it's background programatically)
my listener set from main activity:
//task to carry out when clicking a card item
recycAdapter.setOnItemClickListener(new CardAdapterDevice.OnItemClickListener() {
#Override
public void onItemClick(int position) {
//create pop up choice to connect to device
final Dialog dialog = new Dialog(BluetoothActions.this);
dialog.setContentView(R.layout.dialog_popup_connect_to_device);
dialog.setTitle("Connect to Device");
//assign dialog button etc (to call connection and set background method)
dialog.show();
}
});
but i want to call a method when the dialog button is pushed, to complete the connection and to set the background of the card.
i have spent a couple hours now researching online and experimenting, trying to figure this out and i just cant seem to find anything that works?
can anyone suggest how to do this?
edit: as suggested i have included my adapter below for reference.
public class CardAdapterDevice extends RecyclerView.Adapter<CardAdapterDevice.CardViewHolder> {
private static final String TAG = "CardAdapterDevice";
private ArrayList<DeviceCard> cardList;
private OnItemClickListener clickListener;
public CardAdapterDevice(ArrayList<DeviceCard> exampleList) {
cardList = exampleList;
}
//-listens for card click in recyclerView
public interface OnItemClickListener {
void onItemClick(int position);
}
public static class CardViewHolder extends RecyclerView.ViewHolder {
public ImageView deviceIcon;
public TextView deviceName;
public ImageView connect;
public CardViewHolder(#NonNull View itemView, final OnItemClickListener listener) {
super(itemView);
//references to views (pass values in onBindViewHolder)
deviceIcon = itemView.findViewById(R.id.image_carddevice_icon);
deviceName = itemView.findViewById(R.id.labeltext_carddevice_device_name);
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (listener != null) {
int position = getAdapterPosition();
if (position != RecyclerView.NO_POSITION) {
listener.onItemClick(position);
}
}
}
});
}
}
#NonNull
#Override
public CardAdapterDevice.CardViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.card_device, parent, false);
CardViewHolder cvh = new CardViewHolder(v, clickListener);
return cvh;
}
#Override
public void onBindViewHolder(#NonNull CardAdapterDevice.CardViewHolder holder, int position) {
//pass information to item currently looked at (position)
DeviceCard currentItem = cardList.get(position); //item at position
//get info (image (in holder) changed to image returned by ArrayList item (currentItem))
holder.deviceIcon.setImageResource(currentItem.getImageResource());
holder.deviceName.setText(currentItem.getDeviceName());
}
//-get number of items in list
#Override
public int getItemCount() {
return cardList
.size();
}
//-sets the desired click listener
public void setOnItemClickListener(OnItemClickListener listener) {
clickListener = listener;
}
}
also hoping to clarify things a little:
If i had manually created each card as a variable myself,
i would have direct access to it and could call methods like cardView.setBackgroundColor(etc)
however as they are created and added to the recyclerView autonomously, i dont have that variable and need to find a way to get the view reference from the click itself?
i have seen a lot of onBindAdapter examples, but unless i am missing something that doesnt help me get the card view at the moment of clicking it? (the adapter already has been bound at this point?)

Cannot retrive values from RecyclerView ,Recyclerview is populated via the SQlite DB

Basically we need to get the email from the recyclerview
We tried adding onClickListener on the TextView in the RecyclerView, but when there are more than one entries in the RecyclerView, we cannot get the value.
Is there any way that I can store the values in variables before populating the RecyclerView and passing the values to another Activity?
I tried getting the values from the TextView in the RecyclerView, but I always show the text on the first row
Intent doesn't work on the Adapter class because its not an activity
ArrayList<ModelClass> objModelClassArrayList;
public DatabaseRecyclerAdapter(ArrayList<ModelClass> objModelClassArrayList) {
this.objModelClassArrayList = objModelClassArrayList;
}
#NonNull
#Override
public DatabaseViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType)
{
View singleRow= LayoutInflater.from(parent.getContext()).inflate(R.layout.single_row,parent,false);
return new DatabaseViewHolder(singleRow);
}
#Override
public void onBindViewHolder(#NonNull DatabaseViewHolder holder, int position)
{
ModelClass objModelClass=objModelClassArrayList.get(position);
holder.userNameTV.setText(objModelClass.getName());
holder.userLocation.setText(objModelClass.getAddress());
String e1=objModelClass.getEmail();
holder.userEmail.setText(e1);
}
#Override
public int getItemCount() {
return objModelClassArrayList.size();
}
public static class DatabaseViewHolder extends RecyclerView.ViewHolder
{
TextView userNameTV,userLocation,userEmail;
public DatabaseViewHolder(#NonNull View itemView)
{
super(itemView);
userNameTV=itemView.findViewById(R.id.sr_userNameTV);
userLocation=itemView.findViewById(R.id.sr_location);
userEmail=itemView.findViewById(R.id.sr_email);
}
}
I want to extract the email from a single row and pass it to another page where I can pass it in the DB query to get the results from the database.
first, need to pass context in the constructor of the adapter which can help you to open another activity from the recyclerView adapter. Then Inside BindViewHolder, you can create clickListener like as below,
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String emailText=holder.userEmail.getText().trim();
Intent intent=new Intent(context,activity_name_you _want_ to_open);
intent.setExtra("emailName",emailText);
(name of activity contains RV)context.startActivity(intent)
//Note: here context needs to be typecasted to activity from which we want to open new activity.
}
});

How to update a recyclerview when new data sa been added in Sqlite database

It has been 2 days and I still can't figure out why can't my RecyclerView update immediately when I insert a data in sqlite database.
I been trying many methods such nofityDataSetChanged and swapAdapter but still my RecyclerView won't update, to update it I need to go to another activity before the data has been added into the RecyclerView.
So can anyone tell me where am i doing it wrong?
Heres my code for query
public void AddData(){
reg_pet.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
items=new ArrayList<>();
final int position=0;
adapter=new reg_Adapter(getActivity(),items);
boolean insterdata = myDB.insertDataToPetRegister(pet_name.getText().toString(),
spin_type.getSelectedItem().toString(), spin_breed.getSelectedItem().toString());
if (insterdata = true) {
adapter.notifyItemInserted(position);
adapter.notifyDataSetChanged();
pet_name.setText("");
Toast.makeText(getActivity(), "Your Pet has been Registered", Toast.LENGTH_LONG).show();
} else {
Toast.makeText(getActivity(), "Your Pet has not been Registerd", Toast.LENGTH_LONG).show();
}
}
});
}
My Adapter
public class reg_Adapter extends RecyclerView.Adapter<reg_ViewHolder> {
Activity activity;
Register_pet_database register_pet_database;
List<db_getItem> items;
db_getItem adapter;
DBAdapter dbAdapter;
public reg_Adapter(Activity activity, List<db_getItem> items) {
this.activity = activity;
this.items = items;
}
public reg_ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.reg_pet_recycler, parent, false);
return new reg_ViewHolder(view);
}
#Override
public void onBindViewHolder(reg_ViewHolder holder, final int position) {
holder.pet_name.setText(items.get(position).getPet_name());
holder.pet_type.setText(items.get(position).getPet_type());
holder.pet_breed.setText(items.get(position).getPet_breed());
holder.btndel.setTag(items.get(position).getPet_name());
holder.btndel.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
register_pet_database=new Register_pet_database(v.getContext());
Integer deleted =register_pet_database.deleteData(v.getTag().toString());
if (deleted > 0) {
items.remove(position);
notifyItemRemoved(position);
Toast.makeText(v.getContext(), "Pet Has been Remove", Toast.LENGTH_SHORT).show();
}
else{
Toast.makeText(v.getContext(), "Pet not Removed", Toast.LENGTH_SHORT).show();
}
}
});
}
#Override
public int getItemCount() {
return items.size();
}
}
My code in my fragment
public class pet_tab extends Fragment {
RecyclerView recyclerView;
Register_pet_database register_pet_database;
ArrayList<db_getItem> arrayList;
reg_Adapter reg_adapter;
db_getItem item;
ImageButton btndel;
TextView pet_name;
Cursor c;
public pet_tab() {
// 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_pet_tab, container, false);
recyclerView=(RecyclerView)view.findViewById(R.id.reg_pet_recycler);
btndel=(ImageButton)view.findViewById(R.id.btndel);
loadDb();
return view;
}
public void loadDb(){
register_pet_database=new Register_pet_database(getActivity());
DBAdapter db=new DBAdapter(getActivity());
db.openDB();
arrayList=new ArrayList<>();
c=register_pet_database.queryData("select * from Pet_Registered");
final int position=0;
try {
if(c!=null){
if(c.moveToFirst()){
do {
db_getItem item = new db_getItem();
item.setPet_name(c.getString(1));
item.setPet_type(c.getString(2));
item.setPet_breed(c.getString(3));
arrayList.add(item);
}while(c.moveToNext());
}
}
}catch (SQLException e){
e.printStackTrace();
}
reg_Adapter adapter=new reg_Adapter(getActivity(),arrayList);
recyclerView.setHasFixedSize(true);
LinearLayoutManager linearLayoutManager=new LinearLayoutManager(getContext());
recyclerView.setLayoutManager(linearLayoutManager);;
recyclerView.swapAdapter(adapter,false);
}
}
There is no need to do both notifyItemInserted and notifyDataSetChanged, only the former, as long as you have the position correct. But all that notify methods do is refresh the view. You have to also make sure the actual dataset has been updated.
Your code is card to read and follow because of the php code style so I haven't updated your original sample but basically:
In your Context:
Pet myPet = new Pet(...);
boolean isSuccessful = db.insert(myPet.getName(), ..., ...);
if (isSuccessful) {
mAdapter.add(myPet);
}
In the adapter:
public void add(Pet pet) {
mData.insert(0, pet);
notifyItemInserted(0);
}
As a general rule, restrict responsibility. If you find yourself having to manually track things like positions and calls to notify methods from outside of their owner (in this case adapter), then something is wrong.
Another option is to rely on a Loader to automatically monitor changes
Check out Android Jetpack's Room Persistence Library and Paging Library.
The Room Persistence Library
provides an abstraction layer over SQLite to allow for more robust database access while harnessing the full power of SQLite.
The library helps you create a cache of your app's data on a device that's running your app. This cache, which serves as your app's single source of truth, allows users to view a consistent copy of key information within your app, regardless of whether users have an internet connection.
The Paging Library
helps you load and display small chunks of data at a time. Loading partial data on demand reduces usage of network bandwidth and system resources.
The library supports the following data architectures:
Served only from a backend server.
Stored only in an on-device database.
A combination of the other sources, using the on-device database as a cache.

Add, remove and set CardView parameters programmatically

I am developing an application using the RecyclerView & CardView widgets to display the contacts added by the user.
The application allows the user to add a new card, set the appropriate information (name, phone number & image) to be displayed within the card while the card is being added to the list
The number set by the user can be dialed to initiate a phone
call when the card is clicked
I have created the UI, but I don't know how to go about the aforementioned operations using RecyclerView. How to solve this?
In order to bind the contacts to the CardView, you need to create an ArrayList<> to store the contacts in order to bind them. First, create a new class called Contact.java and add the following code:
public class Contact {
public String name;
public int number;
public Contact(String name, int number) {
this.name = name;
this.number = number;
}
}
This class will make sure that each instance within our ArrayList<> has the required values for the name of the contact and phone number. Now, we need to create an adapter class to manage the click events within our CardView and to bind the values from our ArrayList<> to the Views within the CardView. Create a new class called ContactsAdapter.java and add the following code:
public class ContactsAdapter extends RecyclerView.Adapter<ContactsAdapter.ContactsViewHolder> {
public List<Contact> list;
public ContactsAdapter(List<ContactsAdapter> list) {
this.list = list;
}
public static class ContactsViewHolder extends RecyclerView.ViewHolder {
// Update:
RelativeLayout item;
ImageView photo;
TextView name;
TextView number;
public ContactsViewHolder(View view) {
super(view);
// Update:
item = (RelativeLayout) view.findViewById(R.id.id_to_rel_layout); // If not, then add one by using android.id="#+id/whatever"
photo = (ImageView) view.findViewById(R.id.contact_photo);
name = (TextView) view.findViewById(R.id.contact_name);
number = (TextView) view.findViewById(R.id.contact_number);
}
}
#Override
public ContactsViewHolder onCreateViewHolder(ViewGroup viewGroup, int position) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.contacts_layout, viewGroup, false);
ContactsViewHolder viewHolder = new ContactsViewHolder(view);
return viewHolder;
}
#Override
public void onBindViewHolder(ContactsViewHolder viewHolder, final int position) {
// Update:
item.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// Do onclick stuff here such as dialing someone
}
});
viewHolder.photo.setImageURI(your_uri);
viewHolder.name.setText(list.get(position).name);
viewHolder.number.setText(list.get(position).number);
}
#Override
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
}
#Override
public int getItemCount() {
return list.size();
}
}
Now we have to attach the adapter to our RecyclerView. In the activity that contains the appropriate RecyclerView, add the following code:
private List<Contact> list = new ArrayList<>;
public void loadContacts() {
list.add(new Contacts(photoUri, name, number));
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.your_recyclerview);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
ContactsAdapter adapter = new ContactsAdapter(list);
recyclerView.setLayoutManager(linearLayoutManager);
recyclerView.setAdapter(adapter);
}
You have successfully binded your contacts with your CardView. Tell me how you go.

Large number of items in RecyclerView.Adapter - Memory Issue

Overview: I'm having a chat application. Till now, I was using CursorAdapter with a Listview to load my chat items in the list. But now, I'm planning to refactor the code to use RecyclerView with RecyclerView.Adapter and a "Load More" functionality like whatsapp.
Issue: Memory consumption. With CursorAdapter, items not in viewable area were getting Garbage Collected, but now since I'm using an ArrayList of my CustomModal, once you load all the items in the list (by clicking on the "Load More" button) I'm seeing high memory consumption in the memory logs (No Garbage Collection).
My guess is now, I'm loading all the items in an ArrayList and that is causing the issue. Is that it?
Is there a way to avoid the issue or optimize the problem?
EDIT:
Can't post the complete code here, but here is a snippet of the kind of Adapter that I've implemented:
public class MessageAdapter extends RecyclerView.Adapter<MessageAdapter.MyViewHolder> {
private ArrayList<MyModal> mMyModals;
public MessageAdapter(ArrayList<MyModal> mMyModals) {
this.mMyModals = mMyModals;
//... Some fields initialization here
}
public void changeList(ArrayList<MyModal> myModals, boolean isLoadMoreEnabled){
this.mMyModals = myModals;
//... Some fields initialization here
notifyDataSetChanged();
}
public void toggleLoadMore(boolean isLoadMoreEnabled){
if(isLoadMoreEnabled){
//..Checks if load more is already enabled or not
//..If not then enables it by adding an item at 0th poition of MyModal list
//..Then notifyDataSetChanged()
}else{
//..Checks if load more is already disabled or not
//..If not then disables it by removing an item at 0th poition of MyModal list
//..Then notifyDataSetChanged()
}
}
#Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
MyViewHolder messageViewHolder = null;
View itemLayoutView = null;
MyModal.MessageType messageType = MyModal.MessageType.getMessageTypeFromValue(viewType);
switch (messageType){
case MESSAGE_TYPE1:
itemLayoutView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.layout1, null);
messageViewHolder = new Type1ViewHolder(itemLayoutView);
break;
case MESSAGE_TYPE2:
itemLayoutView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.layout2, null);
messageViewHolder = new Type2ViewHolder(itemLayoutView);
break;
}
return messageViewHolder;
}
#Override
public void onBindViewHolder(MyViewHolder holder, int position) {
final MyModal myModal = mMyModals.get(position);
MyModal.MessageType messageType = myModal.getMessageType();
holder.initialize(myModal);
}
#Override
public int getItemCount() {
return (mMyModals != null)?mMyModals.size():0;
}
#Override
public int getItemViewType(int position) {
return mMyModals.get(position).getMessageType().getValue();
}
public abstract class MyViewHolder extends RecyclerView.ViewHolder {
public MyViewHolder(View itemLayoutView) {
super(itemLayoutView);
}
public abstract void initialize(MyModal myModal);
}
class Type1ViewHolder extends MyViewHolder {
//...Variables
public Type1ViewHolder(View itemLayoutView) {
super(itemLayoutView);
//...variables initialization here
}
#Override
public void initialize(MyModal myModal) {
//...Setting values in view using myModal
}
}
class Type2ViewHolder extends MyViewHolder {
//...Variables
public TextViewHolder(View itemLayoutView) {
super(itemLayoutView);
//...variables initialization here
}
#Override
public void initialize(MyModal myModal) {
//...Setting values in view using myModal
}
}
}
First of all :
public void changeList(ArrayList<MyModal> myModals, boolean isLoadMoreEnabled){
this.mMyModals = myModals;
//... Some fields initialization here
notifyDataSetChanged();
}
Here you are creating a new arraylist and assigning it to your mMyModals. This means there are 2 arraylists at this point and they take up twice the amount of space than required. GC doesnt work the way you expect it to. Since the arraylist is initialized in your activity it will persist as long as the arraylist persists and so will the initial arraylist.
Instead of creating a new arraylist in your activity and passing it to changeList. Just clear your old arraylist and pass that.And also in adapter changeList method you can do the below
public void changeList(ArrayList<MyModal> myModals, boolean isLoadMoreEnabled){
this.mMyModals.clear();
this.mMyModels.addAll(myModels);
//... Some fields initialization here
notifyDataSetChanged();
}
Please let me know if i am not clear. Also show your activity code if this does not work.
Instead of replacing the whole ArrayList and calling notifyDataSetChanged, try adding the items to the ArrayList and then call notifyItemRangeInserted(int positionStart, int itemCount), maybe that could work. Also, you dont have to replace the Adapter's ArrayList. Your Activity/Fragment probably has the same ArrayList, just editing this list in your Activity/Fragment and then calling notifyItemRangeInserted(int positionStart, int itemCount) should do the trick. Also, instead of retrieving all the messages, you could also try to only get the next X amount of messages, so you wont retrieve the messages you already retrieved before (if you didn't do that already).

Categories

Resources