listview doesn't refresh in android fragment with notifyDataSetChanged() - android-fragments

In my android application, I am unable to solve this issue since two months even after trying so many similar solutions found at Stack Overflow. I am populating the listview with an ArrayAdapter. When an item of listview is pressed for long, a dialog box appears and asks for confirmation. After confirmation, the selected item gets deleted from the array-list but in listview last item seems to be removed. When you close the fragment and come back to it, you see the correct items but not immediately after closing the alert dialog.
Can anyone please help? Thanks!
Adapter Class
public class DuasListAdapter extends ArrayAdapter<String>{
// class variables
private final Context context;// to save context
private final List<String> duas;// to save list of stores
LayoutInflater inflater;// so save layout inflater for the view
public DuasListAdapter(Context ctx, List<String> duasList) {
super(ctx, R.layout.adapter_list_duas, duasList);
context = ctx;// save context
duas = duasList;// save list of stores
inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);// save inflater layout
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
convertView=inflater.inflate(R.layout.adapter_list_duas, parent, false);
holder = new ViewHolder(convertView);
convertView.setTag(holder);
holder.duaIndex = position;
if(SingletonClass.duasAra.get(holder.duaIndex)!=""){
holder.tv_dua_arabic.setText(SingletonClass.duasAra.get(holder.duaIndex));
}
else{
if(keyUrdSel && !keyEngSel)
holder.tv_dua_arabic.setText(SingletonClass.duasTitUrd.get(holder.duaIndex));
else
holder.tv_dua_arabic.setText(SingletonClass.duasTitEng.get(holder.duaIndex));
}
holder.tv_dua_no.setText(" ("+ (holder.duaIndex+1));
return convertView;
}
static class ViewHolder {
int duaIndex;
LinearLayout background;// background to display color for read and unread messages
ImageView iv_duatype;
TextView tv_dua_arabic;// title of message
TextView tv_dua_ref;// message by and message created on
TextView tv_dua_no;
/**
* #description constructor to save context and list of stores views
* #param v to refer the view
* #return
*/
public ViewHolder(View v) {
// initialize their respective views
background = (LinearLayout) v.findViewById(R.id.ll_duas_list);
iv_duatype = (ImageView) v.findViewById(R.id.iv_duatype);
tv_dua_arabic = (TextView) v.findViewById(R.id.tv_dua_arabic);
tv_dua_no = (TextView) v.findViewById(R.id.dua_no);
}
}
Fragment Class
public class DuasListFragment extends Fragment {
Context context;
LinearLayout ll_back_duas_list_header;
TextView ll_back_duas_list_header_title;
ListView lvDuaas;
public DuasListAdapter duasAdapter;
int intListViewItemPosition = -1;
ArrayList<String> duas;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
context = inflater.getContext();
View view = inflater.inflate(R.layout.fragment_list_duas, container, false);
// TODO Auto-generated method stub
return view;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
ll_back_duas_list_header = (LinearLayout) getActivity().findViewById(R.id.ll_back_duas_list_header);
ll_back_duas_list_header_title = (TextView) getActivity().findViewById(R.id.ll_back_duas_list_header_title);
lvDuaas = (ListView) getActivity().findViewById(R.id.lv_duas);
String verses = new SharedPreferencesSupplication().read(SingletonClass.keyListOfVerses, "a1");
String[] versesList = verses.split(",");
duas = new ArrayList<String>();
for (int i = 0; i < versesList.length; i++) {
if (versesList[i].length() > 0)
duas.add(versesList[i]);
}
duasAdapter = new DuasListAdapter(context, duas);
lvDuaas.setAdapter(duasAdapter);
lvDuaas.setOnItemLongClickListener(new OnItemLongClickListener() {
#Override
public boolean onItemLongClick(final AdapterView<?> parent,
View view, final int position, long id) {
// TODO Auto-generated method stub
DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
switch (which) {
case DialogInterface.BUTTON_POSITIVE:
duasAdapter = (DuasListAdapter) parent.getAdapter();
duas.remove(position);
duasAdapter.notifyDataSetChanged();
((MainActivity) context).removeItemFromList(position);
break;
case DialogInterface.BUTTON_NEGATIVE:
// No button clicked
break;
}
}
};
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setMessage("Delete Dua from Favorites?").setPositiveButton("Yes", dialogClickListener).setNegativeButton("No", dialogClickListener).show();
return true;
}
});
super.onActivityCreated(savedInstanceState);
}
#Override
public void onResume() {
// TODO Auto-generated method stub
super.onResume();
// duasAdapter.clear();
//duasAdapter.addAll(duas);
duasAdapter.notifyDataSetChanged();
}
}

