0

在我的應用程序中,我有一個部分(片段),我想通過ListView顯示植物及其數量(存儲在Firebase上)。 通過使用浮動操作按鈕,我允許更新與工廠相關的數量,如果它已經在數據庫中,否則添加它。使用Firebase的ListView和自定義適配器

這裏我的頭兩個問題:

#1 此片段顯示在主要活動,因此在啓動時,我想從數據庫中檢索到的數據被立即顯示出來,但是這並未」不會發生,只有在添加/更新工廠後纔會顯示。

#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:致命異常:主 工藝:.veggarden,PID:19148 顯示java.lang.NullPointerException :嘗試在 .veggarden.MyVegGardenAdapter上一個空 對象引用 調用虛擬方法 'java.lang.Object中 android.content.Context.getSystemService(java.lang.String中)'(MyVegGardenAdapter.java:31) at .veggarden.MyVegGardenFragment $ 1.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 $ 1.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) atcom.android.internal.os.ZygoteInit.main(ZygoteInit。)。com.android.internal.os.ZygoteInit $ MethodAndArgsCaller.run(ZygoteInit.java:866) com.android.internal.os.ZygoteInit.main(ZygoteInit。Java的:756)

直到現在出現此錯誤:

  1. 第一次我嘗試添加植物時,我刪除了我廠從DB(背景不爲空,但加入後植物是)
  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; 
    } 

適配器是和以前一樣。

我希望有人能幫助我。

+0

我解決了我的問題,也感謝你。現在只剩下一個問題,請看看,如果可以的話,我會很感激。 – Benny

回答

1

數據檢索在Firebase中是異步的,因此您的列表不會立即填充。您可以考慮顯示一個ProgressBar或一個簡單的TextView,其文本類似於「Loading ...」,直到檢索到數據。

另外,您在每次數據更改時調用您提供給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。

+0

我解決了將適配器放在數據庫回調中的第一個問題,而我從你的想法開始解決了第二個問題。我用我的最後一個問題更新了我的帖子,請看看是否可以,謝謝你的幫助! – Benny

+0

哪一行是適配器中的第31行? – Mehmed

+0

對不起,我編輯過。無論如何它是:inflater =(LayoutInflater)this.context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); – Benny

0

這更像是一個問題而不是答案。 (它更容易在這裏添加代碼)。

在你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); 

但是,我不設置適配器plantListquantityList之前看到您填寫個人的ArrayList?

+0

我從firebase中提取hashmap,然後用鍵和值填充plantsList和quantityList。在代碼中你可以找到這個部分: for(Map.Entry entry:myVegGarden.entrySet())plantsList.add(entry.getKey()); quantityList.add(entry.getValue()); – Benny

+0

是的,但在設置適配器之前我沒有看到你這樣做。你在添加/更新之後做,但不是之前。 – Barns

+0

你說得對,我的第一個問題是由它引起的,謝謝! – Benny

相關問題