What I want to do is to populate AutoComplete TextView with the data from Firestore. I tried to retrieve data from Firestore and use it to create a Songs Object and store it in an ArrayList named songs. Those data are all Strings. You can see below. auto refers to the AutoComplete TextView.
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
seekbar = findViewById(R.id.seekBar);
auto = findViewById(R.id.auto);
current = findViewById(R.id.start);
end = findViewById(R.id.end);
lol.add("chey");
lol.add("oi");
db.collection("Songs_names").get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
@Override
public void onComplete(@NonNull Task<QuerySnapshot> task) {
if(task.isSuccessful()){
if (task.getResult() != null) {
for (QueryDocumentSnapshot document : task.getResult()) {
Log.d("tag","msg " + document.getString("name"));
songs.add(new Songs(document.getString("name")));
Log.d("show objects","showed" + songs);
}
}
}
}
});
songs_auto_adapter auto_adapter = new songs_auto_adapter(this,songs,this);
Log.d("show songs","show" + songs);
//ArrayAdapter<String> adapter = new ArrayAdapter<>(this,android.R.layout.simple_list_item_1,lol);
auto.setAdapter(auto_adapter);
Then, I setAdapter to auto_adapter. You can see it below.
public class songs_auto_adapter extends ArrayAdapter<Songs> {
private List<Songs> songsFull;
private Touch touch;
public songs_auto_adapter(@NonNull Context context, @NonNull List<Songs> objects,Touch touch) {
super(context,0, objects);
songsFull = new ArrayList<>(objects);
this.touch = touch;
}
@NonNull
@Override
public Filter getFilter() {
return filter;
}
@NonNull
@Override
public View getView(final int position, @Nullable View convertView, @NonNull ViewGroup parent) {
Log.d("View", "Creating Views");
if (convertView == null){
convertView = LayoutInflater.from(getContext()).inflate(R.layout.songs_for_auto,parent,false);
}
final TextView textView = convertView.findViewById(R.id.auto_text);
Songs songs = getItem(position);
if (songs != null) {
textView.setText(songs.getName());
}
convertView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.d("clicked","It works" + position);
touch.Touched(textView.getText().toString().trim());
}
});
return convertView;
}
private Filter filter = new Filter() {
@Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults results = new FilterResults();
List<Songs> suggestions = new ArrayList<>();
if ( constraint == null || constraint.length() == 0){
suggestions.addAll(songsFull);
}
else{
String filtertxt = constraint.toString().toLowerCase().trim();
for (Songs s : songsFull){
if (s.getName().toLowerCase().contains(filtertxt)){
suggestions.add(s);
Log.d("filtering","msg" + suggestions);
}
}
}
results.values = suggestions;
results.count = suggestions.size();
return results;
}
@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
clear();
addAll((List) results.values);
notifyDataSetChanged();
Log.d("published","mmm" + results.values + songsFull);
}
};
The problem is when I typed in the AutoComplete TextView, nothing showed up. Then I used Log.d and I found out that publishResult is the one that gets called first and performFiltering is never called. See the message below.
D/published: mmm[][]
I/zygote: Do partial code cache collection, code=55KB, data=53KB
After code cache collection, code=55KB, data=53KB
Increasing code cache capacity to 256KB
However, when I typed in the data manually, like this, songs.add(new Songs("sth")); ,the whole thing works. I cannot think of any solutions to solve this so I am here to ask. Thank you.
You are dealing with network request and it is not sequential. What it means - by the time adapter is created and set to the auto
object the network request that returns a list of songs is not yet finished. Or maybe it has finished successfully, or maybe failed - no one knows.
Thus, from the OnCompleteListener
you must make sure you update the list of songs and adapter. When the list is updated from onComplete
method - your adapter knows nothing about that update. It does not watch changes in the array by itself.
You have to do a few things:
db.collection("Songs_names").get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
@Override
public void onComplete(@NonNull Task<QuerySnapshot> task) {
// Step 1: if activity is no longer valid to use just skip this "task" response.
if (isFinishing() || isDestroyed()) return;
if(task.isSuccessful()){
if (task.getResult() != null) {
for (QueryDocumentSnapshot document : task.getResult()) {
Log.d("tag","msg " + document.getString("name"));
songs.add(new Songs(document.getString("name")));
Log.d("show objects","showed" + songs);
// Step 2: songs are updated
adapter.updateList(songs);
}
}
}
}
});
Now you have to modify your adapter a little bit. A new method is added that accepts a list of songs and notifies adapter about the changes.
public class songs_auto_adapter extends ArrayAdapter<Songs> {
private List<Songs> songsFull;
private Touch touch;
public songs_auto_adapter(@NonNull Context context, @NonNull List<Songs> objects,Touch touch) {
super(context,0, objects);
songsFull = new ArrayList<>(objects);
this.touch = touch;
}
public void updateList(@NonNull List<Songs> newList) {
songsFull = new ArrayList<>(newList);
clear();
addAll(songsFull); // Adapter is now aware of the updated list
}
...
}