ArrayList add() 有时不会将从 Firebase 获取的元素添加到 ArrayList

Posted

技术标签:

【中文标题】ArrayList add() 有时不会将从 Firebase 获取的元素添加到 ArrayList【英文标题】:ArrayList add() sometimes does not add element obtained from Firebase to ArrayList 【发布时间】:2020-09-27 00:20:15 【问题描述】:

所以,我正在尝试创建一个 listView 来显示已设置约会的房间。问题是在某些情况下,此类房间和约会日期不会添加到 ArrayLists 中。我必须不断地打开和关闭活动才能获得确实添加并显示在 ListView 中的情况。

// Global variables
ArrayList<Room> rooms = new ArrayList<>();
ArrayList<String> dates = new ArrayList<>();
private DatabaseReference appointmentsRef;

@Override
protected void onCreate(Bundle savedInstanceState) 
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_history);
    // Other stuff
    appointmentsRef= FirebaseDatabase.getInstance().getReference();

    appointmentsRef.child("appointment").addValueEventListener(new ValueEventListener() 
        @Override
        public void onDataChange(@NonNull DataSnapshot dataSnapshot) 
            for(final DataSnapshot snapshot : dataSnapshot.getChildren())
                final Appointment cita = snapshot.getValue(Appointment.class);
                FirebaseDatabase.getInstance().getReference().child("room").addValueEventListener(new ValueEventListener() 
                    @Override
                    public void onDataChange(@NonNull DataSnapshot dS) 
                        for(DataSnapshot ss : dS.getChildren())
                            Room room = ss.getValue(Room.class);
                            if(room.getId().equals(cita.getRoomID()))
                                Log.e("Pass", "Room " + room.getId() + " in appointment " + cita.getId() + " rented room " + cita.getRoomID());
                                rooms.add(room);
                                dates.add(cita.getDate());
                            
                        
                    
                    @Override
                    public void onCancelled(@NonNull DatabaseError databaseError) 
                        Log.e("Canceled","Cancelled  appointment loop");
                    
                );
            
            Log.e("Rooms", rooms.toString());
            Log.e("Dates", dates.toString());
        



        @Override
        public void onCancelled(@NonNull DatabaseError databaseError) 
            Log.e("Canceled","Cancelled room loop");
        


代码总是通过 if 条件,输出如下所示,但 ArrayLists 在日志文件中显示为空。

E/Rooms: []
E/Dates: []

E/Pass: Room 1b845370-39d0-4e35-9e15-b9eaf6d556b5 in appointment 1803b7a8-7cfc-4c43-8bad-573868d174f5 rented room 1b845370-39d0-4e35-9e15-b9eaf6d556b5
E/Pass: Room 6640904b-edc6-4b1e-b636-a68cb72241ad in appointment a206e8f5-76e3-40cc-91de-e64a976691e9 rented room 6640904b-edc6-4b1e-b636-a68cb72241ad

更新:我移动了在 if 语句后打印 ArrayLists 的 Log.e 作为回答。事实上,ArrayLists 从来都不是空的,那么为什么这些房间和日期没有显示在活动的列表视图中呢?

// Inside onCreate before appointmentsRef
ListView lv = findViewById(R.id.listViewHistory);
// after appointmentsRef
HistoryAdapter adapter = new HistoryAdapter(this, R.layout.history_adapter_view,rooms,dates);
lv.setAdapter(adapter);

历史适配器:

public class HistoryAdapter extends ArrayAdapter<Room> 
private static final String TAG = "HistoryAdapter";
private Context mContext;
private int mResource;
//ViewHolder object
ViewHolder holder;
ArrayList<String> appointments;

private static class ViewHolder 
    TextView title;
    TextView location;
    TextView price;
    TextView date;
    ImageView image;


public HistoryAdapter(Context context, int resource, ArrayList<Room> objects,ArrayList<String> dates) 
    super(context, resource, objects);
    mContext = context;
    mResource = resource;
    this.appointments = dates;


@NonNull
public View getView(int position, View convertView, ViewGroup parent) 

    //sets up the image loader library
    setupImageLoader();

    //get the Appointment information
    // String currentUser = ""+ FirebaseAuth.getInstance().getCurrentUser().getUid();
    //String day = "placeholder";
    String day = appointments.get(position);
    String title = getItem(position).getTitle();
    String location = getItem(position).getLocation();
    String price = "$"+getItem(position).getPrice();
    String imgUrl = getItem(position).getPhoto();

    Log.e("Position","Posición : "+ position);
    //create the view result for showing the animation
    final View result;




    if(convertView == null)
        LayoutInflater inflater = LayoutInflater.from(mContext);
        convertView = inflater.inflate(mResource, parent, false);
        holder= new HistoryAdapter.ViewHolder();
        holder.title = convertView.findViewById(R.id.tvTitle);
        holder.location = convertView.findViewById(R.id.tvLocation);
        holder.price = convertView.findViewById(R.id.tvPrice);
        holder.date = convertView.findViewById(R.id.tvDate);
        holder.image = convertView.findViewById(R.id.image);

        result = convertView;

        convertView.setTag(holder);
    
    else
        holder = (HistoryAdapter.ViewHolder) convertView.getTag();
        result = convertView;
    


    // Animation animation = AnimationUtils.loadAnimation(mContext,
    //        (position > lastPosition) ? R.anim.load_down_anim : R.anim.load_up_anim);
    //result.startAnimation(animation);

    holder.title.setText(title);
    holder.location.setText(location);
    holder.price.setText(price);
    holder.date.setText(day);

    //create the imageloader object
    ImageLoader imageLoader = ImageLoader.getInstance();
    ;
    int defaultImage = mContext.getResources().getIdentifier("@drawable/logo_size",null,mContext.getPackageName());

    //create display options
    DisplayImageOptions options = new DisplayImageOptions.Builder().cacheInMemory(true)
            .cacheOnDisc(true).resetViewBeforeLoading(true)
            .showImageForEmptyUri(defaultImage)
            .showImageOnFail(defaultImage)
            .showImageOnLoading(defaultImage).build();

    //download and display image from url
    imageLoader.displayImage(imgUrl, holder.image, options);

    return convertView;


/**
 * Required for setting up the Universal Image loader Library
 */
private void setupImageLoader()
    // UNIVERSAL IMAGE LOADER SETUP
    DisplayImageOptions defaultOptions = new DisplayImageOptions.Builder()
            .cacheOnDisc(true).cacheInMemory(true)
            .imageScaleType(ImageScaleType.EXACTLY)
            .displayer(new FadeInBitmapDisplayer(300)).build();

    ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(
            mContext)
            .defaultDisplayImageOptions(defaultOptions)
            .memoryCache(new WeakMemoryCache())
            .discCacheSize(100 * 1024 * 1024).build();

    ImageLoader.getInstance().init(config);
    // END - UNIVERSAL IMAGE LOADER SETUP


【问题讨论】:

您正在通过检查 if 条件“if(room.getId().equals(cita.getRoomID()))”来添加元素,您应该调试代码并在通过条件后检查您是否元素是否添加。我确信原因是你的条件。 好吧,你们对 Log.e 显示 emtpy arrayList 是正确的,因为它是异步更新的。我在 if 之后移动了它们,现在它显示确实有房间和日期添加到 ArrayList 中,那么为什么有时这些 ArrayLists 中的值没有显示在 Activity 的 ListView 中?如果我应该添加额外的代码块,请现在告诉我。 【参考方案1】:

您的问题是您的 if 语句在 onDataChanged 方法中运行,该方法是异步的,并且将在不同的线程中运行。

换句话说,您将首先addValueEventListener,然后代码将立即移至下一行代码,您将在其中记录列表。

如果您将日志移动到内部onDataChanged() 内的if 语句之后,您将看到它按预期工作。

【讨论】:

确实是这样,谢谢大家。至于为什么 ListView 是空的,那是因为 adapter.notifyDataSetChanged();需要在向 ArrayLists 添加值后调用。 我正忙着写一个解释这个的答案!很高兴你解决了它:)【参考方案2】:

