我想填充我的回收者视图,以便我可以看到附近的人/地方。我使用GeoFire查询我的数据库,看起来像这样。
GeoQuery geoQuery = geoFire.queryAtLocation(new GeoLocation(latLngCenter.latitude, latLngCenter.longitude), 0.1);
geoQuery.addGeoQueryEventListener(new GeoQueryEventListener() {
@Override
public void onKeyEntered(String key, GeoLocation location) {
System.out.println(String.format("Key %s entered the search area at [%f,%f]", key, location.latitude, location.longitude));
Log.e("TAG", key + location.latitude + location.longitude);
}
@Override
public void onKeyExited(String key) {
System.out.println(String.format("Key %s is no longer in the search area", key));
}
@Override
public void onKeyMoved(String key, GeoLocation location) {
System.out.println(String.format("Key %s moved within the search area to [%f,%f]", key, location.latitude, location.longitude));
Log.e("TAG", key + location.latitude + location.longitude);
}
@Override
public void onGeoQueryReady() {
System.out.println("All initial data has been loaded and events have been fired!");
}
@Override
public void onGeoQueryError(DatabaseError error) {
System.err.println("There was an error with this query: " + error);
}
});
我正在使用此Firebase RecyclerView
RecyclerView recycler = (RecyclerView) findViewById(R.id.RecyclerView);
recycler.setHasFixedSize(true);
recycler.setLayoutManager(new LinearLayoutManager(this));
FirebaseRecyclerAdapter<Chat, ChatHolder> mAdapter = new FirebaseRecyclerAdapter<Chat, ChatHolder>(Chat.class, R.layout.recyclerview, ChatHolder.class, mUsers) {
@Override
public void populateViewHolder(final ChatHolder chatMessageViewHolder, final Chat chatMessage, int position) {
chatMessageViewHolder.setName(chatMessage.getName());
chatMessageViewHolder.setText(chatMessage.getText());
chatMessageViewHolder.setTimestamp(chatMessage.getTimestamp());
}
};
recycler.setAdapter(mAdapter);
使用这些Chat Holder类和聊天对象类
public static class ChatHolder extends RecyclerView.ViewHolder {
View mView;
public ChatHolder(View itemView) {
super(itemView);
mView = itemView;
}
public void setName(String name) {
TextView field = (TextView) mView.findViewById(R.id.textViewName);
field.setText(name);
}
public void setText(String text) {
TextView field = (TextView) mView.findViewById(R.id.textViewMessage);
field.setText(text);
}
public void setTimestamp(String text) {
TextView field = (TextView) mView.findViewById(R.id.textViewTime);
field.setText(text);
}
}
public static class Chat {
String name;
String text;
String uid;
String timestamp;
public Chat() {
}
public Chat(String name, String uid, String message, String timestamp) {
this.name = name;
this.text = message;
this.uid = uid;
this.timestamp = timestamp;
}
public String getName() {
return name;
}
public String getUid() {
return uid;
}
public String getText() {
return text;
}
public String getTimestamp() {
return timestamp;
}
}
目前,FirebaseUI库中提供的适配器填充了recyclerview,因此只使用了一个引用,并且所有子事件都显示在视图中, 现在,我想填充我的recyclerView,这样当一个密钥进入时,它会根据我的key =来填充我的recyclerview,这是我的firebase数据库的外观my firebase database
答案 0 :(得分:1)
将您从geofire检索到的所有数据(也是uid
)推送到新节点以将地理位置结果存储到firebase中会更简单,例如
my-node //your new firebase node to store all uids retrieved from geofire
- {chatUid}: true //true is just a dummy data, you can use anything else except null and empty string.
- {chatUid}: true
- ...
并将FirebaseRecyclerAdapter
ref设置为该节点。
DatabaseReference ref = FirebaseDatabase.getInstance().getReference("my-node");
FirebaseRecyclerAdapter fra = new FirebaseRecyclerAdapter<Boolean, MyVH>(Boolean.class, R.layout.my_layout, MyVH.class, ref) { ... }
然后,在populateViewHolder()
实现的FirebaseRecyclerAdapter
方法中,您可以使用String键从包含数据的主节点获取数据。
public void populateViewHolder(MyVH viewHolder, Boolean model, int position){
// Get references of child views
final TextView nameTextView = viewHolder.getItemView().findViewById(R.id.my_name_text_view_in_vh_layout);
final TextView addressTextView = viewHolder.getItemView().findViewById(R.id.my_address_text_view_in_vh_layout);
// Key in this position.
String key = getRef(position).key;
// Query the full data of the current key located in the `main-data-node`
FirebaseDatabase.getInstance().getReference("main-data-node").child(key).addValueEventListener(new ValueEventListener(){
... //Truncated onCancelled
@Override
public void onDataChange(snap: DataSnapshot){
MyDataModel model = snap.getValue(MyDataModel.class);
nameTextView = model.getName();
addressTextView = model.getAddress();
}
}
}
// The data model class
public class MyDataModel {
private String name;
private String address;
... // Truncated getter, setter, constructor
}
对于地理位置结果的任何更改,只需将结果推送到my-node
,它就会自动通知您的FirebaseRecyclerAdapter
。
P.S。我懒得把我的解决方案与你的数据模型相匹配(对不起),所以我做了一个简化的示例类,所以如果有人偶然发现这个问题,他们可以更容易理解。
P.S.S。我已经有一段时间了,因为我在java中编码,我主要使用kotlin(你很快就应该大声笑),所以,如果有一些语法错误,请随时编辑。
答案 1 :(得分:0)
这是Emanuelet的自定义FirebaseListAdapter,我是由FirebaseRecyclerAdapter创建的。
public abstract class FirebaseRecyclerAdapter<T, VH extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<VH > implements Filterable {
private static final String LOG_TAG = "FirebaseListAdapter";
private Query mRef;
private Class<T> mModelClass;
private int mLayout;
private LayoutInflater mInflater;
protected Class<VH> mViewHolderClass;
private List<T> mModels;
private List<T> mFilteredModels;
private List<String> mKeys = new ArrayList<>();
private Map<String, T> mModelKeys;
private Map<String, T> mFilteredKeys;
private ChildEventListener mListener;
private FirebaseRecyclerAdapter.ValueFilter valueFilter;
/**
* @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 FirebaseRecyclerAdapter(Query mRef, Class<T> mModelClass, int mLayout, Activity activity, Class<VH> viewHolderClass) {
this.mRef = mRef;
this.mModelClass = mModelClass;
this.mLayout = mLayout;
this.mViewHolderClass = viewHolderClass;
mInflater = activity.getLayoutInflater();
mModels = new ArrayList<>();
mModelKeys = new HashMap<>();
// 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(FirebaseRecyclerAdapter.this.mModelClass);
mModelKeys.put(dataSnapshot.getKey(), model);
// Insert into the correct location, based on previousChildName
if (previousChildName == null) {
mModels.add(0, model);
} else {
T previousModel = mModelKeys.get(previousChildName);
int previousIndex = mModels.indexOf(previousModel);
int nextIndex = previousIndex + 1;
if (nextIndex == mModels.size()) {
mModels.add(model);
mKeys.add(dataSnapshot.getKey());
} else {
mModels.add(nextIndex, model);
mKeys.add(dataSnapshot.getKey());
}
}
notifyDataSetChanged();
}
@Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
Log.d(LOG_TAG, "onChildChanged");
// One of the mModels changed. Replace it in our list and name mapping
String modelName = dataSnapshot.getKey();
T oldModel = mModelKeys.get(modelName);
T newModel = dataSnapshot.getValue(FirebaseRecyclerAdapter.this.mModelClass);
int index = mModels.indexOf(oldModel);
mModels.set(index, newModel);
mModelKeys.put(modelName, newModel);
notifyDataSetChanged();
}
@Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
Log.d(LOG_TAG, "onChildRemoved");
// A model was removed from the list. Remove it from our list and the name mapping
String modelName = dataSnapshot.getKey();
T oldModel = mModelKeys.get(modelName);
mModels.remove(oldModel);
mKeys.remove(dataSnapshot.getKey());
mModelKeys.remove(modelName);
notifyDataSetChanged();
}
@Override
public void onChildMoved(DataSnapshot dataSnapshot, String previousChildName) {
Log.d(LOG_TAG, "onChildMoved");
// A model changed position in the list. Update our list accordingly
String modelName = dataSnapshot.getKey();
T oldModel = mModelKeys.get(modelName);
T newModel = dataSnapshot.getValue(FirebaseRecyclerAdapter.this.mModelClass);
int index = mModels.indexOf(oldModel);
mModels.remove(index);
if (previousChildName == null) {
mModels.add(0, newModel);
mKeys.add(dataSnapshot.getKey());
} else {
T previousModel = mModelKeys.get(previousChildName);
int previousIndex = mModels.indexOf(previousModel);
int nextIndex = previousIndex + 1;
if (nextIndex == mModels.size()) {
mModels.add(newModel);
mKeys.add(dataSnapshot.getKey());
} else {
mModels.add(nextIndex, newModel);
mKeys.add(dataSnapshot.getKey());
}
}
notifyDataSetChanged();
}
@Override
public void onCancelled(DatabaseError error) {
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();
mModelKeys.clear();
mKeys.clear();
}
@Override
public int getItemCount() {
return mModels.size();
}
public T getItem(int position) {
return mModels.get(position);
}
@Override
public void onBindViewHolder(VH holder, int position) {
T model = getItem(position);
populateViewHolder(holder, model, position, mKeys);
}
@Override
public long getItemId(int i) {
return i;
}
@Override
public int getItemViewType(int position) {
return mLayout;
}
public void remove(String key) {
T oldModel = mModelKeys.get(key);
mModels.remove(oldModel);
mKeys.remove(key);
mModelKeys.remove(key);
notifyDataSetChanged();
}
@Override
public VH onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(viewType, parent, false);
try {
Constructor<VH> constructor = mViewHolderClass.getConstructor(View.class);
return constructor.newInstance(view);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
/**
* 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 viewHolder The view to populate
* @param model The object containing the data used to populate the view
*/
protected abstract void populateViewHolder(VH viewHolder, T model, int position, List<String> mKeys);
public void addSingle(DataSnapshot snapshot) {
T model = snapshot.getValue(FirebaseRecyclerAdapter.this.mModelClass);
mModelKeys.put(snapshot.getKey(), model);
mModels.add(model);
mKeys.add(snapshot.getKey());
notifyDataSetChanged();
}
public void update(DataSnapshot snapshot, String key) {
T oldModel = mModelKeys.get(key);
T newModel = snapshot.getValue(FirebaseRecyclerAdapter.this.mModelClass);
int index = mModels.indexOf(oldModel);
if (index >= 0) {
mModels.set(index, newModel);
mModelKeys.put(key, newModel);
notifyDataSetChanged();
}
}
public boolean exists(String key) {
return mModelKeys.containsKey(key);
}
@Override
public Filter getFilter() {
if (valueFilter == null) {
valueFilter = new FirebaseRecyclerAdapter.ValueFilter();
}
return valueFilter;
}
protected abstract List<T> filters(List<T> models, CharSequence constraint);
private class ValueFilter extends Filter {
//Invoked in a worker thread to filter the data according to the constraint.
@Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults results = new Filter.FilterResults();
if (mFilteredModels == null) {
mFilteredModels = new ArrayList<>(mModels); // saves the original data in mOriginalValues
mFilteredKeys = new HashMap<>(mModelKeys); // saves the original data in mOriginalValues
}
if (constraint != null && constraint.length() > 0) {
List<T> filtered = filters(mFilteredModels, constraint);
results.count = filtered.size();
results.values = filtered;
mModelKeys = filterKeys(mModels);
} else {
results.count = mFilteredModels.size();
results.values = mFilteredModels;
mModelKeys = mFilteredKeys;
}
return results;
}
//Invoked in the UI thread to publish the filtering results in the user interface.
@SuppressWarnings("unchecked")
@Override
protected void publishResults(CharSequence constraint,
FilterResults results) {
Log.d(LOG_TAG, "filter for " + constraint + ", results nr: " + results.count);
mModels = (List<T>) results.values;
notifyDataSetChanged();
}
}
protected abstract Map<String, T> filterKeys(List<T> mModels);
}
使用FirebaseRecyclerAdapter扩展PostsQueryAdapter
public class FirebasePostsQueryAdapter extends FirebaseRecyclerAdapter<Feeds, PostViewHolder> {
Activity mActivity;
/**
* @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 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
* @param viewHolderClass This is the PostsViewHolder Class which will be used to populate data.
*/
Query query;
public FirebasePostsQueryAdapter(Query mRef, int mLayout, Activity activity, Class<PostViewHolder> viewHolderClass) {
super(mRef, Feeds.class, mLayout, activity, viewHolderClass);
this.query = mRef;
this.mActivity = activity;
}
@Override
protected void populateViewHolder(final PostViewHolder viewHolder, final Feeds model, final int position, final List<String> mKeys) {
viewHolder.setPhoto(model.getThumb_url());
viewHolder.setTimestamp(DateUtils.getRelativeTimeSpanString(
(long) model.getTimestamp()).toString());
viewHolder.setAuthor(model.getUser().getFull_name(), model.getUser().getUid());
viewHolder.setIcon(model.getUser().getProfile_picture(), model.getUser().getUid());
viewHolder.setText(model.getText());
viewHolder.mPhotoView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(mActivity, SingleVideoView.class);
intent.putExtra(Constants.INTENT_VIDEO,model.getVideo_url());
intent.putExtra(Constants.KEY, mKeys.get(position));
mActivity.startActivity(intent);
}
});
ValueEventListener likeListener = new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
viewHolder.setNumLikes(dataSnapshot.getChildrenCount());
if (dataSnapshot.hasChild(FirebaseUtil.getCurrentUserId())) {
viewHolder.setLikeStatus(PostViewHolder.LikeStatus.LIKED, mActivity);
} else {
viewHolder.setLikeStatus(PostViewHolder.LikeStatus.NOT_LIKED, mActivity);
}
}
@Override
public void onCancelled(DatabaseError databaseError) {
}
};
FirebaseUtil.getLikesRef().child(mKeys.get(position)).addValueEventListener(likeListener);
viewHolder.mLikeListener = likeListener;
}
@Override
protected List<Feeds> filters(List<Feeds> models, CharSequence constraint) {
return null;
}
@Override
protected Map<String, Feeds> filterKeys(List<Feeds> mModels) {
return null;
}
}
将数据填充到FirebasePostsQueryAdapter中的位置
@Override
public void onCreate() {
super.onCreate();
mFirebaseRef = FirebaseDatabase.getInstance().getReference(POSTS_STRING);
mFirebaseRef.keepSynced(true);
this.geoFire = new GeoFire(FirebaseDatabase.getInstance().getReference(GEO_POINTS));
mItemListAdapter = new FirebasePostQueryAdapter(mFirebaseRef.equalTo(GEOFIRE_CHILD), getActivity(), R.layout.list_item_items);
}
@Override
public void onConnected(Bundle bundle) {
startLocationUpdates();
center = new GeoLocation(MainActivity.mLastLocation.getLatitude(), MainActivity.mLastLocation.getLongitude());
if (!mActiveGeoQuery) {
center = new GeoLocation(mCurrentLocation.getLatitude(), mCurrentLocation.getLongitude());
startGeoQuery();
mAdapter.notifyDataSetChanged();
}
center = new GeoLocation(MainActivity.mLastLocation.getLatitude(), MainActivity.mLastLocation.getLongitude());
if (center.latitude != 0 && center.longitude != 0 && !mActiveGeoQuery) {
startGeoQuery();
} else if (mActiveGeoQuery) {
Log.d(TAG, "geoquery already active");
} else {
Log.d(TAG, "center not setted");
//I first try to set the center at the Last Known Location if retrieved
if (Double.isNaN(mCurrentLocation.getLatitude()) && mCurrentLocation.getLatitude() != 0) {
center = new GeoLocation(mCurrentLocation.getLatitude(), mCurrentLocation.getLongitude());
startGeoQuery();
}
}
fragment.checkForItems();
}
private void startGeoQuery() {
query = geoFire.queryAtLocation(center, 2);
Log.d(TAG, "center: " + center.toString() + ", radius: " + 2);
query.addGeoQueryEventListener(new GeoQueryEventListener() {
@Override
public void onKeyEntered(String key, GeoLocation location) {
Log.d(TAG, "Key " + key + " entered the search area at [" + location.latitude + "," + location.longitude + "]");
DatabaseReference tempRef = mFirebaseRef.child(key);
tempRef.addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot snapshot) {
// I add the deal only if it doesn't exist already in the adapter
String key = snapshot.getKey();
if (!mAdapter.exists(key)) {
Log.d(TAG, "item added " + key);
mAdapter.addSingle(snapshot);
mAdapter.notifyDataSetChanged();
} else {
//...otherwise I will update the record
Log.d(TAG, "item updated: " + key);
mAdapter.update(snapshot, key);
mAdapter.notifyDataSetChanged();
}
}
@Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
@Override
public void onKeyExited(String key) {
Log.d(TAG, "deal " + key + " is no longer in the search area");
mAdapter.remove(key);
fragment.isListEmpty();
}
@Override
public void onKeyMoved(String key, GeoLocation location) {
Log.d(TAG, String.format("Key " + key + " moved within the search area to [%f,%f]", location.latitude, location.longitude));
DatabaseReference tempRef = mFirebaseRef.child(key);
tempRef.addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot snapshot) {
// I add the deal only if it doesn't exist already in the adapter
String key = snapshot.getKey();
if (!mAdapter.exists(key)) {
Log.d(TAG, "item added " + key);
mAdapter.addSingle(snapshot);
mAdapter.notifyDataSetChanged();
} else {
//...otherwise I will update the record
Log.d(TAG, "item updated: " + key);
mAdapter.update(snapshot, key);
mAdapter.notifyDataSetChanged();
}
}
@Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
@Override
public void onGeoQueryReady() {
Log.d(TAG, "All initial data has been loaded and events have been fired!");
fragment.isListEmpty();
mActiveGeoQuery = true;
}
@Override
public void onGeoQueryError(DatabaseError error) {
Log.e(TAG, "There was an error with this query: " + error);
mActiveGeoQuery = false;
}
});
fragment.bindRecyclerView();
}