RecyclerView + FirebaseRecyclerAdapter + Spinner Filter in activity - android

I have a recyclerview with FirebaseRecyclerAdapter and a spinner.
It show in activity a list of objects retrieve of firabase database.
My question is: Which better way for using filters in recycler view when user select a value in spinner? When selected a value in spinner below, nothing happens... Not update RecyclerView
exemple: When user select 'Brazil' in spinner, call a existent query like
query = databaseReference.child("People").orderByChild("country").equalTo("Brazil").getRef(); //I still dont know if this working.
query = databaseReference.child("People").getRef();
mRecyclerView = (RecyclerView) findViewById(R.id.my_recycler_view);
spinnerFilter = (Spinner) findViewById(R.id.spinFilter);
//--SPINNER EVENT SELECT--
AdapterView.OnItemSelectedListener selectedListener = new AdapterView.OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> spinner, View container, int position, long id)
{
if(id == 0)
{
query = databaseReference.child("PeopleList").orderByChild("country").equalTo("Canada").getRef();
}else
if(id == 1)
{
query = databaseReference.child("PeopleList").orderByChild("country").equalTo("Brazil").getRef();
}else
if(id == 2)
{
query = databaseReference.child("PeopleList").orderByChild("country").equalTo("EUA").getRef();
}
}
#Override
public void onNothingSelected(AdapterView<?> arg0) {
query = databaseReference.child("PeopleList").getRef();
}
};
spinnerFilter.setOnItemSelectedListener(selectedListener);
//---RECYCLER VIEW----
if (mRecyclerView != null) {
mRecyclerView.setHasFixedSize(true);
}
gLayoutManager= new GridLayoutManager(this, 2);
mRecyclerView.setLayoutManager(gLayoutManager);
FirebaseRecyclerAdapter<People, PeopleListViewHolder> adapter = new FirebaseRecyclerAdapter<People, PeopleListViewHolder>
(
People.class,
R.layout.layout_people_item,
PeopleListViewHolder.class,
query
)
{
#Override
protected void populateViewHolder(PeopleListViewHolder viewHolder, People model, int position)
{
viewHolder.tvName.setText(model.getName());
Picasso.with(PeopleListActivity.this).load(model.getUrlThumbnail()).into(viewHolder.thumbnail);
}
};
mRecyclerView.setAdapter(adapter);
I check a lot links here but none success:
https://stackoverflow.com/questions/39076702/filtering-data-before-populating-view-from-firebaserecycleradapter?rq=1
https://github.com/firebase/FirebaseUI-Android/issues/15
https://groups.google.com/forum/#!topic/firebase-talk/O1kJ4VLUg10
https://github.com/firebase/FirebaseUI-Android/issues/15
Thanks very much if can help me.

Related

How to filter firebase recycler view?

