-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathFirebaseListAdapter.java
190 lines (161 loc) · 7.08 KB
/
FirebaseListAdapter.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
package com.example.itcapstone.burglarkitten;
/**
* Created by Rohan on 9/2/2015.
*/
import android.app.Activity;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import com.firebase.client.ChildEventListener;
import com.firebase.client.DataSnapshot;
import com.firebase.client.FirebaseError;
import com.firebase.client.Query;
import java.util.ArrayList;
import java.util.List;
/**
* @author greg
* @since 6/21/13
*
* This class is a generic way of backing an Android ListView with a Firebase location.
* It handles all of the child events at the given Firebase location. It marshals received data into the given
* class type. Extend this class and provide an implementation of <code>populateView</code>, which will be given an
* instance of your list item mLayout and an instance your class that holds your data. Simply populate the view however
* you like and this class will handle updating the list as the data changes.
*
* @param <T> The class type to use as a model for the data contained in the children of the given Firebase location
*/
public abstract class FirebaseListAdapter<T> extends BaseAdapter {
private Query mRef;
private Class<T> mModelClass;
private int mLayout;
private LayoutInflater mInflater;
private List<T> mModels;
private List<String> mKeys;
private ChildEventListener mListener;
/**
* @param mRef The Firebase location to watch for data changes. Can also be a slice of a location, using some
* combination of <code>limit()</code>, <code>startAt()</code>, and <code>endAt()</code>,
* @param mModelClass Firebase will marshall the data at a location into an instance of a class that you provide
* @param mLayout This is the mLayout used to represent a single list item. You will be responsible for populating an
* instance of the corresponding view with the data from an instance of mModelClass.
* @param activity The activity containing the ListView
*/
public FirebaseListAdapter(Query mRef, Class<T> mModelClass, int mLayout, Activity activity) {
this.mRef = mRef;
this.mModelClass = mModelClass;
this.mLayout = mLayout;
mInflater = activity.getLayoutInflater();
mModels = new ArrayList<T>();
mKeys = new ArrayList<String>();
// Look for all child events. We will then map them to our own internal ArrayList, which backs ListView
mListener = this.mRef.addChildEventListener(new ChildEventListener() {
@Override
public void onChildAdded(DataSnapshot dataSnapshot, String previousChildName) {
T model = dataSnapshot.getValue(FirebaseListAdapter.this.mModelClass);
String key = dataSnapshot.getKey();
// Insert into the correct location, based on previousChildName
if (previousChildName == null) {
mModels.add(0, model);
mKeys.add(0, key);
} else {
int previousIndex = mKeys.indexOf(previousChildName);
int nextIndex = previousIndex + 1;
if (nextIndex == mModels.size()) {
mModels.add(model);
mKeys.add(key);
} else {
mModels.add(nextIndex, model);
mKeys.add(nextIndex, key);
}
}
notifyDataSetChanged();
}
@Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
// One of the mModels changed. Replace it in our list and name mapping
String key = dataSnapshot.getKey();
T newModel = dataSnapshot.getValue(FirebaseListAdapter.this.mModelClass);
int index = mKeys.indexOf(key);
mModels.set(index, newModel);
notifyDataSetChanged();
}
@Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
// A model was removed from the list. Remove it from our list and the name mapping
String key = dataSnapshot.getKey();
int index = mKeys.indexOf(key);
mKeys.remove(index);
mModels.remove(index);
notifyDataSetChanged();
}
@Override
public void onChildMoved(DataSnapshot dataSnapshot, String previousChildName) {
// A model changed position in the list. Update our list accordingly
String key = dataSnapshot.getKey();
T newModel = dataSnapshot.getValue(FirebaseListAdapter.this.mModelClass);
int index = mKeys.indexOf(key);
mModels.remove(index);
mKeys.remove(index);
if (previousChildName == null) {
mModels.add(0, newModel);
mKeys.add(0, key);
} else {
int previousIndex = mKeys.indexOf(previousChildName);
int nextIndex = previousIndex + 1;
if (nextIndex == mModels.size()) {
mModels.add(newModel);
mKeys.add(key);
} else {
mModels.add(nextIndex, newModel);
mKeys.add(nextIndex, key);
}
}
notifyDataSetChanged();
}
@Override
public void onCancelled(FirebaseError firebaseError) {
Log.e("FirebaseListAdapter", "Listen was cancelled, no more updates will occur");
}
});
}
public void cleanup() {
// We're being destroyed, let go of our mListener and forget about all of the mModels
mRef.removeEventListener(mListener);
mModels.clear();
mKeys.clear();
}
@Override
public int getCount() {
return mModels.size();
}
@Override
public Object getItem(int i) {
return mModels.get(i);
}
@Override
public long getItemId(int i) {
return i;
}
@Override
public View getView(int i, View view, ViewGroup viewGroup) {
if (view == null) {
view = mInflater.inflate(mLayout, viewGroup, false);
}
T model = mModels.get(i);
// Call out to subclass to marshall this model into the provided view
populateView(view, model);
return view;
}
/**
* Each time the data at the given Firebase location changes, this method will be called for each item that needs
* to be displayed. The arguments correspond to the mLayout and mModelClass given to the constructor of this class.
* <p/>
* Your implementation should populate the view using the data contained in the model.
*
* @param v The view to populate
* @param model The object containing the data used to populate the view
*/
protected abstract void populateView(View v, T model);
}