I also faced same problem with notifydatasetchanged method.
My problem was List that I had attached to adapter was getting updated but notifydatasetchanged method was not working.
If your ListView is designed to show moderate number of items in it,instead of calling notifydatasetchanged you can recreate your adapter.
It worked for me.I wasusing BaseAdapter.

Related

Firestore into recyclerview using FirebaseUI, no data displayed

I just got started with Firestore. So I tried fitting my data directly into a recyclerview, here's the code I used for reference.
Here's my UserRecyclerView Fragment :
public class UserRecyclerView extends Fragment{
private ArrayList<User> user;
RecyclerView mRecyclerView;
public UserRecyclerView(){}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.recycler_view,container,false);
mRecyclerView = (RecyclerView) view.findViewById(R.id.recycler_view);
FirestoreRecyclerAdapter adapter = getFirestoreRecyclerAdapter();
mRecyclerView.setAdapter(adapter);
mRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
return view;
}
#NonNull
private FirestoreRecyclerAdapter getFirestoreRecyclerAdapter() {
Query query = FirebaseFirestore.getInstance()
.collection("users");
FirestoreRecyclerOptions<User> options = new FirestoreRecyclerOptions.Builder<User>()
.setQuery(query, User.class).build();
return new FirestoreRecyclerAdapter<User, UserViewHolder>(options) {
#Override
protected void onBindViewHolder(UserViewHolder holder, int position, User model) {
Log.d("USER", "DATA: "+model.getName());
holder.BindView(position, model);
}
#Override
public UserViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.users_list, parent, false);
return new UserViewHolder(view);
}
};
}
private class UserViewHolder extends RecyclerView.ViewHolder{
TextView info;
UserViewHolder(View itemView) {
super(itemView);
info = (TextView) itemView.findViewById(R.id.info_text);
}
void BindView(int position, User model){
info.setText(model.getName()+" , "+model.getId());
}
}
There's nothing in my MainActivity except for starting the above fragment. I don't know what's wrong with my code. It shows no errors, when I run the app it doesn't crash. It just enters into the recyclerview layout, I checked it by placing a textview field for being sure(now removed). I want you to help me out with the code. It's probably because of the adapter? Help me figure out. I have also uploaded a snippet of my database.
Thanks in advance!
Make sure you call startListening on the adapter to initiate the connection to Cloud Firestore, otherwise no data will be loaded.
#Override
protected void onStart() {
super.onStart();
adapter.startListening();
}
#Override
protected void onStop() {
super.onStop();
adapter.stopListening();
}
See:
https://github.com/firebase/FirebaseUI-Android/blob/master/firestore/README.md#firestorerecycleradapter-lifecycle
Create recyclerview like below:
FirestoreRecyclerAdapter adapter = getFirestoreRecyclerAdapter();
//add below line
mRecyclerView.setHasFixedSize(true);
mRecyclerView.setAdapter(adapter);
mRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));

How to Delete a row in a RecyclerView Programmatically

