使用 Firebase 的 ListView 和自定义适配器

ListView and custom Adapter using Firebase

在我的应用程序中,我有一个部分(片段),我想在其中通过 ListView 显示植物及其数量(存储在 Firebase 上)。 通过使用浮动操作按钮,我允许更新与工厂相关的数量(如果它已经在数据库中),否则添加它。

这是我的前两个问题:

#1 此片段显示在主 activity 中,因此在启动时我希望立即显示从数据库检索的数据,但这并没有发生,它们仅在 adding/updating 之后显示植物.

#2 这是我的代码:

public class MyVegGardenFragment extends Fragment {

    private View v;
    private ListView listView;
    private DatabaseReference database;
    private FirebaseAuth auth;
    private FirebaseUser us;
    private Map<String, Integer> myVegGarden;
    private ArrayList<String> plantsList = new ArrayList<>();
    private ArrayList<Integer> quantityList = new ArrayList<>();
    private MyVegGardenAdapter adapter;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {


        myVegGarden = new HashMap<>();

        database = FirebaseDatabase.getInstance().getReference();
        auth = FirebaseAuth.getInstance();
        us = auth.getCurrentUser();

        database.addValueEventListener(new ValueEventListener() {
            @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {

                User u = dataSnapshot.child("users").child(us.getUid()).getValue(User.class);
                if (u != null) {
                    myVegGarden = u.getMyVegGarden();
                    if (myVegGarden != null) {
                        for(Map.Entry<String, Integer> entry : myVegGarden.entrySet()) {
                            plantsList.add(entry.getKey());
                            quantityList.add(entry.getValue());
                        }
                    }
                }
            }

            @Override
            public void onCancelled(DatabaseError databaseError) {
            }
        });


        v = inflater.inflate(R.layout.myveggarden_fragment_layout, container, false);
        listView = (ListView) v.findViewById(R.id.myVegGarden_list_view);
        adapter = new MyVegGardenAdapter(getActivity(), plantsList, quantityList);
        listView.setAdapter(adapter);

        FloatingActionButton fab = (FloatingActionButton) v.findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                AlertDialog.Builder builder;
                // [...] leaving code for AlertDialog working correctly
                builder.setPositiveButton("PLANT", new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int which) {
                        final String plant = options[plant_picker.getValue()];
                        final int quantity = quantity_picker.getValue();


                        if(!adapter.getPlantsList().contains(plant)){
                            adapter.add(plant, quantity);
                        }
                        else {
                            adapter.updateIfPresent(plant, quantity);
                        }

                        //update db value
                        database.addValueEventListener(new ValueEventListener() {
                            @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
                            @Override
                            public void onDataChange(DataSnapshot dataSnapshot) {

                                User u = dataSnapshot.child("users").child(us.getUid()).getValue(User.class);
                                if (u != null) {
                                    myVegGarden = u.getMyVegGarden();
                                    if (myVegGarden != null) {
                                        myVegGarden.put(plant, quantity);
                                    }
                                    else {
                                        myVegGarden = new HashMap<>();
                                        myVegGarden.put(plant, quantity);
                                        }
                                    database.child("users").child(us.getUid()).child("myVegGarden").setValue(myVegGarden);
                                    }
                            }

                            @Override
                            public void onCancelled(DatabaseError databaseError) {
                            }
                        });
                       dialog.dismiss();
                       }
                });

                builder.setNegativeButton("CANCEL", new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.dismiss();
                    } });

                AlertDialog dialog = builder.create();
                dialog.show();
            }
        });

        return v;
    }

}
public class MyVegGardenAdapter extends BaseAdapter {

    private Context context;
    private LayoutInflater inflater;
    private ArrayList<String> dataSource;
    private ArrayList<Integer> quantity;

    public MyVegGardenAdapter(Context context, ArrayList<String> items, ArrayList<Integer> quantity) {
        this.context = context;
        dataSource = items;
        this.quantity = quantity;
        //line 31
        inflater = (LayoutInflater) this.context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 
    }

    public void add(String plant, Integer qty) {
        dataSource.add(plant);
        quantity.add(qty);
        notifyDataSetChanged();
    }

    public void updateIfPresent(String plant, Integer qty) {
        quantity.set(dataSource.indexOf(plant), qty);
        notifyDataSetChanged();
    }

