使用 geofire 仅检索 3 英里内的用户列表

Posted

技术标签:

【中文标题】使用 geofire 仅检索 3 英里内的用户列表【英文标题】:Retrieving list of users only within 3 miles using geofire 【发布时间】:2020-05-06 00:39:19 【问题描述】:

我正在尝试仅检索当前用户位置 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 的适配器类

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;
    

【问题讨论】:

仅供参考:半径参数的单位是公里而不是英里。 【参考方案1】:

您的地理查询正确搜索距给定位置 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 下的一个节点,并且您只想在每次调用onKeyEntered 方法时加载那个用户的节点。这样,当地理查询范围内有多个键时,您可以一一加载该范围内的每个用户。

如果是这种情况,您将需要在 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() 重新绘制。

【讨论】:

我按照你说的做了。但是现在我在测试时没有得到任何结果 鉴于您发布的代码的状态,任何人都不太可能通过简单的答案来修复它。这就是为什么我试图解释为什么您的代码不起作用的原因,希望之后您能够自己弄清楚更多,例如通过在调试器中运行应用程序并设置断点。例如。 onKeyEntered 是否使用您期望的用户密钥?我显示的代码是否会加载带有该用户信息的DataSnapshot?您能在onDataChange 中阅读该信息吗?是否正确添加到使用列表中? 在你给我的帮助下我得到了它,但我的地雷有点不同。我要把答案放上来。谢谢楼主【参考方案2】:

我在 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】:

您可以指定中心和半径several ways。这是一种方法:

var geoQuery = geoFire.query(
  center: [10.38, 2.41],
  radius: 10.5
);

请注意,radius 以公里为单位。

【讨论】:

非常感谢您的回复。我将如何以我现在拥有的方式将其放入我的代码中?这是否会解决我的问题并仅检索该范围内的用户? 有一种方法可以找出答案。 可以告诉我如何按照现在的设置方式放入我的代码吗?

以上是关于使用 geofire 仅检索 3 英里内的用户列表的主要内容,如果未能解决你的问题,请参考以下文章

iOS - 获取当前邮政编码 X 英里内的位置?

GeoFire查询数据列表

如何返回邮政编码列表的## 英里半径内的所有实例

如何在 Flutter 中仅显示一英里内的帖子

计算半径内的 100 英里

检索给定区域 x 距离内的所有经纬度点