Hey guys I have a recyclerview adapter that populates list of some data on a cardview. I created a three dot menu towards the lower right bottom of the card layout which would actually delete the card row from the recyclerview and also make delete of the item from the sqlite database that stores rows data. The issue is I couldn't figure out how to integrate the method that would implement the delete into the recyclerview ViewHolder. Here is the source code
public class BeneficiaryRecyclerAdapter extends RecyclerView.Adapter<BeneficiaryRecyclerAdapter.BeneficiaryViewHolder> {
private List<Beneficiary> listBeneficiary;
public ImageView overflow;
private Context mContext;
public BeneficiaryRecyclerAdapter(){
}
public BeneficiaryRecyclerAdapter(List<Beneficiary> listBeneficiary, Context mContext) {
this.listBeneficiary = listBeneficiary;
this.mContext = mContext;
}
#Override
public BeneficiaryViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
// inflating recycler item view
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_beneficiary_recycler, parent, false);
return new BeneficiaryViewHolder(itemView);
}
#Override
public void onBindViewHolder(final BeneficiaryViewHolder holder, int position) {
holder.textViewName.setText(listBeneficiary.get(position).getName());
holder.textViewEmail.setText(listBeneficiary.get(position).getEmail());
holder.textViewAddress.setText(listBeneficiary.get(position).getAddress());
holder.textViewCountry.setText(listBeneficiary.get(position).getAddress());
holder.overflow.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
showPopupMenu(holder.overflow);
}
});
}
/**
* Showing popup menu when tapping on 3 dots
*/
private void showPopupMenu(View view) {
// inflate menu
PopupMenu popup = new PopupMenu(this.mContext, view);
MenuInflater inflater = popup.getMenuInflater();
inflater.inflate(R.menu.beneficiary_m, popup.getMenu());
popup.setOnMenuItemClickListener(new MyMenuItemClickListener());
popup.show();
}
//NOTE: The method that would delete the item from the database and recyclerview but i need this method to be called from the onMenuItemClick of the case with id of case R.id.action_delete_beneficiary
public void deleteItem(final BeneficiaryViewHolder holder, int position) {
final DatabaseHelper2 db = new DatabaseHelper2(mContext);
db.open();
db.deleteContact(position);
db.close();
listBeneficiary.remove(position);
notifyItemRemoved(position);
notifyItemRangeChanged(position, listBeneficiary.size());
holder.itemView.setVisibility(View.GONE);
}
/**
* Click listener for popup menu items
*/
class MyMenuItemClickListener implements PopupMenu.OnMenuItemClickListener {
public MyMenuItemClickListener() {
}
#Override
public boolean onMenuItemClick(MenuItem menuItem) {
switch (menuItem.getItemId()) {
case R.id.action_delete_beneficiary:
Toast.makeText(mContext, "deleting beneficiary", Toast.LENGTH_SHORT).show();
return true;
case R.id.action_edit_beneficiary:
Toast.makeText(mContext, "editing beneficiary", Toast.LENGTH_SHORT).show();
return true;
default:
}
return false;
}
}
#Override
public int getItemCount() {
Log.v(BeneficiaryRecyclerAdapter.class.getSimpleName(),""+listBeneficiary.size());
return listBeneficiary.size();
}
/**
* ViewHolder class
*/
public class BeneficiaryViewHolder extends RecyclerView.ViewHolder {
public AppCompatTextView textViewName;
public AppCompatTextView textViewEmail;
public AppCompatTextView textViewAddress;
public AppCompatTextView textViewCountry;
public ImageView overflow;
public BeneficiaryViewHolder(View view) {
super(view);
textViewName = (AppCompatTextView) view.findViewById(R.id.textViewName);
textViewEmail = (AppCompatTextView) view.findViewById(R.id.textViewEmail);
textViewAddress = (AppCompatTextView) view.findViewById(R.id.textViewAddress);
textViewCountry = (AppCompatTextView) view.findViewById(R.id.textViewCountry);
overflow = (ImageView) view.findViewById(R.id.overflow);
}
}
}
I would be glad if anybody can help me with this.
Use this code to delete recyclerview items on same row click button.
holder.deleteImg.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if(list.size()!=0){
list.remove(position);
notifyItemRemoved(position);
notifyItemRangeChanged(position,list.size());
}
}
});

Click on tab itemes response from next tab items

