使用Firebase的ListView和自定义适配器
在我的应用程序中,我有一个部分(片段),我想通过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)
直到现在出现此错误:
- 第一次我尝试添加植物时,我删除了我厂从DB(背景不为空,但加入后植物是)
- ,当我去到另一个片段通过底部的工具栏,然后我回到这个片段添加植物
- 添加许多植物时(很少和不那么肯定)
这里我了最新代码:
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;
}
适配器是和以前一样。
我希望有人能帮助我。
数据检索在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。
这更像是一个问题而不是答案。 (它更容易在这里添加代码)。
在你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);
但是,我不设置适配器plantList
或quantityList
之前看到您填写个人的ArrayList?
我解决了我的问题,也感谢你。现在只剩下一个问题,请看看,如果可以的话,我会很感激。 – Benny