    public List<String> getPlantsList() { return dataSource; }

    @Override
    public int getCount() {
        return dataSource.size();
    }

    @Override
    public Object getItem(int position) {
        return dataSource.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    public Object getQuantity(int position) {
        return quantity.get(position);
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        // Get view for row item
        View rowView = inflater.inflate(R.layout.listview_row, parent, false);

        // Get title element
        TextView firstLine = (TextView) rowView.findViewById(R.id.firstLine);
        // Get subtitle element
        TextView secondLine = (TextView) rowView.findViewById(R.id.secondLine);
        // Get icon element
        ImageView icon = (ImageView) rowView.findViewById(R.id.list_icon);

        String plant = (String) getItem(position);
        Integer quantity = (Integer) getQuantity(position);

        firstLine.setText(plant);
        secondLine.setText(Html.fromHtml("<b>Quantity: </b>" + quantity));

        switch(plant) {
            case "Bean":
                icon.setImageResource(R.drawable.ic_bean_48dp); break;
            case "Cabbage":
                icon.setImageResource(R.drawable.ic_cabbage_48dp); break;
            case "Carrot":
                icon.setImageResource(R.drawable.ic_carrot_48dp); break;
            case "Cucumber":
                icon.setImageResource(R.drawable.ic_cucumber_48dp); break;
            case "Eggplant":
                icon.setImageResource(R.drawable.ic_eggplant_48dp); break;
            case "Lettuce":
                icon.setImageResource(R.drawable.ic_lettuce_48dp); break;
            case "Pea":
                icon.setImageResource(R.drawable.ic_pea_48dp); break;
            case "Pepper":
                icon.setImageResource(R.drawable.ic_pepper_48dp); break;
            case "Radish":
                icon.setImageResource(R.drawable.ic_radish_48dp); break;
            case "Tomato":
                icon.setImageResource(R.drawable.ic_tomato_48dp); break;
        }
        return rowView;
    }
}

嗯...数据库总是正确更新,但是:有时 plantsList 和 quantityLists 插入副本,所以植物出现两次或三次,我不想要它。

编辑: 我成功地解决了这两个问题。 现在只有以下问题。经常发生此错误:

10-19 19:28:52.460 19148-19148/.veggarden E/AndroidRuntime: FATAL EXCEPTION: main Process: .veggarden, PID: 19148 java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.Object android.content.Context.getSystemService(java.lang.String)' on a null object reference at .veggarden.MyVegGardenAdapter.(MyVegGardenAdapter.java:31) at .veggarden.MyVegGardenFragment.onDataChange(MyVegGardenFragment.java:85) at com.google.android.gms.internal.zzbmz.zza(Unknown Source) at com.google.android.gms.internal.zzbnz.zzYj(Unknown Source) at com.google.android.gms.internal.zzboc.run(Unknown Source) at android.os.Handler.handleCallback(Handler.java:751) at android.os.Handler.dispatchMessage(Handler.java:95) at android.os.Looper.loop(Looper.java:154) at android.app.ActivityThread.main(ActivityThread.java:6077) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:866) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:756)

直到现在出现这个错误:

  1. 第一次 当我从数据库中删除我的植物时,我尝试添加植物(上下文不为空,但添加植物后它是)
  2. 当我通过底部工具栏转到另一个片段然后返回到该片段以添加植物时
  3. 当添加很多植物时(很少而且不太确定)

这是我的最新代码:

public class MyVegGardenFragment extends Fragment {

