In my fragment I have an EditText which basically allows, when submitted, to query the values from Firebase Realtime Database and show them in a Recycler View.
Everything is fine when I search the first time but, when I search more than once, the previous results stay in the Recycler View.
I've tried to clear my lists but I got no success. I think I found the problem but I just can't solve it. I need to clear my bundle everytime I pass the retrieved values from the DB from one fragment to another. This is my fragment code (where I call the adaptor, not where I retrieve the DB values):
public class FilteredResultsFragment extends android.support.v4.app.Fragment {
ImageAdapter imageAdapter;
List<String> listOfImages = new ArrayList<>();
List<String> listOfNames = new ArrayList<>();
private RecyclerView recyclerView;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View viewFilteredResults = inflater.inflate(R.layout.fragment_filtered_results, container, false);
recyclerView = viewFilteredResults.findViewById(R.id.recyclerViewFiltered);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
imageAdapter = new ImageAdapter(listOfImages, listOfNames, getContext());
recyclerView.setAdapter(imageAdapter);
return viewFilteredResults;
}
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
if (getArguments() != null) {
removeAll();
listOfImages.addAll(getArguments().getStringArrayList("listOfImages"));
listOfNames.addAll(getArguments().getStringArrayList("listOfNames"));
Log.d("IMAGENS", "img "+listOfImages);
Log.d("Nomes","nome "+listOfNames);
imageAdapter.notifyDataSetChanged();
}
}
public void removeAll(){
listOfImages.clear();
listOfNames.clear();
imageAdapter.notifyDataSetChanged();
}
}
As you can see, I've created a function that should allow me to clear the data... And this is my Adaptor:
public class ImageAdapter extends RecyclerView.Adapter<ImageAdapter.ImageViewHolder> {
List<String> listaPaisesFiltrados;
List<String> listaNomesFiltrados;
android.content.Context context;
ImageAdapter(List<String> listaPaisesFiltrados, List<String> listaNomesFiltrados, android.content.Context c) {
this.listaPaisesFiltrados = listaPaisesFiltrados;
this.listaNomesFiltrados = listaNomesFiltrados;
this.context = c;
}
#NonNull
#Override
public ImageViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.list_item_view, parent, false);
return new ImageViewHolder(itemView);
}
#Override
public void onBindViewHolder(#NonNull ImageViewHolder holder, int position) {
String imageUrl = listaPaisesFiltrados.get(holder.getAdapterPosition());
String nomePais = listaNomesFiltrados.get(holder.getAdapterPosition());
android.content.Context context1 = context.getApplicationContext();
Log.d("ADAPTER", "imageUrl : " + imageUrl + " position : " + holder.getAdapterPosition());
Typeface tpPaisNome = Typeface.createFromAsset(holder.itemView.getContext().getAssets(),"fonts/RobotoSlab-Bold.ttf"); // Define a Typeface
holder.textView.setTypeface(tpPaisNome);
holder.textView.setText(nomePais);
Glide.with(holder.imageView.getContext()).load(imageUrl).centerCrop().into(holder.imageView);
}
#Override
public int getItemCount() {
Log.d("ADAPTER", "SIZE : " + listaPaisesFiltrados.size());
return listaPaisesFiltrados.size();
}
public class ImageViewHolder extends RecyclerView.ViewHolder {
ImageView imageView;
TextView textView;
public ImageViewHolder(View itemView) {
super(itemView);
imageView = itemView.findViewById(R.id.imgPaisFiltrado);
textView = itemView.findViewById(R.id.txtNomePaisFiltrado);
}
}
}
EDIT
In my ExploreFragment (where I have the query and the EditText) I send two ArrayLists via Bundle to the fragment that opens the adaptor. I need to clear my Bundler but, when I use blunder.clear() than no value is passed to the other fragment and no image is displayed. This is my code
srcView.setOnEditorActionListener(new EditText.OnEditorActionListener() {
#Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
boolean handled = false;
if (actionId == EditorInfo.IME_ACTION_DONE) {
txtRecomendado.setVisibility(View.INVISIBLE);
scrollView.setVisibility(View.INVISIBLE);
final Bundle bundler = new Bundle();
bundler.clear();
FirebaseDatabase mDatabase = FirebaseDatabase.getInstance();
DatabaseReference paisNomeContinentes = mDatabase.getReference().child("paises");
Query queries = paisNomeContinentes.orderByChild("Nome").startAt(String.valueOf(srcView.getText())).endAt(String.valueOf(srcView.getText())+"\uf8ff");
Log.d("AQUIPUTO","teste "+String.valueOf(srcView.getText()));
queries.addListenerForSingleValueEvent(new com.google.firebase.database.ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
List<String> imagemPaisList = new ArrayList<>();
List<String> nomePaisList = new ArrayList<>();
for (DataSnapshot ds : dataSnapshot.getChildren()) {
String imagemPais = ds.child("Imagem").getValue(String.class);
String nomePais = ds.child("Nome").getValue(String.class);
imagemPaisList.add(imagemPais);
nomePaisList.add(nomePais);
}
int urlCount = imagemPaisList.size();
// int randomImage = new Random().nextInt(urlCount);
for (int i = 0; i < nomePaisList.size(); i++) {
//Integer randomVariavel = new Random().nextInt(urlCount);
randomImagemPaisList.add(imagemPaisList.get(i));
randomNomePaisList.add(nomePaisList.get(i));
}
getFragmentManager().popBackStack();
FragmentTransaction transaction = getActivity().getSupportFragmentManager().beginTransaction();
transaction.setCustomAnimations(R.anim.slide_up_info,R.anim.slide_down_info);
FilteredResultsFragment fragment = new FilteredResultsFragment();
bundler.putStringArrayList("listOfImages", (ArrayList<String>) randomImagemPaisList);
bundler.putStringArrayList("listOfNames", (ArrayList<String>) randomNomePaisList);
fragment.setArguments(bundler);
transaction.replace(R.id.fragContainerExploreFilteredResults, fragment);
transaction.addToBackStack(null);
transaction.commit();
}
How can I clear my bundle? I've tried to clear it after I use:
bundler.putStringArrayList("listOfImages", (ArrayList<String>) randomImagemPaisList);
bundler.putStringArrayList("listOfNames", (ArrayList<String>) randomNomePaisList);
fragment.setArguments(bundler);
And if I do this no value is passed from one fragment to another. I've tried to clear it when the fragment.trasaction is complete but I still get the same 'error'...
might be possible that updated data is not notify with recyclerview, you can create doRefresh method in adapter class and then update the list and notify it, please see the below code
Fragment Code
public class FilteredResultsFragment extends android.support.v4.app.Fragment {
ImageAdapter imageAdapter;
List<String> listOfImages = new ArrayList<>();
List<String> listOfNames = new ArrayList<>();
private RecyclerView recyclerView;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View viewFilteredResults = inflater.inflate(R.layout.fragment_filtered_results, container, false);
setAdapter();
return viewFilteredResults;
}
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
if (getArguments() != null) {
listOfImages.clear();
listOfNames.clear();
listOfImages.addAll(getArguments().getStringArrayList("listOfImages"));
listOfNames.addAll(getArguments().getStringArrayList("listOfNames"));
Log.d("IMAGENS", "img "+listOfImages);
Log.d("Nomes","nome "+listOfNames);
setAdapter();
}
}
private void setAdapter() {
if (listOfImages != null && listOfNames != null) {
if (imageAdapter == null) {
imageAdapter = new ImageAdapter(getContext());
}
imageAdapter.doRefresh(listOfImages, listOfNames);
if (recyclerView.getAdapter() == null) {
recyclerView = viewFilteredResults.findViewById(R.id.recyclerViewFiltered);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
recyclerView.setAdapter(imageAdapter);
}
}
}
}
and Adapter class as below
public class ImageAdapter extends RecyclerView.Adapter<ImageAdapter.ImageViewHolder> {
List<String> listaPaisesFiltrados;
List<String> listaNomesFiltrados;
android.content.Context context;
ImageAdapter(android.content.Context c) {
this.context = c;
}
public void doRefresh(List<String> listaPaisesFiltrados, List<String> listaNomesFiltrados) {
this.listaPaisesFiltrados = listaPaisesFiltrados;
this.listaNomesFiltrados = listaNomesFiltrados;
notifyDataSetChanged();
}
#NonNull
#Override
public ImageViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.list_item_view, parent, false);
return new ImageViewHolder(itemView);
}
#Override
public void onBindViewHolder(#NonNull ImageViewHolder holder, int position) {
String imageUrl = listaPaisesFiltrados.get(holder.getAdapterPosition());
String nomePais = listaNomesFiltrados.get(holder.getAdapterPosition());
android.content.Context context1 = context.getApplicationContext();
Log.d("ADAPTER", "imageUrl : " + imageUrl + " position : " + holder.getAdapterPosition());
Typeface tpPaisNome = Typeface.createFromAsset(holder.itemView.getContext().getAssets(),"fonts/RobotoSlab-Bold.ttf"); // Define a Typeface
holder.textView.setTypeface(tpPaisNome);
holder.textView.setText(nomePais);
Glide.with(holder.imageView.getContext()).load(imageUrl).centerCrop().into(holder.imageView);
}
#Override
public int getItemCount() {
Log.d("ADAPTER", "SIZE : " + listaPaisesFiltrados.size());
return listaPaisesFiltrados.size();
}
public class ImageViewHolder extends RecyclerView.ViewHolder {
ImageView imageView;
TextView textView;
public ImageViewHolder(View itemView) {
super(itemView);
imageView = itemView.findViewById(R.id.imgPaisFiltrado);
textView = itemView.findViewById(R.id.txtNomePaisFiltrado);
}
}
}
you can try to create new adapter and set to your recycler.
listOfImages.clear();
listOfNames.clear();
/*
Adding your new searching data here
*/
recyclerView.setAdapter(new ImageAdapter(listOfImages, listOfNames, getContext());
recyclerView.invalidate();
When you try to clear by calling removeAll, you only clear the list objects from the Fragment class. What you need is to clear the list objects in your RecyclerView.Adapter class.
Try this:
Move your removeAll to your adapter. That way, you clear the list objects in that adapter.
In your Fragment, just call your removeAll from your Adapter.
Sample Code:
Fragment.java
//No more removeAll() here
public void callAdapterToRemove() {
imageAdapter.removeAll();
}
ImageAdapter.java
//Now it's here
public void removeAll() {
listaPaisesFiltrados.clear();
listaNomesFiltrados.clear();
notifyDataSetChanged();
}
Related
I'm new to Android and I'm still learning. Could You please help me?
I'm trying to set clickable List in ListFragment with custom ArrayAdapter. It works with normal ArrayAdapter but not with custom. Here is my CustomArrayAdapter class:
public class CustomListAdapter<T> extends ArrayAdapter<T> {
private List<Integer> imageIds;
private List<String> titleList;
private List<String> descriptionList;
private List<String> createdList;
private final Context activityContext;
private static final String DATE_FORMAT_8 = "yyyy-MM-dd HH:mm:ss";
SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT_8);
public CustomListAdapter(#NonNull Context context, #NonNull List<T> objects) {
super(context, R.layout.row_layout, objects);
this.activityContext = context;
imageIds = new ArrayList<>();
titleList = new ArrayList<>();
descriptionList = new ArrayList<>();
createdList = new ArrayList<>();
}
#NonNull
#Override
public View getView(int position, #Nullable View convertView, #NonNull ViewGroup parent) {
View rowView = convertView;
if (convertView == null)
rowView = LayoutInflater.from(getContext()).inflate(R.layout.row_layout, parent, false);
ImageView ivNote = rowView.findViewById(R.id.ivNote);
TextView tvTitle = rowView.findViewById(R.id.tvTitle);
TextView tvDescription = rowView.findViewById(R.id.tvDescription);
TextView tvCreated = rowView.findViewById(R.id.tvCreated);
ivNote.setImageResource(imageIds.get(position));
tvTitle.setText(titleList.get(position));
tvDescription.setText(descriptionList.get(position));
tvCreated.setText(createdList.get(position));
return rowView;
}
list setters etc
And here is my ListFragment class:
private List<TaskDTO> taskDTOS;
TasksItemSelected tasksItemSelected;
public interface TasksItemSelected {
void onTaskItemSelected(int index);
}
public TasksFragment() {
// Required empty public constructor
}
#Override
public void onAttach(#NonNull Context context) {
super.onAttach(context);
tasksItemSelected = (TasksItemSelected) context;
}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
if (getArguments() != null) {
taskDTOS = (List<TaskDTO>) getArguments().getSerializable("tasksList");
}
View view = inflater.inflate(R.layout.fragment_tasks, container, false);
return view;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (taskDTOS != null && taskDTOS.size() > 0) {
setListAdapter(getCustomListAdapter(getContext(),taskDTOS));
}
}
private CustomListAdapter getCustomListAdapter(Context activity, List<TaskDTO> taskDTOS) {
CustomListAdapter customListAdapter = new CustomListAdapter(activity, taskDTOS);
customListAdapter.setImageIds(taskDTOS);
customListAdapter.setTitleList(taskDTOS);
customListAdapter.setDescriptionList(taskDTOS);
customListAdapter.setCreatedList(taskDTOS);
return customListAdapter;
}
#Override
public void onListItemClick(#NonNull ListView l, #NonNull View v, int position, long id) {
super.onListItemClick(l, v, position, id);
tasksItemSelected.onTaskItemSelected(position);
}
}
When I set new ArrayAdapter, list items become clickable and new activity runs, but nothing happens when tapping list items in the above case.
How to shuffle items from Firebase Database?
Let's say my DB contains 4 items [1,2,3,4]. In my RecyclerView, I want to display shuffled, something like [2,3,1,4]. How can I do that?
This is the code in my Fragment. So it works retrieving data but I need to shuffle it before displaying.
RecyclerView mRecyclerView;
FirebaseDatabase mFirebaseDatabase;
DatabaseReference mRef;
#Override
public View onCreateView(#NotNull LayoutInflater inflater, ViewGroup viewGroup, Bundle savedInstanceState) {
if (rootView == null) {
rootView = inflater.inflate(R.layout.fragment_store, viewGroup, false);
setHasOptionsMenu(false);
mRecyclerView = rootView.findViewById(R.id.recycler_view);
mRecyclerView.setHasFixedSize(true);
mRecyclerView.setLayoutManager(new GridLayoutManager(getActivity(), 3));
mFirebaseDatabase = FirebaseDatabase.getInstance();
mRef = mFirebaseDatabase.getReference("Store");
}
return rootView;
}
For retrieving data from Firebase Database I am using the following code:
#Override
public void onStart() {
super.onStart();
FirebaseRecyclerOptions<Store2> options =
new FirebaseRecyclerOptions.Builder<Store2>()
.setQuery(mRef, Store2.class)
.build();
FirebaseRecyclerAdapter<Store2, StoreHolder> firebaseRecyclerAdapter =
new FirebaseRecyclerAdapter<Store2, StoreHolder>(options) {
#NonNull
#Override
public StoreHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.store_gridview, parent, false);
return new StoreHolder(view);
}
#Override
protected void onBindViewHolder(#NonNull final StoreHolder holder, int position, #NonNull final Store2 model) {
holder.setDetails(getContext(), model.getImage(), model.getName(), model.getPrice());
}
};
firebaseRecyclerAdapter.startListening();
mRecyclerView.setAdapter(firebaseRecyclerAdapter);
}
There are no option available for shuffling data in firebase.
But you can do it locally .
Pass your list in this.
Collections.shuffle(arrayList);
When you are using the following reference:
mRef = mFirebaseDatabase.getReference("Store");
It means that you are trying to display all children that exist under Store node. If under this node you store children that are added using the push() method, the default ordering is chronological, since every pushed id contains a time component. If you want to change this behaviour, you need to get all the items and shuffle them manually.
As #DougStevenson mentioned in his comment, you have to use the same reference, get all items, add them to a list, shuffle them and then use a ListAdapter for that.
Please also note, that when you pass a DocumentReference or a Query object to the setQuery() method, where is no further mechanism that can help you shuffle your items. Unfortunately, you'll lose the benefits of the Firebase-UI library but you'll get your work done.
As #DougStevenson and #frankenstein mentioned, I added all items into a list, then I was able the shuffle it using Collections.shuffle(arrayList);
I couldn't do it using FirebaseRecyclerAdapter, and I needed a custom Adapter for my RecyclerView.ViewHolder.
What is the downside of not using FirebaseRecyclerAdapter? Or is it any? Maybe someone with experience can answer this question.
My fragment:
private View rootView;
private RecyclerView recyclerView;
private StoreAdapter storeAdapter;
private ArrayList<Store> arrayStore;
#Override
public View onCreateView(#NotNull LayoutInflater inflater, ViewGroup viewGroup, Bundle savedInstanceState) {
if (rootView == null) {
rootView = inflater.inflate(R.layout.fragment_store, viewGroup, false);
setHasOptionsMenu(false);
recyclerView = rootView.findViewById(R.id.recycler_view);
recyclerView.setLayoutManager(new GridLayoutManager(getActivity(), 3));
DatabaseReference reference = FirebaseDatabase.getInstance().getReference().child("Data");
reference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
arrayStore = new ArrayList<>();
for(DataSnapshot snapshot: dataSnapshot.getChildren()) {
Store store = snapshot.getValue(Store.class);
arrayStore.add(store);
}
storeAdapter = new StoreAdapter(getActivity() ,arrayStore);
recyclerView.setAdapter(storeAdapter);
Collections.shuffle(arrayStore, new Random(3));
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
return rootView;
}
My adapter:
private final Context context;
private ArrayList<Store> arrayStore;
public StoreAdapter(Context context, ArrayList<Store> arrayStore) {
this.context = context;
this.arrayStore = arrayStore;
}
#NonNull
#Override
public StoreAdapter.GameViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
return new StoreAdapter.GameViewHolder(LayoutInflater.from(context).inflate(
R.layout.store_gridview, viewGroup, false));
}
#Override
public void onBindViewHolder(#NonNull final StoreAdapter.GameViewHolder viewHolder, final int position) {
setColor(viewHolder);
viewHolder.itemName.setText(arrayStore.get(position).getName());
Glide.with(viewHolder.view.getContext())
.load(arrayStore.get(position).getImage())
.thumbnail(0.75f)
.into(viewHolder.itemImage);
viewHolder.view.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
sendData(viewHolder, position);
}
});
}
#Override
public int getItemCount() {
return arrayStore.size();
}
class GameViewHolder extends RecyclerView.ViewHolder {
private final View view;
private final ImageView itemImage, itemRarity;
private final TextView itemName;
GameViewHolder(#NonNull View itemView) {
super(itemView);
this.view = itemView;
itemRarity = view.findViewById(R.id.itemRarity);
itemImage = view.findViewById(R.id.itemImage);
itemName = view.findViewById(R.id.itemName);
}
}
private void sendData(#NonNull final StoreAdapter.GameViewHolder viewHolder, int position) {
if (viewHolder.getAdapterPosition() != RecyclerView.NO_POSITION) {
Intent intent = new Intent(context, DetailsActivity.class);
intent.putExtra("name", arrayStore.get(position).getName());
intent.putExtra("rarity", arrayStore.get(position).getRarity());
intent.putExtra("item", arrayStore.get(position).getImage());
intent.putExtra("price", arrayStore.get(position).getPrice());
intent.putExtra("key", "Store");
context.startActivity(intent);
}
}
private void setColor(#NonNull final StoreAdapter.GameViewHolder viewHolder) {
String rarity = arrayStore.get(viewHolder.getAdapterPosition()).getRarity();
if (rarity.contains("legendary")) {
viewHolder.itemRarity.setImageResource(R.drawable.color_legendary_gradient);
} else if (rarity.contains("classic")) {
viewHolder.itemRarity.setImageResource(R.drawable.color_classic_gradient);
} else if (rarity.contains("epic")) {
viewHolder.itemRarity.setImageResource(R.drawable.color_epic_gradient);
} else if (rarity.contains("elite")) {
viewHolder.itemRarity.setImageResource(R.drawable.color_elite_gradient);
} else if (rarity.contains("rare")) {
viewHolder.itemRarity.setImageResource(R.drawable.color_rare_gradient);
} else if (rarity.contains("special")) {
viewHolder.itemRarity.setImageResource(R.drawable.color_special_gradient);
}
}
My bean class:
private String image, name, price, rarity;
public Store() {
}
public String getImage() {
return image;
}
public String getName() {
return name;
}
public String getPrice() {
return price;
}
public String getRarity() {
return rarity;
}
I'm trying to populate a RecyclerView using data stored in a ArrayList. When the RecyclerView loads each piece of data is repeted 4x in each row.
I've tried a whole variety of solutions I've found, but none seem to resolve the issue.
After some debugging the data in 'mData', appears to be correct, so that would lead me to believe that this issue is 'onBindViewHolder'?
Adapter
public class EventsRecyclerViewAdapter extends RecyclerView.Adapter<EventsRecyclerViewAdapter.ViewHolder> {
private List<String> mData;
private LayoutInflater mInflater;
private ItemClickListener mClickListener;
// data is passed into the constructor
public EventsRecyclerViewAdapter(List<String> data) {
this.mInflater = LayoutInflater.from(MainActivity.mainActivity);
this.mData = data;
}
// 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, int position) {
holder.keyID.setText(mData.get(position));
holder.lockID.setText(mData.get(position));
holder.eventTime.setText(mData.get(position));
holder.eventType.setText(mData.get(position));
}
// 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 keyID;
TextView lockID;
TextView eventTime;
TextView eventType;
ViewHolder(View itemView) {
super(itemView);
keyID = itemView.findViewById(R.id.keyIDTV);
lockID = itemView.findViewById(R.id.lockIDTV);
eventTime = itemView.findViewById(R.id.eventDateTV);
eventType = itemView.findViewById(R.id.eventTypeTV);
itemView.setOnClickListener(this);
}
#Override
public void onClick(View view) {
if (mClickListener != null) mClickListener.onItemClick(view, getAdapterPosition());
}
}
// parent activity will implement this method to respond to click events
public interface ItemClickListener {
void onItemClick(View view, int position);
}
Fragment
public class KeyEvents extends Fragment {
public static KeyInfo newInstance() {
KeyInfo fragment = new KeyInfo();
return fragment;
}
EventsRecyclerViewAdapter adapter;
ArrayList<String> eventsList;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EventsOperationHandler ev = new EventsOperationHandler();
eventsList = new ArrayList<>();
for (int i=0; i<ev.getEvents().size(); i++) {
eventsList.add(ev.getEvents().get(i).get(0));
eventsList.add(ev.getEvents().get(i).get(1));
eventsList.add(ev.getEvents().get(i).get(2));
eventsList.add(ev.getEvents().get(i).get(3));
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_event_info, container, false);
RecyclerView recyclerView = view.findViewById(R.id.rvEvents);
recyclerView.setLayoutManager(new LinearLayoutManager(MainActivity.mainActivity));
adapter = new EventsRecyclerViewAdapter(eventsList);
recyclerView.setAdapter(adapter);
return view;
}
}
In your 'onBindViewHolder' you are setting same data in every textView of yours
// binds the data to the TextView in each row
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.keyID.setText(mData.get(position));
holder.lockID.setText(mData.get(position));
holder.eventTime.setText(mData.get(position));
holder.eventType.setText(mData.get(position));
}
Here you are setting mData.get(position) to every textView in the holder
A solution would be creating a Pojo class for your recyclerView
Create Pojo Event class
class Event{
public String keyID;
public String lockID;
public String eventTime;
public String eventType;
}
Create a list of 'Event' in your fragment
public class KeyEvents extends Fragment {
public static KeyInfo newInstance() {
KeyInfo fragment = new KeyInfo();
return fragment;
}
EventsRecyclerViewAdapter adapter;
ArrayList<Event> eventsList;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EventsOperationHandler ev = new EventsOperationHandler();
eventsList = new ArrayList<>();
for (int i=0; i<ev.getEvents().size(); i++) {
Event event = new Event();
event.keyID = ev.getEvents().get(i).get(0);
event.lockID = ev.getEvents().get(i).get(1);
event.eventTime = ev.getEvents().get(i).get(2);
event.eventType = ev.getEvents().get(i).get(3);
eventsList.add(event);
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_event_info, container, false);
RecyclerView recyclerView = view.findViewById(R.id.rvEvents);
recyclerView.setLayoutManager(new LinearLayoutManager(MainActivity.mainActivity));
adapter = new EventsRecyclerViewAdapter(eventsList);
recyclerView.setAdapter(adapter);
return view;
}
}
Modify your recyclerView to handle to 'Event' class
public class EventsRecyclerViewAdapter extends RecyclerView.Adapter<EventsRecyclerViewAdapter.ViewHolder> {
private List<Event> mData;
private LayoutInflater mInflater;
private ItemClickListener mClickListener;
// data is passed into the constructor
public EventsRecyclerViewAdapter(List<Event> data) {
this.mInflater = LayoutInflater.from(MainActivity.mainActivity);
this.mData = data;
}
// 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, int position) {
Event event = mData.get(position);
holder.keyID.setText(event.keyID);
holder.lockID.setText(event.lockId);
holder.eventTime.setText(event.eventTime);
holder.eventType.setText(event.eventType);
}
// 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 keyID;
TextView lockID;
TextView eventTime;
TextView eventType;
ViewHolder(View itemView) {
super(itemView);
keyID = itemView.findViewById(R.id.keyIDTV);
lockID = itemView.findViewById(R.id.lockIDTV);
eventTime = itemView.findViewById(R.id.eventDateTV);
eventType = itemView.findViewById(R.id.eventTypeTV);
itemView.setOnClickListener(this);
}
#Override
public void onClick(View view) {
if (mClickListener != null) mClickListener.onItemClick(view, getAdapterPosition());
}
}
// parent activity will implement this method to respond to click events
public interface ItemClickListener {
void onItemClick(View view, int position);
}
That's it, this should work perfectly.
I am having problems updating my RecyclerView with new data. If I press a confirmation button on a CardView in the first tab, the card should get added to the second tab but it won't update it there until I rotate the screen. I get the data for the card from reading a text file. Please advise me how to call the notifyDataSetChange method after I have added the new data to my text file. I have tried everything and all I get is NullPointerExceptions. The RecyclerViews are in fragments and I use FragementStatePagerAdapter.
I'll put some of my classes here. Ask if you need more information.
RecyclerViewAdapter.java
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewHolder> {
private List<String> mListTitle;
private List<String> mListDesc;
private List<String> mListPoints;
private List<String> mListDates;
private String fragment_tag;
public RecyclerViewAdapter() {
}
public RecyclerViewAdapter(List<List<String>> super_list, String tag) {
this.mListTitle = super_list.get(0);
this.mListDesc = super_list.get(1);
this.mListPoints = super_list.get(2);
this.mListDates = super_list.get(3);
fragment_tag = tag;
}
#Override
public RecyclerViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
return new RecyclerViewHolder(inflater, parent, fragment_tag);
}
#Override
public void onBindViewHolder(RecyclerViewHolder holder, int position) {
holder.mTitleText.setText(mListTitle.get(position));
holder.mDescText.setText(mListDesc.get(position));
holder.mPointsText.setText(mListPoints.get(position));
if (fragment_tag.equals("completed")) {
holder.mDateText.setText(mListDates.get(position));
}
}
#Override
public int getItemCount() {
return mListTitle.size();
}
}
class RecyclerViewHolder extends RecyclerView.ViewHolder {
RecyclerView recyclerView;
RecyclerViewAdapter mAdapter;
public TextView mTitleText, mDescText, mDateText, mPointsText, popupTitle;
public Button mConfButton, popCancelBtn, popAcceptBtn;
public RecyclerViewHolder(View itemView) {
super(itemView);
}
public RecyclerViewHolder(final LayoutInflater inflater, final ViewGroup container, String tag) {
// Inflating the card layout depending on the tag parameter.
super(inflater.inflate
((tag.equals("challenges")) ? R.layout.card_view_chall : R.layout.card_view_comp, container,
false));
mTitleText = itemView.findViewById(R.id.title_holder);
mDescText = itemView.findViewById(R.id.desc_holder);
mPointsText = itemView.findViewById(R.id.points_holder);
mDateText = itemView.findViewById(R.id.date_holder);
if (tag.equals("challenges")) {
mConfButton = itemView.findViewById(R.id.card_conf_button);
mConfButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// Setting the layout inflater for popup window.
LayoutInflater pInflater = (LayoutInflater) itemView.getContext().getSystemService(LAYOUT_INFLATER_SERVICE);
ViewGroup container1 = (ViewGroup) pInflater.inflate(R.layout.confirmation_popup, null);
final PopupWindow popupWindow = new PopupWindow(container1, 700, 600, true);
popupTitle = container1.findViewById(R.id.popuptext);
popAcceptBtn = container1.findViewById(R.id.accept_button);
popCancelBtn = container1.findViewById(R.id.cancel_button);
popupTitle.setText(mTitleText.getText().toString());
// Dismisses the popup window
popCancelBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
popupWindow.dismiss();
}
});
// Click listener for dialog accept button.
popAcceptBtn.setOnClickListener(new View.OnClickListener() {
String date;
#Override
public void onClick(View view) {
List<String> list = new ArrayList<>();
list.add(mTitleText.getText().toString());
list.add(mDescText.getText().toString());
list.add(mPointsText.getText().toString());
list.add(date = new SimpleDateFormat("dd-MM-yyyy", Locale.getDefault()).format(new Date()));
// Saving data from current card into the completed challenges list.
TempDataReader reader = new TempDataReader(itemView.getContext());
new TempDataReader(itemView.getContext()).saveFile(list);
// I want to notify the dataset change here if possible!
popupWindow.dismiss();
}
});
popupWindow.showAtLocation(itemView, Gravity.CENTER, 25, 100);
}
});
}
}
}
SectionsPagerAdapter.java
public class SectionsPagerAdapter extends FragmentStatePagerAdapter{
private ViewPager viewPager;
private final List<Fragment> mFragmentList = new ArrayList<>();
private final List<String> mFragmentTitleList = new ArrayList<>();
public void addFragment(Fragment fragment, String title){
mFragmentList.add(fragment);
mFragmentTitleList.add(title);
}
public SectionsPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public CharSequence getPageTitle(int position) {
return mFragmentTitleList.get(position);
}
#Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
#Override
public Fragment getItem(int position) {
return mFragmentList.get(position);
}
#Override
public int getCount() {
return mFragmentList.size();
}
#Override
public void notifyDataSetChanged() {
super.notifyDataSetChanged();
}
}
CompletedFragment.java
public class CompletedFragment extends Fragment {
RecyclerView recyclerView;
RecyclerViewAdapter adapter;
public Fragment newInstance() {
return new CompletedFragment();
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.completed_fragment, container, false);
recyclerView = view.findViewById(R.id.completed_frag);
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
TempDataReader reader = new TempDataReader(getActivity());
List<List<String>> super_list = reader.readCompFile();
if(super_list == null || super_list.size() < 1){
return null;
} else{
adapter = new RecyclerViewAdapter(super_list,"completed");
recyclerView.setAdapter(adapter);
return view;
}
}
}
EDIT:
Added the code for the second fragment, which is the one that should be updated after the onClick at RecyclerViewHolder-class.
You have to add a function in your adapter for adding data:
public void addData(String title, String desc, String point, String date) {
this.mListTitle.add(title);
this.mListDesc.add(desc);
this.mListPoints.add(point);
this.mListDates.add(date);
notifyDataSetChanged();
}
If you want to enable animations call notifyItemInserted() instead of notifyDataSetChanged();
It is important that you add a String to every list because in your onBindViewHolder() you get the item to display from every list with list.get(position). Otherwise you'll get a IndexOutOfBoundsException.
You can create an interface and use as a callback. Send it as a parameter of the RecyclerViewAdapter and then to your RecyclerViewHolder. When the item should be added you call the callback that will get you back to your fragment. There you can read the file again and call notifyDataSetChanged.
I know i explain pretty bad so i will try to change your code so that it does what i said:
this will be your RecyclerViewAdapter:
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewHolder> {
private List<String> mListTitle;
private List<String> mListDesc;
private List<String> mListPoints;
private List<String> mListDates;
private String fragment_tag;
private Runnable callback;
public RecyclerViewAdapter() {
}
public RecyclerViewAdapter(List<List<String>> super_list, String tag, Runnable callBack) {
//add the callback here(Runnable) and save it into a local variable
this.callback=callback;
this.mListTitle = super_list.get(0);
this.mListDesc = super_list.get(1);
this.mListPoints = super_list.get(2);
this.mListDates = super_list.get(3);
fragment_tag = tag;
}
#Override
public RecyclerViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
return new RecyclerViewHolder(inflater, parent, fragment_tag, callback);
//send the callback to your viewHolder
}
#Override
public void onBindViewHolder(RecyclerViewHolder holder, int position) {
holder.mTitleText.setText(mListTitle.get(position));
holder.mDescText.setText(mListDesc.get(position));
holder.mPointsText.setText(mListPoints.get(position));
if (fragment_tag.equals("completed")) {
holder.mDateText.setText(mListDates.get(position));
}
}
#Override
public int getItemCount() {
return mListTitle.size();
}
}
class RecyclerViewHolder extends RecyclerView.ViewHolder {
RecyclerView recyclerView;
RecyclerViewAdapter mAdapter;
public TextView mTitleText, mDescText, mDateText, mPointsText, popupTitle;
public Button mConfButton, popCancelBtn, popAcceptBtn;
public RecyclerViewHolder(View itemView) {
super(itemView);
}
public RecyclerViewHolder(final LayoutInflater inflater, final ViewGroup container, String tag, Runnable callback) {
//ADD the callback to the parameters list here
// Inflating the card layout depending on the tag parameter.
super(inflater.inflate
((tag.equals("challenges")) ? R.layout.card_view_chall : R.layout.card_view_comp, container,
false));
mTitleText = itemView.findViewById(R.id.title_holder);
mDescText = itemView.findViewById(R.id.desc_holder);
mPointsText = itemView.findViewById(R.id.points_holder);
mDateText = itemView.findViewById(R.id.date_holder);
if (tag.equals("challenges")) {
mConfButton = itemView.findViewById(R.id.card_conf_button);
mConfButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// Setting the layout inflater for popup window.
LayoutInflater pInflater = (LayoutInflater) itemView.getContext().getSystemService(LAYOUT_INFLATER_SERVICE);
ViewGroup container1 = (ViewGroup) pInflater.inflate(R.layout.confirmation_popup, null);
final PopupWindow popupWindow = new PopupWindow(container1, 700, 600, true);
popupTitle = container1.findViewById(R.id.popuptext);
popAcceptBtn = container1.findViewById(R.id.accept_button);
popCancelBtn = container1.findViewById(R.id.cancel_button);
popupTitle.setText(mTitleText.getText().toString());
// Dismisses the popup window
popCancelBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
popupWindow.dismiss();
}
});
// Click listener for dialog accept button.
popAcceptBtn.setOnClickListener(new View.OnClickListener() {
String date;
#Override
public void onClick(View view) {
List<String> list = new ArrayList<>();
list.add(mTitleText.getText().toString());
list.add(mDescText.getText().toString());
list.add(mPointsText.getText().toString());
list.add(date = new SimpleDateFormat("dd-MM-yyyy", Locale.getDefault()).format(new Date()));
// Saving data from current card into the completed challenges list.
TempDataReader reader = new TempDataReader(itemView.getContext());
new TempDataReader(itemView.getContext()).saveFile(list);
// I want to notify the dataset change here if possible!
//call the callback
callback.run();
popupWindow.dismiss();
}
});
popupWindow.showAtLocation(itemView, Gravity.CENTER, 25, 100);
}
});
}
}
}
And this will be your fragment:
public class CompletedFragment extends Fragment {
RecyclerView recyclerView;
RecyclerViewAdapter adapter;
public Fragment newInstance() {
return new CompletedFragment();
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.completed_fragment, container, false);
recyclerView = view.findViewById(R.id.completed_frag);
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
TempDataReader reader = new TempDataReader(getActivity());
List<List<String>> super_list = reader.readCompFile();
if(super_list == null || super_list.size() < 1){
return null;
} else{
adapter = new RecyclerViewAdapter(super_list,"completed", new Runnable() {
#Override
public void run() {
//here read the list again and call notifyDataSetChanged on your recycler
}
});
);
recyclerView.setAdapter(adapter);
return view;
}
}
}
Hope it helps and it works for you. If i did anything wrong, let me know, i can't run the code right now so...
edited, i forgot to add code in the callback
I have seen a lot of similar questions about this issue but I couldn't find any solution to this:
I have three fragments in a TabLayout handled by a ViewPager and I have a ListView in the third Fragment which is not displaying any items after I change the orientation of the screen. I have tried to set a background color to the convertView in the ArrayAdapter to see if it was displayed and indeed it is not being displayed after I change the orientation. But it's weird because in my First Fragment where I use the same ArrayAdapter for another ListView everything's working and I can't understand why it's not on the third Fragment.
One thing is sure: I have checked with logs almost everywhere that the ArrayList.size() is never zero.
Here's the Third Fragment's relevant code:
public class SavedScanFragment extends Fragment{
private ListView lv;
private Database db;
private ArrayList<Scan> scans;
private ScanAdapter adapter;
public static SavedScanFragment newInstance() {
return new SavedScanFragment();
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
db = new Database(getContext());
getScans();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View result = inflater.inflate(R.layout.fragment_saved_scan, container, false);
lv = (ListView) result.findViewById(R.id.saved_scan_list);
return result;
}
#Override
public void onResume() {
super.onResume();
db = new Database(getContext());
getScans();
}
public void getScans() {
AsyncTask<Void, Void, Boolean> task = new AsyncTask<Void, Void, Boolean>() {
#Override
protected Boolean doInBackground(Void... params) {
scans = db.getScans();
if(scans!=null)
return true;
else {
scans = new ArrayList<>();
return false;
}
}
#Override
protected void onPostExecute(Boolean result) {
if(result)
populateList();
}
};
task.execute();
}
public void populateList() {
if(getContext()!=null) {
if (lv.getAdapter() == null) {
adapter = new ScanAdapter(getContext(), R.layout.scan_list_item, scans);
lv.setAdapter(adapter);
} else {
((ScanAdapter) lv.getAdapter()).update(scans);
}
}
}
And the ArrayAdapter's code:
public class ScanAdapter extends ArrayAdapter<Scan> implements ListAdapter {
private Context context;
private int layoutResourceID;
private ArrayList<Scan> results;
public ScanAdapter(Context context, int layoutResourceID, ArrayList<Scan> results) {
super(context, layoutResourceID, results);
this.context = context;
this.layoutResourceID = layoutResourceID;
this.results = results;
}
public void update(ArrayList<Scan> scans) {
results.clear();
results.addAll(scans);
notifyDataSetChanged();
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = convertView; //view = row
ViewHolder holder = null;
if(view == null) {
LayoutInflater inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = inflater.inflate(layoutResourceID, parent, false);
view.setBackgroundColor(Color.GREEN);
holder = new ViewHolder();
holder.name = (TextView)view.findViewById(R.id.scan_name_and_date_text);
holder.ssid = (TextView)view.findViewById(R.id.scan_SSID_text);
holder.frequency = (TextView)view.findViewById(R.id.scan_frequency_text);
holder.level = (TextView)view.findViewById(R.id.scan_level_text);
view.setTag(holder);
} else
holder = (ViewHolder) view.getTag();
Scan sr = results.get(position);
holder.name.setText(sr.getName()+" ( "+sr.getDate()+" )");
holder.ssid.setText(sr.getSsid() + " ( " + sr.getBssid() + " )");
holder.frequency.setText(sr.getFrequency()+" MHz");
holder.level.setText(Integer.toString(sr.getLevel())+ " dBm");
return view;
}
static class ViewHolder {
TextView name,ssid, frequency, level;
}
}
Thanks in advance!
From this post. You can try to use onActivityCreated instead of onCreate. Try this:
public class SavedScanFragment extends Fragment{
//....to restore the saved state
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
//Restore the fragment state here
db = new Database(getContext());
getScans();
}
//add this to save instant state
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
//save your important variable here to outstate
}
}