I have an android app linked with my firestore database. I am fetching data from the firestore and displaying the results in a recycler view with the help of firebaseRecyclerView. My app is working perfectly and I'm able to display all the results in my recycler view.
Now I want to add the feature of filtering the already fetched data using a spinner. i.e the user first sees all the results, then uses the spinner to display only the relevant data.
I'm able to manage the case when there are no results from the firestore/recycler view that is empty.
To filter, I think I may have to use the onItemSelected function of the spinner, however, I don't know how I should proceed.
To filter I would be basically comparing two strings.
One from the text selected from the spinner , and one from each Textview of the row of recyclerview.
Please let me know how I should proceed.
Here is my onCreate method:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_query);
filter_price_text = findViewById(R.id.filter_price_text);
recyclerView = findViewById(R.id.recyclerView_tProfile);
emptyView = findViewById(R.id.empty_view);
query = db
.collection("tUsers")
.whereEqualTo(testArray[0], true)
.whereEqualTo(testArray[1], true)
.whereEqualTo("pincode", customerPincode)
.whereEqualTo("sector", customerSector); //query is working . ignore what I'm querying for. I have removed unimportant data from onCreate
FirestoreRecyclerOptions<TProfileModel> options = new FirestoreRecyclerOptions.Builder<TProfileModel>().setQuery(query, TProfileModel.class).build();
adapter = new FirestoreRecyclerAdapter<TProfileModel, profileViewHolder>(options) {
#NonNull
#Override
public profileViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.t_profile_row, parent, false);
return new profileViewHolder(view);
}
#Override
protected void onBindViewHolder(#NonNull profileViewHolder holder, int position, #NonNull final TProfileModel model) { //final added manually to test should remove
holder.t_name.setText(model.getName());
String tsector = "Sector : " + model.getSector();
holder.t_sector.setText(tsector);
String tpin = "Pincode : " + model.getPincode();
holder.t_pincode.setText(tpin);
String trating = "Rating : " + model.getRating();
holder.t_rating.setText(trating);
String tprice = "Avg. Price : " + model.getAvgPriceRange();
holder.t_price.setText(tprice);
ta_price = model.getAvgPriceRange();//will be used for comparing with spinner data
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(MainQueryActivity.this, ViewTActivity.class);
intent.putExtra("name", model.getName());
intent.putExtra("sector", model.getSector());
intent.putExtra("pincode", model.getPincode());
startActivity(intent);
}
});
}
#Override
public void onDataChanged() {
emptyView.setVisibility(getItemCount() == 0 ? View.VISIBLE : View.GONE);
priceFilter.setVisibility(getItemCount() == 0 ? View.GONE : View.VISIBLE);
filter_price_text.setVisibility(getItemCount() == 0 ? View.GONE : View.VISIBLE);
}
#Override
public void onError(FirebaseFirestoreException e) {
// Called when there is an error getting a query snapshot. You may want to update
// your UI to display an error message to the user.
// ...
}
};
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(adapter);
priceFilter = findViewById(R.id.priceFilter_spinner);
ArrayAdapter<CharSequence> adapter1 = ArrayAdapter.createFromResource(this, R.array.t_price_spin, android.R.layout.simple_spinner_item);
adapter1.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
priceFilter.setAdapter(adapter1);
final String spinner_price = priceFilter.getSelectedItem().toString().trim();
priceFilter.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
//compare ta_price with spinner_price
}
#Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
}

Changing Spinner list content after item is selected Android

