简体   繁体   中英

Data fetched from FirebaseDatabase is getting shown in 3 separate AlertDialogs instead of one

I'm fetching some data from FirebaseDatabase and then putting them into an array and then trying to show them in a List which is in a custom AlertDialog .

Here's the code:

query = mDatabase.child("child").child(anotherChild).child("yetAnotherChild");

uProfile.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        query.orderByChild("someChild").addChildEventListener(new ChildEventListener() {
            @Override
            public void onChildAdded(DataSnapshot dataSnapshot, String s) {
                if (dataSnapshot != null) {

                    Map<String, String> newD = (Map<String, String>) dataSnapshot.getValue();

                    ArrayList<String> l = new ArrayList<String>();
                    l.add(newD.get("lol").substring(30));

                    String names[] = l.toArray(new String[0]);

                    AlertDialog.Builder alertDialog = new AlertDialog.Builder(Activity.this);
                    LayoutInflater inflater = getLayoutInflater();
                    View convertView = inflater.inflate(R.layout.dialog_list, null);
                    alertDialog.setView(convertView);
                    alertDialog.setTitle("title");
                    ListView lv = (ListView) convertView.findViewById(R.id.lv);
                    ArrayAdapter<String> adapter = new ArrayAdapter<String>(getBaseContext(), android.R.layout.simple_list_item_1, names);
                    lv.setAdapter(adapter);

                    alertDialog.setPositiveButton("OK", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialogInterface, int i) {

                        }
                    });

                    alertDialog.show();
                } else {
                    Toast.makeText(getBaseContext(), "NULLLLL", Toast.LENGTH_SHORT).show();
                }
            }

            ...
            ...
        });

    }
});

Here's the database structure:

app
 -child
   -anotherChild
     -yetAnotherChild
       -inaccessibleChild
         -someChild: "value"
         -lol: "value"

I can't use valueEventListener() here as I have no access to inaccessibleChild . The inaccessibleChild here is the uid of the other users who followed a particular user. How can I access there uid ?

The problem is that data is getting fetched but instead of getting shown in a list in one AlertDialog , it is getting shown one-by-one in 3 separate AlertDialog .

What is going wrong here?

Please let me know.

Firebase transactions are asynchronous so your initial line to add the 3 children happens after you set your listener, therefore your callback is called 3 times. (making 3 dialogs).

Move this line outside of the on click:

 query = mDatabase.child("child").child(anotherChild).child("yetAnotherChild");

Then when you add the below listener (inside the on click) it should be ok

query.orderByChild("someChild").addChildEventListener(new ChildEventListener() {
                    @Override
                    public void onChildAdded(DataSnapshot dataSnapshot, String s) {

Put limit when you are using valueEventListener or addChildEventListener . like this,

int limit = 100;
databaseRef.child("chats").limitToLast(limit).addValueEventListener(listener);

And always remove listener when you've done with your work related to firebase. like this,

databaseRef.child("chats").limitToLast(limit).removeEventListener(listener);

Thanks.

Create and show the AlertDialog before you call .addChildEventListener()

Then use inside addChildEventListener call notifyDatasetChanged() after you downloaded the appropriate data. Do not create the AlertDialog inside the addChildEventListener

您可以使用Firebase查询来限制侦听器下载的数据。

query.orderByChild("someChild").limitToLast(1).addChildEventListener(...

@Blundell has the insight of solving your problem. I just want to suggest you a similar approach with addValueEventListener .

The value event listener will fire once for the initial state of the data, and then again every time the value of that data changes.

You need to move out the firebase query from onClick function. So your query might look like this..

// Declare the variable names as public
private String names[];

private void addFirebaseListener() {
    ref = mDatabase.child("child").child(anotherChild).child("yetAnotherChild");
    ref. userRef.addListenerForSingleValueEvent(new ValueEventListener() {
        @Override
        public void onDataChange(DataSnapshot snapshot) {
          Map<String, String> newD = (Map<String, String>) dataSnapshot.getValue();

          ArrayList<String> l = new ArrayList<String>();
          l.add(newD.get("lol").substring(30));

          names[] = l.toArray(new String[0]);

          // Call notifyDataSetChanged each time your array gets updated
          adapter.notifyDataSetChanged();
        }

        @Override public void onCancelled(FirebaseError error) { }
    });
}

Now write a function to show the ListView in the AlertDialog

// Declare the adapter as public and initialize it with null
private ArrayAdapter<String> adapter = null;

private void showListInDialog() {
    AlertDialog.Builder alertDialog = new AlertDialog.Builder(Activity.this);
    LayoutInflater inflater = getLayoutInflater();
    View convertView = inflater.inflate(R.layout.dialog_list, null);
    alertDialog.setView(convertView);
    alertDialog.setTitle("title");
    ListView lv = (ListView) convertView.findViewById(R.id.lv);

    if(adapter == null)
        adapter = new ArrayAdapter<String>(getBaseContext(), android.R.layout.simple_list_item_1, names);

    // Now set the adapter
    lv.setAdapter(adapter);

    alertDialog.setPositiveButton("OK", new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialogInterface, int i) {
            alertDialog.dismiss();
        }
    });

    alertDialog.show();
}

Now inside your onCreate function you need to set the Firebase listener first and then set the onClick function like this.

uProfile.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        showListInDialog();
    }
});

