Как упорядочить Firebase ListView (FirebaseUI) по пользовательскому свойству (не по значению базы данных)

Возможно ли иметь сам порядок FirebaseUI ListView по свойству локальной модели данных (в данном случае Person.class), а не по значению, содержащемуся в базе данных... в режиме реального времени?

Проблема: я конвертирую сильно привязанное к местоположению приложение (похожее на приложение для совместного использования) с PHP/mysql на firebase. (Кстати, я очень взволнован Firebase... и я уже увидел много преимуществ).

Для наших целей приложение просто отображает список, отображающий ближайших и доступных поставщиков услуг, упорядоченных по расстоянию. Раньше это делалось путем отправки на сервер PHP-запроса, включающего широту и долготу клиента в качестве параметров POST. Затем сервер отправляет обратно список поставщиков услуг в формате JSON. Сервер будет выполнять математические расчеты, упорядочивая результаты на основе текущего расстояния поставщика услуг до клиента. Кроме того... поставщики услуг, которые недоступны, вообще не возвращаются. Проблема со всем этим в том, что пользователю по-прежнему нужно «потянуть, чтобы обновить».

Подход Firebase работает до сих пор, но я смог отфильтровать свой список (с поддержкой FirebaseUI) только по доступности поставщика услуг (см. код), но я не смог придумать, как упорядочить список по расстоянию.

В приведенном ниже коде показано, что я пытаюсь сделать. Класс Person представляет поставщиков услуг.

Итак... скажем, класс Person имеет переменную Person.distance и метод getDistance(), который возвращает рассчитанное расстояние между их местоположением (из базы данных Firebase) и местоположением, хранящимся в данный момент в телефоне пользователя приложения. Очевидно, что эту переменную нельзя хранить в базе данных и использовать в качестве сопоставимого значения, поскольку она является относительной и зависит от местонахождения как пользователя, так и поставщика услуг.

Есть ли способ добиться этого, используя представление переработчика? Я бы предположил, что это будет что-то сделано внутри адаптера... но все это уже завернуто в FirebaseUI... и я недостаточно хорош в кодировании, чтобы переписать классы библиотеки Android. Спасибо за любую помощь.

Соответствующий код (работает нормально... это не проблема... сначала прочитайте сообщение):

ref = FirebaseDatabase.getInstance().getReference().child("person");

    Query orderedByAvail_Query = ref.orderByChild("available").equalTo(true);

    FirebaseListAdapter<Person> fireAdapter = new FirebaseListAdapter<Person>(getActivity(),
            Person.class, R.layout.single_row, orderedByAvail_Query) {
        @Override
        protected void populateView(View v, Person person, int position) {

            TextView name_title = (TextView) v.findViewById(R.id.Nickname);
            name_title.setText(person.getNickname());

            if (person.getAvail()){
                name_title.setBackgroundColor(Color.GREEN);
            } else {
                name_title.setBackgroundColor(Color.WHITE);
            }




        }
    };

        // Arraylist<Person> tempList4Sorting = new ArrayList<Person>;

        // for (i=1 to fireAdapter.size){
        // tempList4Sorting.add(fireAdapter.getItem(i);
        // i++;
        // }

        // Collections.sort(tempList4Sorting, Person.distance, DESCENDING);

        // fireAdapter.clearItems();
        // 
        // put sorted items back into the adapter using another for loop

        // fireAdapter.notifyDataSetChanged (?)


    setListAdapter(fireAdapter);

person Nerdy Bunz    schedule 30.08.2016    source источник
comment
Адаптеры в FirebaseUI могут упорядочивать элементы только в том порядке, в котором их возвращает Firebase (который основан на указанном вами запросе). Если это не удовлетворяет ваши потребности, вам придется создать свой собственный адаптер. FirebaseUI имеет открытый исходный код, поэтому адаптеры, вероятно, можно использовать для вдохновения/в качестве отправной точки.   -  person Frank van Puffelen    schedule 30.08.2016
comment
Я начал пытаться это сделать, спасибо за толчок.   -  person Nerdy Bunz    schedule 30.08.2016
comment
Хорошо хорошо. Если вы застряли, напишите сюда с минимальным кодом, который воспроизводит то, где вы застряли, и мы можем попытаться помочь. Я собираюсь проголосовать за то, чтобы закрыть этот вопрос, потому что написание такого адаптера (хоть и забавно — я написал их в FirebaseUI) слишком широко для ответа на переполнение стека.   -  person Frank van Puffelen    schedule 30.08.2016
comment
Добавил немного в пост.   -  person Nerdy Bunz    schedule 31.08.2016
comment
Звучит как довольно крутая реализация. Из того, что вы описали, возможно, даже можно просто создать подкласс FirebaseListAdapter. В любом случае: можете ли вы опубликовать описание в качестве ответа? Самостоятельные ответы совершенно уместны здесь, в StackOverflow, и позволяют другим увидеть, где заканчиваются проблемы и где начинается решение. :-)   -  person Frank van Puffelen    schedule 31.08.2016


Ответы (1)


Я добился того, что кажется рабочим решением... хотя я определенно предпочел простоту эффективности. Не думаю, что я действительно что-то «переписал» — больше похоже на то, что я играл в старую игру «Операция». В любом случае... Вот что я сделал:

1) Сделал класс Person реализацией Comparable, чтобы Collection.sort() работал. В методе compareTo() сортировка выполняется с помощью Person.getDistance(), который просто вычисляет расстояние между широтой/долготой по данным GPS телефона, на котором запущено приложение, и широтой/долготой человека (получено из базы данных Firebase).

2) Изменен метод getItem() в FirebaseListActivity следующим образом:

@Override
public T getItem(int position) {
    Log.i("XXX", "getItem() was called");


    // put all (parsed) list items into an array

    ArrayList<Person> temp_person_list = new ArrayList();

    for (int i = 1; i<=(mSnapshots.getCount()); i++){

        temp_person_list.add((Person) parseSnapshot(mSnapshots.getItem(i-1)));

    }

    // sort the array based on distance property
    Collections.sort(temp_person_list);


    // return the originally desired position
    return (T) temp_person_list.get(position);

}

Я знаю, что это ужасно в принципе... (Я уверен, что лучшим способом было бы добавить еще один базовый ArrayList внутри класса FirebaseArray, который молча переназначает индексы в зависимости от расстояния... но, честно говоря, у меня болит голова) .

В любом случае... Я также рассмотрел другое возможное решение (например, Geofire) - использование геохэша вместо широты/долготы. Таким образом, будет только ОДНО (сортируемое) поле местоположения вместо двух... и его можно будет запросить для получения отсортированного (по близости) списка с использованием существующих методов запроса Firebase. Все еще работаю над этим.

person Nerdy Bunz    schedule 31.08.2016
comment
Обновляет ли это представление автоматически, если ранг элемента изменяется (на основе алгоритма сортировки) после заполнения представления? - person Adi; 25.11.2018