1

I have a list of Tags stored in Firebase. In one of my Fragments, a MultiAutoCompleteTextView (MACTV) lets the User, to select the relevant Tags.

The objective,

  • Is to populate the Array (used in ArrayAdapter for MACTV) using the Tags from Firebase.
  • Once the user selects the relevant Tags from MACTV using OnItemClickListener, the selected Tags needs to be saved to Firebase.

Here's how I tried implementing.

Defining the ArrayAdapter for MACTV:

ArrayAdapter<String> adapterMultiAutoComplete = new ArrayAdapter<>(getActivity(), android.R.layout.simple_list_item_1);

Using the AddValueEventListener to populate the ArrayAdapter

if (fbUser != null) {
  dbTags.addValueEventListener(new ValueEventListener() {
      @Override
      public void onDataChange(DataSnapshot dataSnapshot) {
      //Basically, this says "For each DataSnapshot *Data* in dataSnapshot, do what's inside the method.
          for (DataSnapshot tagNameSnapshot : dataSnapshot.getChildren()) {
          //Get the suggestion by childing the key of the string you want to get.
              String ValueTagName = tagNameSnapshot.child("tagName")).getValue(String.class);
          //Add ValueTagName to ArrayAdapter
              adapterMultiAutoComplete.add(ValueTagName);
          }
       }

       @Override
       public void onCancelled(DatabaseError databaseError) {/*Do Nothing*/}
   });
}

Code for MACTV

MultiAutoCompleteTextView articleTags = (MultiAutoCompleteTextView) findViewById(R.id.mactv_tags);
articleTags.requestFocus();
articleTags.setTokenizer(new MultiAutoCompleteTextView.CommaTokenizer());
articleTags.setAdapter(adapterMultiAutoComplete);

Saving the selected tags to Firebase

List<String> ArticleTags = new ArrayList<>(Arrays.asList(articleTags.getText().toString().split(", ")));
DatabaseReference db = FirebaseDatabase.getInstance().getReference().child("tags").setValue(ArticleTags);

The Threshold value is defined as 2. Unfortunately, no relevant tags appear as pop-up when I start typing in MACTV.

Where did I go wrong?

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
user3314337
  • 341
  • 3
  • 13

1 Answers1

2

BTW, great question! The most interesting I've seen this month.

First of all, with this code you loading whole tags node to device. Imagine, you have 2M of tags (does SO has?), will this code still work? ;)

The second thing, code inside onDataChange (as well as code in all firebase callbacks) is called on worker thread, but adapter methods should be called in UI thread.

And the last, here is how I see the solution:

  1. You need to use Queries (you can read more in docs)
  2. First of all, we need to sort tags by name. So this is base query:

    DatabaseReference baseRef = FirebaseDatabase.getInstance().getReference().child("tags").orderByChild("tagName");
    
  3. Next - you must attach TextWatcher to your MultiAutoCompleteTextView with following code

    articleTags.addTextChangedListener(new TextWatcher() {
    
        @Override public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
    
        @Override
        public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
    
            if (charSequence.length <3) return;
    
            String searchTarget = charSequence.toString().toLowerCase();
            //Here magic happens)
            baseRef.startAt(searchTarget).endAt(searchTarget + "\uf8ff").limitToFirst(20).addValueEventListener(new ValueEventListener() {
                @Override
                public void onDataChange(DataSnapshot dataSnapshot) {
                    //Handle executing this code in main thread yourself, answer will be too long with it
                    adapterMultiAutoComplete.removeAll();
                    for (DataSnapshot data: dataSnapshot.getChildren()) {
                         adapterMultiAutoComplete.add(data.getValue(String.class))
                    }
                    adapterMultiAutoComplete.notifyDatasetChanged();
                }
    
                @Override
                public void onCancelled(DatabaseError databaseError) {
                   Log.wtf("What a terrible failure!", databaseError.toException());
                }
            });
    
        }
    
        @Override public void afterTextChanged(Editable editable) {}
    
    });
    

P.S.: Code never tested and probably not working. But you got the idea ;)

Dmytro Rostopira
  • 10,588
  • 4
  • 64
  • 86
  • Thanks for introducing the alternate perspective towards the solution. I didn't think of using Queries earlier. But which is the better way? _Case 01:_ Using For loop to fetch all the available tagNames. In this case, upside being faster searches, but doesn't make sense when involving too many tags. In _Case 02:_ (using queries), too much search time involved (say when I wish to search and add ten tags) – user3314337 Mar 09 '17 at 22:19
  • @user3314337 set limit to 100, for example. If you got less than 100 values, save them in adapter and don't make more queries =) – Dmytro Rostopira Mar 10 '17 at 08:50