@Hammad Try this modified code basing on your requirement.

    query = mDatabase.child("child").child(anotherChild).child("yetAnotherChild");

    AlertDialog alertDialog;
    ArrayList<String> l;

    uProfile.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            query.orderByChild("someChild").addChildEventListener(new ChildEventListener() {
                @Override
                public void onChildAdded(DataSnapshot dataSnapshot, String s) {
                    if (dataSnapshot != null) {

                        Map<String, String> newD = (Map<String, String>) dataSnapshot.getValue();

    if(l == null) {
                        l = new ArrayList<String>();
    }
                        l.add(newD.get("lol").substring(30));

    String names[] = new String[l.size()];
    for(int length=0; length < l.size(); l++) {
        names[length] = l.get(length);
    }

    if(alertDialog == null) {
                        alertDialog = new AlertDialog.Builder(Activity.this).create();
                        LayoutInflater inflater = getLayoutInflater();
                        View convertView = inflater.inflate(R.layout.dialog_list, null);
                        alertDialog.setView(convertView);
                        alertDialog.setTitle("title");
                        ListView lv = (ListView) convertView.findViewById(R.id.lv);

    } ArrayAdapter<String> adapter = new ArrayAdapter<String>(getBaseContext(), android.R.layout.simple_list_item_1, names);
                        lv.setAdapter(adapter);

                        alertDialog.setPositiveButton("OK", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialogInterface, int i) {

                            }
                        });

if(!alertDialog.isShowing()) {
                        alertDialog.show();
}
                    } else {
                        Toast.makeText(getBaseContext(), "NULLLLL", Toast.LENGTH_SHORT).show();
                    }
                }

                ...
                ...
            });

        }
    });
ChildEventListener childEventListener = new ChildEventListener() {
@Override
public void onChildAdded(DataSnapshot dataSnapshot, String previousChildName) {
    Log.d(TAG, "onChildAdded:" + dataSnapshot.getKey());

/**

Retrieve lists of items or listen for additions to a list of items. This callback is triggered once for each existing child and then again every time a new child is added to the specified path. The DataSnapshot passed to the listener contains the new child's data.

**/

}

@Override
public void onChildChanged(DataSnapshot dataSnapshot, String previousChildName) {
    Log.d(TAG, "onChildChanged:" + dataSnapshot.getKey());
}

@Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
    Log.d(TAG, "onChildRemoved:" + dataSnapshot.getKey());
}

@Override
public void onChildMoved(DataSnapshot dataSnapshot, String previousChildName) {
    Log.d(TAG, "onChildMoved:" + dataSnapshot.getKey());
}

@Override
public void onCancelled(DatabaseError databaseError) {
}

}; ref.addChildEventListener(childEventListener); Usining ChildEventListener onChildAdded are call per child you have in that node. Means your code runs 3 time so dialog appers 3 time.

This is the problem So the solution is:

While using a ChildEventListener is the recommended way to read lists of data, there are situations where attaching a ValueEventListener to a list reference is useful.

Attaching a ValueEventListener to a list of data will return the entire list of data as a single DataSnapshot, which you can then loop over to access individual children.

Even when there is only a single match for the query, the snapshot is still a list; it just contains a single item. To access the item, you need to loop over the result:

inaccessibleChild.addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
    for (DataSnapshot postSnapshot: dataSnapshot.getChildren()) {
       //here you can access the each child of that node.
    }
}

@Override
public void onCancelled(DatabaseError databaseError) {
    // Getting Post failed, log a message
    Log.w(TAG, "loadPost:onCancelled", databaseError.toException());
    // ...
}

});

Well, none of the above answers really helped, though Linxy's answer is correct, I saw it after solving the probelm.

Moving all the code out and writing just this line: alertDialog.show(); inside setOnClickListener() improving my code to this:

    query = mDatabase.child("child").child(anotherChild).child("yetAnotherChild");

    query.orderByChild("someChild").addChildEventListener(new ChildEventListener() {
                @Override
                public void onChildAdded(DataSnapshot dataSnapshot, String s) {
                    if (dataSnapshot != null) {

                        Map<String, String> newD = (Map<String, String>) dataSnapshot.getValue();

                        ArrayList<String> l = new ArrayList<String>();
                        l.add(newD.get("lol").substring(30));

                        String names[] = l.toArray(new String[0]);

                        AlertDialog.Builder alertDialog = new AlertDialog.Builder(Activity.this);
                        LayoutInflater inflater = getLayoutInflater();
                        View convertView = inflater.inflate(R.layout.dialog_list, null);
                        alertDialog.setView(convertView);
                        alertDialog.setTitle("title");
                        ListView lv = (ListView) convertView.findViewById(R.id.lv);
                        ArrayAdapter<String> adapter = new ArrayAdapter<String>(getBaseContext(), android.R.layout.simple_list_item_1, names);
                        lv.setAdapter(adapter);

                        alertDialog.setPositiveButton("OK", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialogInterface, int i) {

                            }
                        });
                    } else {
                        Toast.makeText(getBaseContext(), "NULLLLL", Toast.LENGTH_SHORT).show();
                    }
                }

                ...
                ...
            });

uProfile.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        alert11.show();
    }
});

Anyway, I would like to thank all those who answered this question.

Peace.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM