1

我正在创建一个 Firebase GeoFire 查询以在 ListView 中显示结果。我从emanuelet得到了这个 ListView 示例。我已经让它与 emanuelet 共享的要点一起工作,现在我还获得了 geoQuery 的 logcat 中的值。

D/GeoPostsFragment: Key -KMsopZitLFzcxFuPOCn entered the search area at [40.7853889,-122.4056973]
D/GeoPostsFragment: Key -KMu2IcjIyMx5Qpb5Kfh entered the search area at [40.7853889,-122.4056973]
D/GeoPostsFragment: Key -KMz3knxKsAAixlf4ijY entered the search area at [40.7853889,-122.4056973]
D/GeoPostsFragment: Key -KMzXh2Kf7nLdOPqnMpY entered the search area at [40.7853889,-122.4056973]
D/GeoPostsFragment: Key -KN1Z1qZZZzksPlmwG75 entered the search area at [40.7853889,-122.4056973]
D/GeoPostsFragment: Key -KN6ltFbtLgpR5GbPki6 entered the search area at [40.7853889,-122.4056973]
D/GeoPostsFragment: All initial data has been loaded and events have been fired!


这就是我在 firebase 上构建 GeoFire 数据的方式: 在此处输入图像描述


这是我的 GeoQuery 片段。

@Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        Log.d(LOG_TAG, "onCreate");
        super.onCreate(savedInstanceState);
        mContext = getActivity();

        settings = PreferenceManager.getDefaultSharedPreferences(mContext);
        setHasOptionsMenu(true);

        mFirebaseRef = FirebaseDatabase.getInstance().getReference().child("posts");
        //I initialize the object that will be used to retrieve the location
        mGeoRef = FirebaseDatabase.getInstance().getReference("GeoFire");
        geoFire = new GeoFire(mGeoRef);

        mItemListAdapter = new ItemListAdapter(mFirebaseRef.equalTo("posts"), post, R.layout.include_post_text, getActivity());
    }


    @Override
    public void onStart() {
        super.onStart();
        center = new GeoLocation(40.7853889, -122.4056973);
        if (center.latitude != 0 && center.longitude != 0 && !mActiveGeoQuery) {
            startGeoQuery();
        } else if (mActiveGeoQuery) {
            Log.d(LOG_TAG, "geoquery already active");
        } else {
            Log.d(LOG_TAG, "center not setted");
            //I first try to set the center at the Last Known Location if retrieved
            if (Double.isNaN(mLatitudeText) && mLatitudeText != 0) {
                Log.d(LOG_TAG, "center setted from fused location");
                center = new GeoLocation(40.7853889, -122.4056973);
                startGeoQuery();

            }
        }
        checkForItems();
    }


    @Override
    public void onResume() {
        super.onResume();
            mItemListAdapter.notifyDataSetChanged();
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {

        rootView = inflater.inflate(R.layout.geo_fragment, container, false);

        ButterKnife.bind(this, rootView);

        mListView = (ListView) mListContainer.findViewById(R.id.list);
        mListContainer.setViewState(MultiStateView.VIEW_STATE_LOADING);
        mListContainer.setViewState(MultiStateView.VIEW_STATE_EMPTY);
        mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
                Post cursor = (Post) mListView.getItemAtPosition(i);

                if (cursor != null) {
                    mCallbacks.onItemSelected(cursor, view);
                }
                mPosition = i;
            }
        });
        return rootView;
    }

    private void bindListView() {
        mListView.setAdapter(mItemListAdapter);
    }

    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {

    }

    private void startGeoQuery() {
        // safety net in case that for some reasons the returned radius is 0
        query = geoFire.queryAtLocation(new GeoLocation(40.7853889, -122.4056973), 1);
        Log.d(LOG_TAG, "center: " + center.toString() + ", radius: " + 0.2);
        query.addGeoQueryEventListener(new GeoQueryEventListener() {
            @Override
            public void onKeyEntered(String key, GeoLocation location) {
                Log.d(LOG_TAG, "Key " + key + " entered the search area at [" + location.latitude + "," + location.longitude + "]");
                DatabaseReference tempRef = mFirebaseRef.child("posts")
                        .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 (!mItemListAdapter.exists(key)) {
                            Log.d(LOG_TAG, "item added " + key);
                            mItemListAdapter.addSingle(snapshot);
                            Log.d(LOG_TAG,"Snapshot added " + snapshot);
                            mItemListAdapter.notifyDataSetChanged();
                            mListContainer.setViewState(MultiStateView.VIEW_STATE_CONTENT);
                        } else {
                            //...otherwise I will update the record
                            Log.d(LOG_TAG, "item updated: " + key);
                            mItemListAdapter.update(snapshot, key);
                            mItemListAdapter.notifyDataSetChanged();
                        }
                    }

                    @Override
                    public void onCancelled(DatabaseError firebaseError) {
                        Log.d(LOG_TAG, "cancelled with error:" + firebaseError.getMessage());
                    }
                });
            }

            @Override
            public void onKeyExited(String key) {
                Log.d(LOG_TAG, "deal " + key + " is no longer in the search area");
                mItemListAdapter.remove(key);
                isListEmpty();
            }

            @Override
            public void onKeyMoved(String key, GeoLocation location) {
                Log.d(LOG_TAG, String.format("Key " + key + " moved within the search area to [%f,%f]", location.latitude, location.longitude));
            }

            @Override
            public void onGeoQueryReady() {
                Log.d(LOG_TAG, "All initial data has been loaded and events have been fired!");
                isListEmpty();
                mActiveGeoQuery = true;
            }



            @Override
            public void onGeoQueryError(DatabaseError error) {
                Log.e(LOG_TAG, "There was an error with this query: " + error);
                mListContainer.setViewState(MultiStateView.VIEW_STATE_ERROR);
                mListContainer.setContentDescription("GeoQueryError");
                mActiveGeoQuery = false;
            }
        });
        bindListView();
    }


这是我正在使用的 ItemListAdapter,它扩展了自定义 FirebaseListAdapter。

 @BindView(R.id.post_body)
    TextView exampleView;
    Context context;

    public ItemListAdapter(Query ref,Class<Post> postClass, int layout, Activity activity) {
        super(ref,postClass,layout,activity);
        this.context = activity.getApplicationContext();
    }

    /**
     * Bind an instance of the ExampleObject class to our view. This method is called by <code>FirebaseListAdapter</code>
     * when there is a data change, and we are given an instance of a View that corresponds to the layout that we passed
     * to the constructor, as well as a single ExampleObject instance that represents the current data to bind.
     *
     * @param v    A view instance corresponding to the layout we passed to the constructor.
     * @param model An instance representing the current state of a message
     */

    @Override
    protected void populateView(View v, Post model) {
        ButterKnife.bind(this, v);
        // populate the list element
        exampleView.setText(model.body);
    }

    @Override
    protected List<Post> filters(List<Post> models, CharSequence constraint) {
        List<Post> filterList = new ArrayList<>();
        for (int i = 0; i < models.size(); i++) {
            /* implement your own filtering logic
             * and then call  filterList.add(models.get(i));
             */
            filterList.add(models.get(i));
        }
        return filterList;
    }

    @Override
    protected Map<String, Post> filterKeys(List<Post> mModels) {
        return null;
    }
}


这是我用于 GeoQueries 的 emanuelet 的自定义 FirebaseListAdapter。

 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<>();
        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(FirebaseListAdapter.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);
                    } else {
                        mModels.add(nextIndex, model);
                    }
                }

                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(FirebaseListAdapter.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);
                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(FirebaseListAdapter.this.mModelClass);
                int index = mModels.indexOf(oldModel);
                mModels.remove(index);
                if (previousChildName == null) {
                    mModels.add(0, newModel);
                } else {
                    T previousModel = mModelKeys.get(previousChildName);
                    int previousIndex = mModels.indexOf(previousModel);
                    int nextIndex = previousIndex + 1;
                    if (nextIndex == mModels.size()) {
                        mModels.add(newModel);
                    } else {
                        mModels.add(nextIndex, newModel);
                    }
                }
                notifyDataSetChanged();
            }

            @Override
            public void onCancelled(DatabaseError databaseError) {
                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();
    }

    @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;
    }

    public void remove(String key) {
        T oldModel = mModelKeys.get(key);
        mModels.remove(oldModel);
        mModelKeys.remove(key);
        notifyDataSetChanged();
    }

    @Override
    public View getView(int i, View view, ViewGroup viewGroup) {
        if (view == null) {
            view = mInflater.inflate(mLayout, viewGroup, false);
        }

        T model = mModels.get(i);
        if (model != null) {
            // 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);

    public void addSingle(DataSnapshot snapshot) {
        T model = snapshot.getValue(FirebaseListAdapter.this.mModelClass);
        mModelKeys.put(snapshot.getKey(), model);

        mModels.add(model);

        notifyDataSetChanged();
    }

    public void update(DataSnapshot snapshot, String key) {
        T oldModel = mModelKeys.get(key);
        T newModel = snapshot.getValue(FirebaseListAdapter.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 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 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,
                                      Filter.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);
}


这是模型类帖子

public Post() {
        // Default constructor required for calls to DataSnapshot.getValue(Post.class)
    }

    public Post(String uid, String author, String title, String body, String location) {
        this.uid = uid;
        this.author = author;
        this.title = title;
        this.body = body;
        this.location = location;
    }

    // [START post_to_map]
    @Exclude
    public Map<String, Object> toMap() {
        HashMap<String, Object> result = new HashMap<>();
        result.put("uid", uid);
        result.put("author", author);
        result.put("title", title);
        result.put("body", body);
        result.put("starCount", starCount);
        result.put("stars", stars);

        return result;
    }
    // [END post_to_map]

}

我是 firebase 环境的新手,这些 GeoQueries 与 Parse 结构完全不同,我很困惑,请帮我解决这个问题。即使我检查了xml布局也是正确的。它也准确地向我显示了 Logcat 中显示的列表项的数量。

4

0 回答 0