    private View v;
    private ListView listView;
    private DatabaseReference database;
    private FirebaseAuth auth;
    private FirebaseUser us;
    private Map<String, Integer> myVegGarden;
    private ArrayList<String> plantsList = new ArrayList<>();
    private ArrayList<Integer> quantityList = new ArrayList<>();
    private MyVegGardenAdapter adapter;


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {


        myVegGarden = new HashMap<>();

        database = FirebaseDatabase.getInstance().getReference();
        auth = FirebaseAuth.getInstance();
        us = auth.getCurrentUser();

        v = inflater.inflate(R.layout.myveggarden_fragment_layout, container, false);
        listView = (ListView) v.findViewById(R.id.myVegGarden_list_view);

        database.addValueEventListener(new ValueEventListener() {
            @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {

                plantsList = new ArrayList<String>();
                quantityList = new ArrayList<Integer>();

                User u = dataSnapshot.child("users").child(us.getUid()).getValue(User.class);
                if (u != null) {
                    myVegGarden = u.getMyVegGarden();
                    if (myVegGarden != null) {
                        for(Map.Entry<String, Integer> entry : myVegGarden.entrySet()) {
                            plantsList.add(entry.getKey());
                            quantityList.add(entry.getValue());
                        }
                    }
                    **line 85**
                    adapter = new MyVegGardenAdapter(getActivity(), plantsList, quantityList); 
                    listView.setAdapter(adapter);
                }
            }

            @Override
            public void onCancelled(DatabaseError databaseError) {
            }
        });


        FloatingActionButton fab = (FloatingActionButton) v.findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                AlertDialog.Builder builder;
                // [...] leaving code for AlertDialog working correctly
                builder.setPositiveButton("PLANT", new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int which) {
                        final String plant = options[plant_picker.getValue()];
                        final int quantity = quantity_picker.getValue();

                        if(!adapter.getPlantsList().contains(plant)){
                            adapter.add(plant, quantity);
                        }
                        else {
                            adapter.updateIfPresent(plant, quantity);
                        }

                        if (myVegGarden != null) {
                            myVegGarden.put(plant, quantity);
                            database.child("users").child(us.getUid()).child("myVegGarden").setValue(myVegGarden);
                        }

                        else {
                            myVegGarden = new HashMap<String, Integer>();
                            myVegGarden.put(plant, quantity);
                            database.child("users").child(us.getUid()).child("myVegGarden").setValue(myVegGarden);
                        }

                        dialog.dismiss();
                    }
                });

                builder.setNegativeButton("CANCEL", new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.dismiss();
                    } });

                AlertDialog dialog = builder.create();
                dialog.show();
            }
        });

        return v;
    }

适配器和以前一样。

希望有人能帮助我。

这更像是对您的代码提出的问题,而不是答案。 (这里加代码更方便)

在你的 onCreateView 方法中你有这个:

v = inflater.inflate(R.layout.myveggarden_fragment_layout, container, false);
        listView = (ListView) v.findViewById(R.id.myVegGarden_list_view);
        adapter = new MyVegGardenAdapter(getActivity(), plantsList, quantityList);
        listView.setAdapter(adapter);

但是,在设置适配器之前,我没有看到您在哪里填充单独的 ArrayList plantListquantityList

Firebase 中的数据检索是异步的,因此您的列表不会立即填充。您可以考虑显示 ProgressBar 或带有类似于 "Loading..." 文本的简单 TextView,直到检索到数据。

此外,您提供给 addValueEventListener() 的侦听器在每次数据更改时都会被调用。

对于第二个问题,我认为你应该做两个调整。第一个是更新 onCreateView() 中的顶部 addValueEventListener():

database.addValueEventListener(new ValueEventListener() {
            @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {

                User u = dataSnapshot.child("users").child(us.getUid()).getValue(User.class);
                if (u != null) {
                    myVegGarden = u.getMyVegGarden();
                    if (myVegGarden != null) {
                        plantsList = new ArrayList<>();
                        quantityList = new ArrayList<>();
                        for(Map.Entry<String, Integer> entry : myVegGarden.entrySet()) {
                            plantsList.add(entry.getKey());
                            quantityList.add(entry.getValue());
                        }
                        adapter.notifyDataSetChanged();
                    }
                }
            }

            @Override
            public void onCancelled(DatabaseError databaseError) {
            }
        });

第二个调整是简化 AlertDialog 的 onClick() 如下:

@Override
public void onClick(DialogInterface dialog, int which) {
    final String plant = options[plant_picker.getValue()];
    final int quantity = quantity_picker.getValue();

    // Update db value
    myVegGarden = new HashMap<>();
    myVegGarden.put(plant, quantity);
    database.child("users").child(us.getUid()).child("myVegGarden").setValue(myVegGarden);
}

这应该足够了。使用植物和数量创建地图,如果那里不存在该对,setValue() 会将其添加到路径中。如果该对存在于该路径中,setValue() 将更新它。此更新将依次调用调整后的 addValueEventListener(),更新 plantList 和 quantityList。