I have a fragment with fragment pager adapter implements tablayout...each tab has a recycler view with some items(same recyler view adapter for all recylerviews)... by implementing OnItemClick listener for click on each item call an activity that implement a viewpager with fragmentStatePagerAdapter,to shows recycler view items...based on swipe of tablayout viewpager shows previous or next tab items...
data of each item stores in a arraylist.
when i'm clcking on one item in current tab it likes i'm clicking on next tab item in a same position
tablayput fragment:
viewPager = (ViewPager) view.findViewById(R.id.viewpager);
//setupViewPager(viewPager);
MyFragmentPagerAdapter adapter = new MyFragmentPagerAdapter(getChildFragmentManager(),getActivity());
adapter.addFrag(new OneFragment(), "title1");
adapter.addFrag(new TwoFragment(), "title2");
adapter.addFrag(new ThreeFragment(), "title3");
viewPager.setAdapter(adapter);
tabLayout = (TabLayout) view.findViewById(R.id.tabs);
tabLayout.setupWithViewPager(viewPager);
OneFragment and TwoFragment and ... are similar:
rv=(RecyclerView) view.findViewById(R.id.rv);
StaggeredGridLayoutManager sglm = new StaggeredGridLayoutManager(2,StaggeredGridLayoutManager.VERTICAL);
rv.setLayoutManager(sglm);
rv.setHasFixedSize(true);
persons = new ArrayList<>();
persons.add(new Person("name", "age","email","phone","resume", R.drawable.photo));
persons.add(new Person("name", "age","email","phone","resume", R.drawable.photo));
.
.
.
for (int j=0; j < persons.size() ;j++){
PersonItem personItem = new PersonItem();
personItem.setName(persons.get(j).name);
personItem.setAge(persons.get(j).age);
personItem.setEmail(persons.get(j).email);
personItem.setPhone(persons.get(j).phone);
personItem.setResume(persons.get(j).resume);
personItem.setImageId(persons.get(j).photoId);
personList1.addItem(personItem);
RVAdapter adapter = new RVAdapter(getContext(),persons);
adapter.setOnItemClickListener(new RVAdapter.OnItemClickListener() {
#Override
public void onItemClick(View view, int position) {
int pos = position;
Bundle bundle = new Bundle();
bundle.putSerializable("person", personList1);
Intent intent = new Intent(getContext(),
MyActivity.class);
intent.putExtras(bundle);
intent.putExtra("pos", pos);
startActivity(intent);
}
});
rv.setAdapter(adapter);
recycler view adapter:
public class RVAdapter extends RecyclerView.Adapter<RVAdapter.PersonViewHolder> {
private Context mContext;
private static OnItemClickListener listener;
//AdapterView.OnItemClickListener listener;
// Define the listener interface
public interface OnItemClickListener {
void onItemClick(View itemView, int position);
}
// Define the method that allows the parent activity or fragment to define the listener
public void setOnItemClickListener(OnItemClickListener listener) {
this.listener = listener;
}
public static class PersonViewHolder extends RecyclerView.ViewHolder {
CardView cv;
TextView personName;
TextView personAge;
ImageView personPhoto;
PersonViewHolder(final View itemView) {
super(itemView);
cv = (CardView)itemView.findViewById(R.id.cv);
personName = (TextView)itemView.findViewById(R.id.person_name);
personAge = (TextView)itemView.findViewById(R.id.person_age);
personPhoto = (ImageView)itemView.findViewById(R.id.person_photo);
// Setup the click listener
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// Triggers click upwards to the adapter on click
if (listener != null)
listener.onItemClick(itemView, getLayoutPosition());
}
});
}
}
List<Person> persons;
public RVAdapter(Context context , List<Person> persons){
this.persons = persons;
mContext = context;
}
#Override
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
}
#Override
public PersonViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item, viewGroup, false);
PersonViewHolder pvh = new PersonViewHolder(v);
return pvh;
}
#Override
public void onBindViewHolder(PersonViewHolder personViewHolder, int i) {
personViewHolder.personName.setText(persons.get(i).name);
personViewHolder.personAge.setText(persons.get(i).age);
personViewHolder.personPhoto.setImageResource(persons.get(i).photoId);
}
#Override
public int getItemCount() {
return persons.size();
}
}
for solving this problem i've made my RvAdapter class as a inner and private class in each tab fragments :
private static class RVAdapter extends RecyclerView.Adapter<RVAdapter.PersonViewHolder> {
//...and keep remaning

How do I Set TextFields from ParseQueryAdapter?

I have a ParseQueryAdapter that is populating a listview inside of a fragment:
QueryAdapter:
public class CustomAdapter extends ParseQueryAdapter implements AdapterView.OnItemClickListener {
public CustomAdapter(Context context) {
super(context, new ParseQueryAdapter.QueryFactory<ParseObject>() {
public ParseQuery create() {
ParseQuery query = new ParseQuery("Invoice");
query.whereEqualTo("CustomerID", ParseUser.getCurrentUser());
return query;
}
});
}
#Override
public View getItemView(ParseObject object, View cellview, ViewGroup parent) {
if (cellview == null) {
cellview = View.inflate(getContext(), R.layout.cell_layout, null);
}
TextView cellDate = (TextView) cellview.findViewById(R.id.dateTextView);
TextView invoice = (TextView) cellview.findViewById(R.id.invoiceNoTextView);
cellDate.setText(object.getString("Date"));
invoice.setText(object.getString("InvoiceNumber"));
return cellview;
}
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
}
}
Fragment Containing Listview:
public class InvoiceHistory extends Fragment {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final FrameLayout layout = (FrameLayout) inflater.inflate(R.layout.fragment_invoice_history, container, false);
ListView invoicesListView = (ListView) layout.findViewById(R.id.invoiceList);
ParseQueryAdapter adapter = new CustomAdapter(getActivity());
invoicesListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
Fragment detailView = new InvoicesFragment();
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.placeholder, detailView);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
}
});
invoicesListView.setAdapter(adapter);
adapter.loadObjects();
return layout;
}
}
This populates the ListView with the proper fields from the query, but when I click on one of the cells the fragment transaction takes place, but i can't figure out how to set textfields on that new fragment with ParseObject Keys from the Query.
This is the fragment that gets Presented that I want to set the Textfields in:
public class InvoicesFragment extends Fragment {
public InvoicesFragment() {
// Required empty public constructor
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
RelativeLayout layout = (RelativeLayout) inflater.inflate(R.layout.fragment_invoices, container, false);
return layout;
}
Android is super new to me so maybe this is a wrong way to approach it. But all i want to do here is have that detail fragment show up with populated TextViews. Im not really sure what I'm doing.
You could approach this by setting up a constructor in your DetailsFragment that you can pass in the textview info you want to show. In the example below, the data and invoice number are passed into the constructor. When the fragment is set up, this fragment is set up with the Parse object's data:
invoicesListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
ParseObject object = adapter.getItem(i);
Fragment detailView = DetailsFragment.newInstance(object.getString("Date"),
object.getString("InvoiceNumber"));
// ...
}
});
Here's a portion of the DetailsFragment showing the new constructor as well as how you could use them in setting up your view:
// InvoicesFragment.java
public static class InvoicesFragment extends Fragment {
// ...
public static InvoicesFragment newInstance(String invoiceDate, String invoiceNumber) {
InvoicesFragment f = new InvoicesFragment();
Bundle args = new Bundle();
args.putString("invoiceDate", InvoiceDate);
args.putString("invoiceNumber", InvoiceNumber);
f.setArguments(args);
return f;
}
public String getInvoiceDate() {
return getArguments().getString("invoiceDate", "");
}
public String getInvoiceNumber() {
return getArguments().getString("invoiceNumber", "");
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
RelativeLayout layout = (RelativeLayout) inflater.inflate(R.layout.fragment_invoices, container, false);
// Assign some TextView, ex: dateTextView
dateTextView.setText(getInvoiceDate());
return layout;
}
}