Is there a way to change the displayed list inside of the Spinner after selection. I have 2 Strings, English and Finnish and I want to first change their language when one is selected, secondly change their order when the other is selected.
private void initLanguageSpinner() {
List<String> spinnerLocale = new ArrayList<>();
if ((currentdocumentLocale.toString().startsWith("deviceNameFi") || currentdocumentLocale.getLanguage().equals("fi"))) {
spinnerLocale.add(getLocalizedResources(context, currentdocumentLocale).getString(R.string.finnish_language));
spinnerLocale.add(getLocalizedResources(context, currentdocumentLocale).getString(R.string.english_language));
} else {
spinnerLocale.add(getLocalizedResources(context, currentdocumentLocale).getString(R.string.english_language));
spinnerLocale.add(getLocalizedResources(context, currentdocumentLocale).getString(R.string.finnish_language));
}
ArrayAdapter<String> adapter = new ArrayAdapter<>(getActivity(), R.layout.document_spinner_item, spinnerLocale);
adapter.setDropDownViewResource(R.layout.document_spinner_dropdown_item);
customerSelectedLanguageSpinner.setAdapter(adapter);
customerSelectedLanguageSpinner.setSelection(0, false);
customerSelectedLanguageSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
if (customerSelectedLanguageSpinner.getSelectedItem().toString().equals(getLocalizedResources(context, currentdocumentLocale).getString(R.string.finnish_language))) {
onLocaleChangedFi();
initLanguageSpinner();
} else if (customerSelectedLanguageSpinner.getSelectedItem().toString().equals(getLocalizedResources(context, currentdocumentLocale).getString(R.string.english_language))) {
onLocaleChangedEng();
initLanguageSpinner();
}
}
#Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
}
By doing this it basically does what I need, but it also goes into an endless loop since the onItemSelected is always called after initialization, even when I do the .setSelection(0, false). Is there a way to do what I need?
EDIT
So I tried creating a new adapter and then notifying it of changes.
private ArrayAdapter<String> spinnerAdapter;
private void initLanguageSpinner() {
List<String> spinnerLocale = new ArrayList<>();
if ((currentdocumentLocale.toString().startsWith("deviceNameFi") || currentdocumentLocale.getLanguage().equals("fi"))) {
spinnerLocale.add(getLocalizedResources(context, currentdocumentLocale).getString(R.string.finnish_language));
spinnerLocale.add(getLocalizedResources(context, currentdocumentLocale).getString(R.string.english_language));
} else {
spinnerLocale.add(getLocalizedResources(context, currentdocumentLocale).getString(R.string.english_language));
spinnerLocale.add(getLocalizedResources(context, currentdocumentLocale).getString(R.string.finnish_language));
}
spinnerAdapter = new ArrayAdapter<>(getActivity(), R.layout.document_spinner_item, spinnerLocale);
spinnerAdapter.setDropDownViewResource(R.layout.document_spinner_dropdown_item);
customerSelectedLanguageSpinner.setAdapter(spinnerAdapter);
customerSelectedLanguageSpinner.setSelection(0, false);
customerSelectedLanguageSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
if (customerSelectedLanguageSpinner.getSelectedItem().toString().equals(getLocalizedResources(context, currentdocumentLocale).getString(R.string.finnish_language))) {
setSpinnerAdapter(customerSelectedLanguageSpinner.getSelectedItem().toString());
onLocaleChangedFi();
} else if (customerSelectedLanguageSpinner.getSelectedItem().toString().equals(getLocalizedResources(context, currentdocumentLocale).getString(R.string.english_language))) {
setSpinnerAdapter(customerSelectedLanguageSpinner.getSelectedItem().toString());
onLocaleChangedEng();
}
}
#Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
}
private void setSpinnerAdapter(String language){
if(language.equals("fi")){
String[] myListFinnish = new String[] { getLocalizedResources(context, currentdocumentLocale).getString(R.string.finnish_language), getLocalizedResources(context, currentdocumentLocale).getString(R.string.english_language)};
spinnerAdapter = new ArrayAdapter<>(getActivity(), R.layout.document_spinner_item, myListFinnish);
} else {
String[] myListEnglish = new String[] { getLocalizedResources(context, currentdocumentLocale).getString(R.string.english_language), getLocalizedResources(context, currentDocumentLocale).getString(R.string.finnish_language)};
spinnerAdapter = new ArrayAdapter<>(getActivity(), R.layout.document_spinner_item, myListEnglish);
}
spinnerAdapter.setDropDownViewResource(R.layout.document_spinner_dropdown_item);
spinnerAdapter.notifyDataSetChanged();
}
This way it does not even change the list strings or order. onLocaleChangedEng(); basically changes the Locale variable in the application.
your code is a dead loop. and if the adapter item data changed,
such as add, remove, clear by adapter you can use notifyDataSetChanged
method.
Notifies the attached observers that the underlying data has been changed and any View reflecting the data set should refresh itself.
adapter.clear()/add(object)/addAll();
adapter.notifyDataSetChanged();
then the view will update but not init.
if the whole data changed, you can also recreate an adapter, and then set data
dataAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item, newStringList);
dataAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinnerCategory.setAdapter(dataAdapter)
At the same time, you can also clear and addAll, then notifyDataSetChanged
adapter.clear() //remove all data;
adapter.addAll(source);
adapter.notifyDataSetChanged();
//or remove it, then insert it in the first. that is acturl for your conditon
adapter.remove(objcet) //remove special data;
adapter.insert(object,index);
adapter.notifyDataSetChanged();
Secondly, you can also set a Tag to make when init the onItemSelected not called. the code like this:
//define a tag when first it is 0
int check = 0;
//then in the onItemSelected add a check condition
public void onItemSelected(AdapterView<?> parent, View arg1, int pos,long id) {
if(++check> 1) {
//do some things when item selected.
}
}
use notifyDataSetChanged to update data, not every time init spinner. if you only want to remove the init selected effect, use the check tag.

StaggeredGridLayout manager after deleting item, items gets scattered

