使用 geofire 仅在 3 英里内检索用户列表
Retrieving list of users only within 3 miles using geofire
我正在尝试仅检索当前用户位置 3 英里以内的用户列表。我使用 recyclerView 列出用户,我还使用 geofire 查询密钥,我使用 firebase 来存储我的用户。当我尝试仅检索 3 英里以内的用户列表时,它会为我提供所有用户,包括 3 英里以外的用户。有人可以帮我解决这个问题吗?下面是我的代码。提前致谢
MainClass 检索数据
@Override
public void onSuccess(Location location) {
Double latitude=location.getLatitude();
Double longitude=location.getLongitude();
Geocoder geocoder=new Geocoder(NewsFeedActivity.this, Locale.getDefault());
firebaseDatabase=FirebaseDatabase.getInstance().getReference("Users");
GeoFire geoFire=new GeoFire(firebaseDatabase.child("locals"));
final GeoQuery geoQuery=geoFire.queryAtLocation(new GeoLocation(latitude,longitude),3);
geoQuery.addGeoQueryEventListener(new GeoQueryEventListener() {
@Override
public void onKeyEntered(final String key, GeoLocation location) {
firebaseDatabase.addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
list=new ArrayList<Users>();
for(DataSnapshot dataSnapshot1:dataSnapshot.getChildren()){
Users my=dataSnapshot1.getValue(My.class);
list.add(my);
}
adapter=new MyAdapter(MainActivity.this,list);
recycler.setAdapter(adapter);
adapter=new MyAdapter(MainActivity.this,list);
recycler.setAdapter(adapter);
}
@Override
public void onCancelled(@NonNull DatabaseError databaseError) {
}
});
}
@Override
public void onKeyExited(String key) {
}
@Override
public void onKeyMoved(String key, GeoLocation location) {
}
@Override
public void onGeoQueryReady() {
}
@Override
public void onGeoQueryError(DatabaseError error) {
System.err.println("There was an error with this query: " + error);
}
});
recyclerView 的适配器class
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
Context context;
private ArrayList<UserInformation> users;
public MyAdapter(Context c,ArrayList<UserInformation> u){
context=c;
users=u;
}
@NonNull
@Override
public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new MyViewHolder(LayoutInflater.from(context).inflate(R.layout.cardview,parent,false));
}
@Override
public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
holder.user_name.setText(users.get(position).getBusinessname());
holder.address.setText(users.get(position).getAddress());
}
@Override
public int getItemCount() {
return users.size();
}
class MyViewHolder extends RecyclerView.ViewHolder{
TextView user_name,address;
public MyViewHolder(@NonNull View itemView) {
super(itemView);
user_name=(TextView)itemView.findViewById(R.id.user_name);
address=(TextView)itemView.findViewById(R.id.user_address);
}
}
}
//Users Class
public class Users {
public String usersname;
public String address;
public String phonenumber;
public Users(String usersname, String address, String phonenumber) {
this.usersname = usersname;
this.address = address;
this.phonenumber = phonenumber;
}
public Users(){
}
public String getBusinessname() {
return usersname;
}
public void setBusinessname(String businessname) {
this.usersname = usersname;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getPhonenumber() {
return phonenumber;
}
public void setPhonenumber(String phonenumber) {
this.phonenumber = phonenumber;
}
}
您可以指定中心和半径 several ways。这是一种方法:
var geoQuery = geoFire.query({
center: [10.38, 2.41],
radius: 10.5
});
请注意,radius 是以公里为单位指定的。
您的地理查询正确地搜索了距给定位置 3 公里以内的键:
firebaseDatabase=FirebaseDatabase.getInstance().getReference("Users");
GeoFire geoFire=new GeoFire(firebaseDatabase.child("locals"));
final GeoQuery geoQuery=geoFire.queryAtLocation(new GeoLocation(latitude,longitude),3);
但是当 GeoFire 在该范围内找到一个键并调用 onKeyEntered
时,您将 ValueEventListener
添加到整个 Users
节点:
geoQuery.addGeoQueryEventListener(new GeoQueryEventListener() {
@Override
public void onKeyEntered(final String key, GeoLocation location) {
firebaseDatabase.addValueEventListener(new ValueEventListener() {
因此,对于地理查询范围内的每个键,您加载所有用户。如果范围内有多个键,则为每个键加载所有用户,并每次创建一个适配器。充其量这是混乱和浪费的,但我认为在这种情况下,这也是为什么你的代码没有按照你想要的去做。
我最好的猜测是地理查询中的每个键都标识了 Users
下的一个节点,并且您只想在每次 [=13] 时加载 that 用户的节点=] 方法被调用。这样,当地理查询范围内有多个键时,您可以一个一个地加载该范围内的每个用户。
如果是这种情况,您需要在 onKeyEntered
:
中使用此侦听器
firebaseDatabase.child(key).addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
Users my=dataSnapshot.getValue(My.class);
list.add(my);
adapter.notifyDataSetChanged();
}
@Override
public void onCancelled(@NonNull DatabaseError databaseError) {
throw databaseError.toException(); // never ignore possible errors
}
});
我在这里做的一些改动:
- 这不是加载所有用户,而是为属于(或进入)地理查询范围的每个键加载一个用户。
- 使用
addListenerForSingleValueEvent
,而不是连续的监听器。这样您只需一次获取用户的数据,而不是不断添加越来越多的侦听器。
- 调用
adapter.notifyDataSetChanged();
而不是为每个落入地理查询范围内的键创建新的适配器。
您还需要做的事情:
- 仅初始化一次适配器,在
onKeyEntered
的 之外,将其传递给 list
。
然后当 onKeyEntered
触发时,加载该用户的数据,将其添加到 list
并告诉适配器它需要使用 adapter.notifyDataSetChanged()
重新绘制。
我在 Frank Van Puffelen 的帮助下解决了这个问题。这是我所做的
public void onKeyEntered(final String key, GeoLocation location) {
firebaseDatabase.child(key).addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
Users my=dataSnapshot.getValue(Users.class);
list.add(my);
adapter=new MyAdapter(MainActivity.this,list);
recycler.setAdapter(adapter);
}
我正在尝试仅检索当前用户位置 3 英里以内的用户列表。我使用 recyclerView 列出用户,我还使用 geofire 查询密钥,我使用 firebase 来存储我的用户。当我尝试仅检索 3 英里以内的用户列表时,它会为我提供所有用户,包括 3 英里以外的用户。有人可以帮我解决这个问题吗?下面是我的代码。提前致谢
MainClass 检索数据
@Override
public void onSuccess(Location location) {
Double latitude=location.getLatitude();
Double longitude=location.getLongitude();
Geocoder geocoder=new Geocoder(NewsFeedActivity.this, Locale.getDefault());
firebaseDatabase=FirebaseDatabase.getInstance().getReference("Users");
GeoFire geoFire=new GeoFire(firebaseDatabase.child("locals"));
final GeoQuery geoQuery=geoFire.queryAtLocation(new GeoLocation(latitude,longitude),3);
geoQuery.addGeoQueryEventListener(new GeoQueryEventListener() {
@Override
public void onKeyEntered(final String key, GeoLocation location) {
firebaseDatabase.addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
list=new ArrayList<Users>();
for(DataSnapshot dataSnapshot1:dataSnapshot.getChildren()){
Users my=dataSnapshot1.getValue(My.class);
list.add(my);
}
adapter=new MyAdapter(MainActivity.this,list);
recycler.setAdapter(adapter);
adapter=new MyAdapter(MainActivity.this,list);
recycler.setAdapter(adapter);
}
@Override
public void onCancelled(@NonNull DatabaseError databaseError) {
}
});
}
@Override
public void onKeyExited(String key) {
}
@Override
public void onKeyMoved(String key, GeoLocation location) {
}
@Override
public void onGeoQueryReady() {
}
@Override
public void onGeoQueryError(DatabaseError error) {
System.err.println("There was an error with this query: " + error);
}
});
recyclerView 的适配器class
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
Context context;
private ArrayList<UserInformation> users;
public MyAdapter(Context c,ArrayList<UserInformation> u){
context=c;
users=u;
}
@NonNull
@Override
public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new MyViewHolder(LayoutInflater.from(context).inflate(R.layout.cardview,parent,false));
}
@Override
public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
holder.user_name.setText(users.get(position).getBusinessname());
holder.address.setText(users.get(position).getAddress());
}
@Override
public int getItemCount() {
return users.size();
}
class MyViewHolder extends RecyclerView.ViewHolder{
TextView user_name,address;
public MyViewHolder(@NonNull View itemView) {
super(itemView);
user_name=(TextView)itemView.findViewById(R.id.user_name);
address=(TextView)itemView.findViewById(R.id.user_address);
}
}
}
//Users Class
public class Users {
public String usersname;
public String address;
public String phonenumber;
public Users(String usersname, String address, String phonenumber) {
this.usersname = usersname;
this.address = address;
this.phonenumber = phonenumber;
}
public Users(){
}
public String getBusinessname() {
return usersname;
}
public void setBusinessname(String businessname) {
this.usersname = usersname;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getPhonenumber() {
return phonenumber;
}
public void setPhonenumber(String phonenumber) {
this.phonenumber = phonenumber;
}
}
您可以指定中心和半径 several ways。这是一种方法:
var geoQuery = geoFire.query({
center: [10.38, 2.41],
radius: 10.5
});
请注意,radius 是以公里为单位指定的。
您的地理查询正确地搜索了距给定位置 3 公里以内的键:
firebaseDatabase=FirebaseDatabase.getInstance().getReference("Users");
GeoFire geoFire=new GeoFire(firebaseDatabase.child("locals"));
final GeoQuery geoQuery=geoFire.queryAtLocation(new GeoLocation(latitude,longitude),3);
但是当 GeoFire 在该范围内找到一个键并调用 onKeyEntered
时,您将 ValueEventListener
添加到整个 Users
节点:
geoQuery.addGeoQueryEventListener(new GeoQueryEventListener() {
@Override
public void onKeyEntered(final String key, GeoLocation location) {
firebaseDatabase.addValueEventListener(new ValueEventListener() {
因此,对于地理查询范围内的每个键,您加载所有用户。如果范围内有多个键,则为每个键加载所有用户,并每次创建一个适配器。充其量这是混乱和浪费的,但我认为在这种情况下,这也是为什么你的代码没有按照你想要的去做。
我最好的猜测是地理查询中的每个键都标识了 Users
下的一个节点,并且您只想在每次 [=13] 时加载 that 用户的节点=] 方法被调用。这样,当地理查询范围内有多个键时,您可以一个一个地加载该范围内的每个用户。
如果是这种情况,您需要在 onKeyEntered
:
firebaseDatabase.child(key).addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
Users my=dataSnapshot.getValue(My.class);
list.add(my);
adapter.notifyDataSetChanged();
}
@Override
public void onCancelled(@NonNull DatabaseError databaseError) {
throw databaseError.toException(); // never ignore possible errors
}
});
我在这里做的一些改动:
- 这不是加载所有用户,而是为属于(或进入)地理查询范围的每个键加载一个用户。
- 使用
addListenerForSingleValueEvent
,而不是连续的监听器。这样您只需一次获取用户的数据,而不是不断添加越来越多的侦听器。 - 调用
adapter.notifyDataSetChanged();
而不是为每个落入地理查询范围内的键创建新的适配器。
您还需要做的事情:
- 仅初始化一次适配器,在
onKeyEntered
的 之外,将其传递给list
。
然后当 onKeyEntered
触发时,加载该用户的数据,将其添加到 list
并告诉适配器它需要使用 adapter.notifyDataSetChanged()
重新绘制。
我在 Frank Van Puffelen 的帮助下解决了这个问题。这是我所做的
public void onKeyEntered(final String key, GeoLocation location) {
firebaseDatabase.child(key).addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
Users my=dataSnapshot.getValue(Users.class);
list.add(my);
adapter=new MyAdapter(MainActivity.this,list);
recycler.setAdapter(adapter);
}