How to save fragment's state across fragment transactions

I am having some difficulties in wrapping my head around android fragments. I have an ActionBar with a Spinner and I create a new Fragment on every click on the spinner item. I have something like the following code:
public class MyActivity extends Activity
{
...
...
private void initActionBarSpinner()
{
getActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
OnNavigationListener callback = new OnNavigationListener()
{
#Override
public boolean onNavigationItemSelected(int itemPosition, long itemId)
{
if (itemPosition >= 0 && itemPosition < dataSet.size())
{
openTagFragment(itemPosition);
return true;
}
return false;
}
};
spinnerAdapter = new ArrayAdapter<String>(this, R.layout.action_bar_spinner, R.id.spinnertAdapterItem, tags);
getActionBar().setListNavigationCallbacks(spinnerAdapter, callback);
}
#Override
public void onCreate(Bundle savedInstanceState)
{
Log.d(TAG, TAG + " oncreate");
super.onCreate(savedInstanceState);
initActionBarSpinnerAdapter();
}
protected void openTagFragment(int itemPosition)
{
Log.d(TAG, "Replacing fragment");
String fragmentTag = dataSet.get(itemPosition);
MyFragment myFragment = new MyFragment();
myFragment.setData(dataSet.get(itemPosition));
FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.fragmentContainer, myFragment, fragmentTag);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
}
}
Problem I am facing: on orientation change, a new fragment gets created, which means it fetches the same data again from the server. I can see onSaveInstanceState() getting called on existing fragment when rotated but if I am going to do a new on Fragment again, when and how will I get a valid savedInstance bundle on fragment's onCreate()?

Resources