I am using a Staggered Grid Layout to show data to recycler view from database.I have faced a problem where after deleting an item from db as well as remove position from adapter, I got some item rendering issues.Like the scattered all over the place.
Here is my code
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_note_favorite);
ButterKnife.bind(this);
toolbar = (Toolbar) findViewById(R.id.fav_note_toolbar);
setSupportActionBar(toolbar);
noteAdapter = new NoteAdapter(noteModelList, NoteFavoriteActivity.this);
layoutManager = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL);
recyclerView.setLayoutManager(layoutManager);
recyclerView.setItemAnimator(new DefaultItemAnimator());
recyclerView.setAdapter(noteAdapter);
touchListenerRecycler();
loadDataAll();
}
private void loadAll() {
noteModelList.clear();
DBManagerFav dbManagerFav = DBManagerFav.getInstance(NoteFavoriteActivity.this);
dbManagerFav.openDataBase();
noteModelList = dbManagerFav.getAllNoteList();
Log.i(TAG, " size : " + noteModelList.size());
noteAdapter = new NoteAdapter(noteModelList, NoteFavoriteActivity.this);
recyclerView.setAdapter(noteAdapter);
noteAdapter.notifyDataSetChanged();
dbManagerFav.closeDataBase();
}
private void deleteOperation() {
DBManagerFav dbManagerFav = DBManagerFav.getInstance(NoteFavoriteActivity.this);
dbManagerFav.openDataBase();
NoteModel noteModel = new NoteModel();
noteModel.setId(noteModelList.get(adapterClickedPosition).getId());
int status = dbManagerFav.deleteNote(noteModelList.get(adapterClickedPosition).getId());
if (status > 0) {
noteAdapter.removeAt(adapterClickedPosition);
}
dbManagerFav.closeDataBase();
loadDataAll();
}
//this belongs to adapter
public void removeAt(int position) {
Log.d(TAG, " removing at position : " + position);
noteModelList.remove(position);
notifyItemRemoved(position);
notifyItemRangeChanged(position, noteModelList.size());
notifyDataSetChanged();
}
I have attached two screenshots before and after
Before deleting an item
after deleting an item
Can you point me out what else do I need ?
You can put inside loadDataAll(){
layoutManager = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL);
recyclerView.setLayoutManager(layoutManager);
//........
}
Voila..

Wrong item in recyclerview after filtering