您正在子“房间”上添加 ValueEventListener ,然后调用 Log.e("Rooms", rooms.toString());Log.e("Dates", dates.toString()); ,但是将来更改数据时将调用以下函数。这就是房间和日期都是空的原因。

               @Override
                    public void onDataChange(@NonNull DataSnapshot dS) 
                        for(DataSnapshot ss : dS.getChildren())
                            Room room = ss.getValue(Room.class);
                            if(room.getId().equals(cita.getRoomID()))
                                Log.e("Pass", "Room " + room.getId() + " in appointment " + cita.getId() + " rented room " + cita.getRoomID());
                                rooms.add(room);
                                dates.add(cita.getDate());
                            
                        

                       Log.e("Rooms", rooms.toString());
                       Log.e("Dates", dates.toString());
                    

修改如下函数,然后查看输出:

               @Override
                    public void onDataChange(@NonNull DataSnapshot dS) 
                        for(DataSnapshot ss : dS.getChildren())
                            Room room = ss.getValue(Room.class);
                            if(room.getId().equals(cita.getRoomID()))
                                Log.e("Pass", "Room " + room.getId() + " in appointment " + cita.getId() + " rented room " + cita.getRoomID());
                                rooms.add(room);
                                dates.add(cita.getDate());
                            
                        

                       Log.e("Rooms", rooms.toString());
                       Log.e("Dates", dates.toString());
                    

【讨论】:

【参考方案3】:

ArrayList 是异步更新的。即使记录其内容的代码低于更新它们的代码,它实际上是首先运行的,而它们仍然是空的。如果您需要在更新后使用它们,请将您的代码放在回调中,如下所示:

for(DataSnapshot ss : dS.getChildren())
    Room room = ss.getValue(Room.class);
    if (room.getId().equals(cita.getRoomID())) 
      Log.e("Pass", "Room " + room.getId() + " in appointment " + cita.getId() + " 
      rented room " + cita.getRoomID());

      rooms.add(room);
      dates.add(cita.getDate());
    
    // at this point rooms and dates have been populated. place code that needs
    // to work with these populated ArrayLists here.

【讨论】:

以上是关于ArrayList add() 有时不会将从 Firebase 获取的元素添加到 ArrayList的主要内容,如果未能解决你的问题,请参考以下文章

ArrayList 从源码角度剖析底层原理

java arraylist越界问题

ArrayList 使用 forEach 遍历时删除元素会报错吗?

java基础之ArrayList 和VectorCopyOnWriteArrayList

java基础之ArrayList 和VectorCopyOnWriteArrayList

一个arraylist,往里面add值的时候,遇到重复就不要添加,怎么做