hi i have a RecyclerView adapter as follow :-
public class RecyclerViewAdapter extends RecyclerView.Adapter<ViewHolder> {
List<Data> list = Collections.emptyList();
Context context;
public RecyclerViewAdapter(List<Data> list, Context context) {
this.list = list;
this.context = context;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
//Inflate the layout, initialize the View Holder
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.apps_layout, parent, false);
ViewHolder holder = new ViewHolder(v);
return holder;
}
#Override
public void onViewDetachedFromWindow(ViewHolder holder) {
super.onViewDetachedFromWindow(holder);
((ViewHolder)holder).itemView.clearAnimation();
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
//Use the provided View Holder on the onCreateViewHolder method to populate the current row on the RecyclerView
holder.title.setText(list.get(position).title);
holder.description.setText(list.get(position).description);
}
public void setFilter(List<Data> searched_data) {
list = new ArrayList<>();
list.addAll(searched_data);
notifyDataSetChanged();
}
#Override
public int getItemCount() {
//returns the number of elements the RecyclerView will display
return list.size();
}
#Override
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
}
// Insert a new item to the RecyclerView on a predefined position
public void insert(int position, Data data) {
list.add(position, data);
notifyItemInserted(position);
}
// Remove a RecyclerView item containing a specified Data object
public void remove(Data data) {
int position = list.indexOf(data);
list.remove(position);
notifyItemRemoved(position);
}
}
okay now as you can see setFilter method to filter recyclerview list
here is how i call it
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
#Override
public boolean onQueryTextSubmit(String s) {
return false;
}
#Override
public boolean onQueryTextChange(String s) {
List<Data> filteredModelList = filter(data, s);
adapter.setFilter(filteredModelList);
return true;
}
});
now filter works fine now when i click on it the position is wrong
i know the error coming from the position as the older recyclerview on click has old position index i dont know how to fix it anyway here is onclicklistener
data = appsdata(); // load the list List<Data>
Collections.sort(data); // sorting
recyclerView = (RecyclerView) findViewById(R.id.recyclerview);
final LinearLayoutManager layoutManager = new LinearLayoutManager(this);
layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
recyclerView.setLayoutManager(layoutManager);
adapter = new RecyclerViewAdapter(data, this);
recyclerView.setAdapter(adapter);
recyclerView.setNestedScrollingEnabled(false);
recyclerView.addOnItemTouchListener(new RecyclerItemClickListener(this, recyclerView, new RecyclerItemClickListener.OnItemClickListener() {
#Override
public void onItemClick(View view, int position) {
String s = data.get(position).title;
String ss = data.get(position).description;
Intent intent = new Intent(MainAcitivity.this, Work.class);
intent.putExtra("title", s);
intent.putExtra("desc", ss);
startActivity(intent);
}
}));
i know this line is causing problem
String s = data.get(position).title;
because it is same as old when list was not filtered ,how i can update this recyclerview so that i can get updated index when launching Work.class
okay guys this what works partially for me
i tried updating the data list by adding
data = filteredModelList; // add this line
after filtering and this worked fine
#Override
public boolean onQueryTextChange(String s) {
List<Data> filteredModelList = filter(data, s);
adapter.setFilter(filteredModelList);
data = filteredModelList; // add this line
return true;
}
now i get real index but when i search and clear the list the list is empty because i replaced the data list :(
EDIT :- i fixed it by hack not a nice way but works
as you can see the old list was gone by using above stuff before edit
when i used to change the data list
data = filteredModelList; // add this line
then the new data list has only filtered values and when you clear searchView then that data list is empty because it does not have those all apps list what i did was that i made new list as data_fix and onclicklistener i used datafix instead of data list
see this how
RecyclerItemClickListener method
String s = datafix.get(position).title; // data.get to datafix.get
String ss = datafix.get(position).description; // data.get to datafix.get
now onsetfilter call
i called this
datafix = filteredModelList; // instead of data = filteredModelList;
also i initialize it oncreate so it is not empty on launch of the app by
data = appsdata(); // load the list List<Data>
datafix = appsdata(); // load the list List<Data> added just below it to intialize
now this way the full loaded applist // List data stays full and doesnot clear even if you clear from searchView and index is all right
The logic lies to how you update the recycler view items,
public void setFilter(List<Data> searched_data) {
list = new ArrayList<>();
list.addAll(searched_data);
notifyDataSetChanged();
}
The above line is not necessary, adapter = new RecyclerViewAdapter(data, this); this line is very important to understand. You see when you pass data to the adapter is like this you are creating a symbolic link between the list in the adapter and outside. So therefore any changes made to the outside list, to update the adapter version.Just call adapter.notifyDatasetChange method
do this whenever any changes are made to the data model object.
CHANGE THE FOLLOWING FROM
List<Data> filteredModelList = filter(data, s);
adapter.setFilter(filteredModelList);
data = filteredModelList; // add this line
return true;
TO,
adapter.list.clear();
data = filter(data, s);
adapter.notifyDataSetChange();

Android - ListView layout two line list item

I have a listview with two lines of items: user ID and username. How to retrieve only user ID from the listview when one of the rows is clicked?
public class MainActivity extends AppCompatActivity {
private DatabaseReference mUsers;
String getValue = null;
ListView lvUsers = (ListView) findViewById(R.id.lvUsers);
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mUsers = FirebaseDatabase.getInstance.getReference().child(Constants.FIREBASE_USERS);
FirebaseListAdapter<Users> adapter = new FirebaseListAdapter<Users>(
this, Users.class, android.R.layout.two_line_list_item, mUsers
) {
#Override
protected void populateView(View v, Users user, int pos) {
((TextView)v.findViewById(android.R.id.text1)).setText(user.getUserID());
((TextView)v.findViewById(android.R.id.text2)).setText(user.getUsername());
}
};
lvUsers.setAdapter(adapter);
setupListViewMenu();
}
private void setupListViewMenu() {
lvUsers.setOnItemLongClickListener(
new AdapterView.OnItemLongClickListener() {
#Override
public boolean onItemLongClick(AdapterView<?> adapter,
View item, int pos, long id) {
// How to retrieve only User ID from here?
getValue = ((TextView)view).getText().toString();
return true;
}
});
}
}
The listview data is from Firebase database. Is retrieving using for loop the only way? I have to loop through two dimensional array? Thanks
Could you please try the code below?
getValue = ((TextView)item.findViewById(android.R.id.text2)).getText().toString();
I believe that android.R.id.text2 is one of the childs of item(View received in the clickListener)
But need to test... Hope it works

Categories

Resources