Как обработать пейджинговую библиотеку initialKey, когда это LastVisibleItem вместо FirstVisibleItem? - PullRequest
0 голосов
/ 08 мая 2019

Я замечаю, что когда я прокручиваю вверх, библиотека подкачки использует ключ FirstVisibleItem в качестве initialKey после аннулирования данных, что хорошо для меня, но когда я прокручиваю вниз, она использует ключ LastVisibleItem в качестве initialKey, которыйвызывает у меня проблему.

Я использую этот запрос для получения сообщений чата из базы данных Firebase, и он прекрасно работает, пока initialKey является FirstVisibleItem:

.startAt (initialKey) .limitToFirst(размер);

Но использование того же запроса, когда initialKey является LastVisibleItem, заставляет переработчика перепрыгнуть (прокрутить вниз) к элементу, у которого есть initialKey, и сделать его сверху.

Пока единственное решение состоит в том, чтобы изменить запрос таким образом, когда пользователь прокручивает вниз:

.endAt (initialKey) .limitToLast (size);

Есть ли способ заставитьбиблиотека подкачки для использования только ключа FirstVisibleItem или способ сообщить хранилищу о том, что пользователь в данный момент прокручивает вверх или вниз для настройки запросасоответственно?

public void getMessages(String initialKey, final int size,
                        @NonNull final ItemKeyedDataSource.LoadInitialCallback<Message> callback) {

    Log.i(TAG, "getMessages initiated. initialKey= " +  initialKey);
    this.initialKey = initialKey;
    Query messagesQuery;
    isInitialFirstLoaded = true;

    ValueEventListener  initialMessagesListener = new ValueEventListener() {
        @Override
        public void onDataChange(DataSnapshot dataSnapshot) {
            // [START_EXCLUDE]
            Log.d(TAG, "start onDataChange. isInitialFirstLoaded = "+ isInitialFirstLoaded);

            if (!isInitialFirstLoaded){
                // Remove post value event listener
                removeListeners();
                Log.d(TAG, "usersChanged Invalidated removeEventListener");
                //isInitialFirstLoaded =  true;
                Log.d(TAG, "onInvalidated(). isInitialFirstLoaded = "+ isInitialFirstLoaded);
                invalidatedCallback.onInvalidated();
                //UsersDataSource.InvalidatedCallback.class.getMethod("loadInitial", "LoadInitialParams");
                //UsersDataSource.invalidate();
                return;
            }

            if (dataSnapshot.exists()) {
                final List<Message> messagesList = new ArrayList<>();
                // loop throw users value
                for (DataSnapshot snapshot: dataSnapshot.getChildren()){
                    Message message = snapshot.getValue(Message.class);
                    if (message != null) {
                        message.setKey(snapshot.getKey());
                    }
                    messagesList.add(message);
                    //Log.d(TAG, "getMessage = "+ message.getMessage()+" getSnapshotKey= " +  snapshot.getKey());
                }

                if(messagesList.size() != 0){

                    callback.onResult(messagesList);
                    Log.d(TAG, "getMessages  List.size= " +  messagesList.size()+ " lastkey= "+messagesList.get(messagesList.size()-1).getKey() + " getInitialKey= "+ getInitialKey() );
                }
            } else {
                // no data
                Log.w(TAG, "getMessages no users exist");
            }
            getListeners();
            isInitialFirstLoaded =  false;
            Log.d(TAG, "end isInitialFirstLoaded = "+ isInitialFirstLoaded);
        }

        @Override
        public void onCancelled(DatabaseError databaseError) {
            // Getting Post failed, log a message
            Log.w(TAG, "loadPost:onCancelled", databaseError.toException());
        }
    };

    if (initialKey == null) {// if it's loaded for the first time. Key is null
        Log.d(TAG, "getMessages initialKey= " + initialKey);
        messagesQuery = mMessagesRef.orderByKey()//limitToLast to start from the last (page size) items
                .limitToLast(size);

    } else {// not the first load. Key is the last or first seen key
        Log.d(TAG, "getMessages initialKey= " + initialKey);// I need to adjust the Query according scrolling direction
        if(user is scrolling up){
            messagesQuery = mMessagesRef.orderByKey().startAt(initialKey).limitToFirst(size);
        }else{
            messagesQuery = mMessagesRef.orderByKey().endAt(initialKey).limitToLast(size);
        }
    } 
    messagesQuery.addValueEventListener(initialMessagesListener);
    mListenersList.add(new FirebaseListeners(messagesQuery, initialMessagesListener));

Обновление:

Пока я нашел два решения, первое проще, а второе лучше:

Решения 1: Добавьте ScrollListener, чтобы выяснить, прокручивает ли пользователь вверх или вниз, прокручивает ли пользователь вверх, будет ли видимый элемент первым видимым элементом или выше, если прокрутить вниз, начальный ключ будет последним видимым элементом или ниже, все, что у меня естьдля этого нужно изменить запрос соответствующим образом.

public class PagingFragment extends Fragment {

@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
                         @Nullable Bundle savedInstanceState) {
    View fragView = inflater.inflate(R.layout.messages_fragment, container, false);


    // Listen for scroll events
    mMessagesRecycler.addOnScrollListener(new RecyclerView.OnScrollListener() {
        @Override
        public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
            super.onScrollStateChanged(recyclerView, newState);
            Log.d(TAG, "onScrollStateChanged newState= "+newState);
        }

        @Override
        public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
            super.onScrolled(recyclerView, dx, dy);
            Log.d(TAG, "onScrolled dx= "+dx +" dy= "+dy);

            int lastCompletelyVisibleItem = mLinearLayoutManager.findLastCompletelyVisibleItemPosition(); // the position of last displayed item

            if(lastCompletelyVisibleItem >= (totalItemCount-1)){
                // The position of last displayed item = total items, witch means we are at the bottom
                mScrollDirection = REACHED_THE_BOTTOM;
                Log.i(TAG, "List reached the bottom");
            }else if(lastCompletelyVisibleItem <= visibleItemCount){
                // The position of last displayed item is less than visibleItemCount, witch means we are at the top
                mScrollDirection = REACHED_THE_TOP;
                Log.i(TAG, "List reached the top");
            }else{
                if(dy < 0 ){
                    // dy is negative number,  scrolling up
                    Log.i(TAG, "List scrolling up");
                    mScrollDirection = SCROLLING_UP;
                }else{
                    // dy is positive number,  scrolling down
                    Log.i(TAG, "List scrolling down");
                    mScrollDirection = SCROLLING_DOWN;
                }
            }

            // set scrolling direction. it's needed for the initialkey
            mMessagesViewModel.setScrollDirection(mScrollDirection);

    return fragView;
}

    public class MessagesViewModel extends ViewModel {
        // Set scroll direction
        public void setScrollDirection(int scrollDirection, int lastVisibleItem) {
            MessagesListRepository.setScrollDirection(scrollDirection);
        }
    }


    public class MessagesListRepository {

        public static void setScrollDirection(int scrollDirection) {
            Log.d(TAG, "mScrollDirection = " + scrollDirection);
            mScrollDirection = scrollDirection;
        }

        // get initial data
        public void getMessages(String initialKey, final int size,
                                @NonNull final ItemKeyedDataSource.LoadInitialCallback<Message> callback) {

            Log.i(TAG, "getMessages initiated. initialKey= " +  initialKey);
            this.initialKey = initialKey;
            Query messagesQuery;
            isInitialFirstLoaded = true;

            ValueEventListener initialMessagesListener = new ValueEventListener() {
                @Override
                public void onDataChange(DataSnapshot dataSnapshot) {
                    // [START_EXCLUDE]
                    Log.d(TAG, "start onDataChange. isInitialFirstLoaded = "+ isInitialFirstLoaded);

                    if (!isInitialFirstLoaded){
                        // Remove post value event listener
                        removeListeners();
                        Log.d(TAG, "usersChanged Invalidated removeEventListener");
                        //isInitialFirstLoaded =  true;
                        Log.d(TAG, "onInvalidated(). isInitialFirstLoaded = "+ isInitialFirstLoaded);
                        invalidatedCallback.onInvalidated();
                        //UsersDataSource.InvalidatedCallback.class.getMethod("loadInitial", "LoadInitialParams");
                        //UsersDataSource.invalidate();
                        return;
                    }

                    if (dataSnapshot.exists()) {
                        final List<Message> messagesList = new ArrayList<>();
                        // loop throw users value
                        for (DataSnapshot snapshot: dataSnapshot.getChildren()){
                            Message message = snapshot.getValue(Message.class);
                            if (message != null) {
                                message.setKey(snapshot.getKey());
                            }
                            messagesList.add(message);
                        }

                        if(messagesList.size() != 0){
                            callback.onResult(messagesList);
                            Log.d(TAG, "getMessages  List.size= " +  messagesList.size()+ " lastkey= "+messagesList.get(messagesList.size()-1).getKey() + " getInitialKey= "+ getInitialKey() );
                        }
                    } else {
                        // no data
                        Log.w(TAG, "getMessages no users exist");
                    }
                    isInitialFirstLoaded =  false;
                    Log.d(TAG, "end isInitialFirstLoaded = "+ isInitialFirstLoaded);
                }

                @Override
                public void onCancelled(DatabaseError databaseError) {
                    // Getting Post failed, log a message
                    Log.w(TAG, "loadPost:onCancelled", databaseError.toException());
                }
            };

            if (initialKey == null) {// if it's loaded for the first time. Key is null
                Log.d(TAG, "getMessages initialKey= " + initialKey);
                messagesQuery = mMessagesRef.orderByKey()//limitToLast to start from the last (page size) items
                        .limitToLast(size);

            } else {// not the first load. Key is the last seen key
                Log.d(TAG, "getMessages initialKey= " + initialKey);
                switch (mScrollDirection){
                    case REACHED_THE_BOTTOM:
                        Log.d(TAG, "messages query = REACHED_THE_BOTTOM");
                        messagesQuery = mMessagesRef.orderByKey()
                                .limitToLast(size);
                        break;
                    case REACHED_THE_TOP:
                        Log.d(TAG, "messages query = REACHED_THE_TOP");
                        messagesQuery = mMessagesRef.orderByKey()
                                .limitToFirst(size);
                        break;
            case SCROLLING_UP:
                messagesQuery = mMessagesRef.orderByKey()
                        .startAt(initialKey)
                        .limitToFirst(size);
                break;
            case SCROLLING_DOWN:
                messagesQuery = mMessagesRef.orderByKey()
                        .endAt(initialKey)
                        .limitToLast(size);
                break;
                    default:
                        messagesQuery = mMessagesRef.orderByKey()//limitToLast to start from the last (page size) items
                                .limitToLast(size);
                        break;
                }
            }

            messagesQuery.addValueEventListener(initialMessagesListener);
            mListenersList.add(new FirebaseListeners(messagesQuery, initialMessagesListener));

        }
    }

Решение 2: Добавьте ScrollListener для получения lastCompletelyVisibleItem, затем в репозитории я сравниваю lastCompletelyVisibleItem с позицией initialkey, циклически бросая, а массив содержит все отображаемыепредметы, чтобы узнать положение начального ключа.Если позиция initialkey равна или больше, чем lastCompletelyVisibleItem, это означает, что я должен запросить вышеуказанные (до) элементы, если позиция initialkey меньше, чем lastCompletelyVisibleItem, это значит, что я должен запросить нижеследующие (после) элементы.

public class PagingFragment extends Fragment {

@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
                         @Nullable Bundle savedInstanceState) {
    View fragView = inflater.inflate(R.layout.messages_fragment, container, false);


    // Listen for scroll events
    mMessagesRecycler.addOnScrollListener(new RecyclerView.OnScrollListener() {
        @Override
        public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
            super.onScrollStateChanged(recyclerView, newState);
            Log.d(TAG, "onScrollStateChanged newState= "+newState);
        }

        @Override
        public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
            super.onScrolled(recyclerView, dx, dy);
            Log.d(TAG, "onScrolled dx= "+dx +" dy= "+dy);

            int lastCompletelyVisibleItem = mLinearLayoutManager.findLastCompletelyVisibleItemPosition(); // the position of last displayed item

            if(lastCompletelyVisibleItem >= (totalItemCount-1)){
                // The position of last displayed item = total items, witch means we are at the bottom
                mScrollDirection = REACHED_THE_BOTTOM;
                Log.i(TAG, "List reached the bottom");
            }else if(lastCompletelyVisibleItem <= visibleItemCount){
                // The position of last displayed item is less than visibleItemCount, witch means we are at the top
                mScrollDirection = REACHED_THE_TOP;
                Log.i(TAG, "List reached the top");
            }else{
                if(dy < 0 ){
                    // dy is negative number,  scrolling up
                    Log.i(TAG, "List scrolling up");
                    mScrollDirection = SCROLLING_UP;
                }else{
                    // dy is positive number,  scrolling down
                    Log.i(TAG, "List scrolling down");
                    mScrollDirection = SCROLLING_DOWN;
                }
            }

            mMessagesViewModel.setScrollDirection(mScrollDirection, lastCompletelyVisibleItem);

    return fragView;
}

    public class MessagesViewModel extends ViewModel {
        // Set scroll direction
        public void setScrollDirection(int scrollDirection, int lastVisibleItem) {
            MessagesListRepository.setScrollDirection(scrollDirection, lastVisibleItem);
        }
    }


    public class MessagesListRepository {

        private static List<Message> totalItemsList;// = new ArrayList<>();


        private ValueEventListener afterMessagesListener = new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {
                // [START_EXCLUDE]
                Log.d(TAG, "start onDataChange isAfterFirstLoaded = "+ isAfterFirstLoaded);
                if (!isAfterFirstLoaded){
                    // Remove post value event listener
                    removeListeners();
                    Log.d(TAG, "getMessagesAfter Invalidated removeEventListener");
                    //isAfterFirstLoaded =  true;
                    Log.d(TAG, "getMessagesAfter onInvalidated(). isAfterFirstLoaded = "+ isAfterFirstLoaded);
                    invalidatedCallback.onInvalidated();
                    return;
                }

                if (dataSnapshot.exists()) {
                    List<Message> messagesList = new ArrayList<>();
                    // loop throw users value
                    for (DataSnapshot snapshot: dataSnapshot.getChildren()){
                        if(!getLoadAfterKey().equals(snapshot.getKey())) { // if snapshot key = startAt key? don't add it again
                            Message message = snapshot.getValue(Message.class);
                            if (message != null) {
                                message.setKey(snapshot.getKey());
                            }
                            messagesList.add(message);
                            // Add messages to totalItemsList ArrayList to be used to get the initial key position
                            totalItemsList.add(message);
                            //Log.d(TAG, "getMessage = "+ message.getMessage()+" getSnapshotKey= " +  snapshot.getKey());
                        }
                    }

                    if(messagesList.size() != 0){
                        //callback.onResult(messagesList);
                        getLoadAfterCallback().onResult(messagesList);
                        Log.d(TAG, "getMessagesAfter  List.size= " +  messagesList.size()+ " lastkey= "+messagesList.get(messagesList.size()-1).getKey());
                    }
                } else {
                    // no data
                    Log.w(TAG, "getMessagesAfter no users exist");
                }
                isAfterFirstLoaded =  false;
                Log.d(TAG, "end isAfterFirstLoaded = "+ isAfterFirstLoaded);
            }

            @Override
            public void onCancelled(DatabaseError databaseError) {
                // Getting Post failed, log a message
                Log.w(TAG, "getMessagesAfter loadPost:onCancelled", databaseError.toException());
            }
        };

        private ValueEventListener beforeMessagesListener = new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {
                // [START_EXCLUDE]
                Log.d(TAG, "start onDataChange isBeforeFirstLoaded = "+ isBeforeFirstLoaded);
                if (!isBeforeFirstLoaded){
                    // Remove post value event listener
                    removeListeners();
                    Log.d(TAG, "getMessagesBefore Invalidated removeEventListener");
                    //isBeforeFirstLoaded =  true;
                    Log.d(TAG, "getMessagesBefore onInvalidated(). isBeforeFirstLoaded = "+ isBeforeFirstLoaded);
                    invalidatedCallback.onInvalidated();
                    return;
                }

                if (dataSnapshot.exists()) {
                    List<Message> messagesList = new ArrayList<>();
                    // loop throw users value
                    for (DataSnapshot snapshot: dataSnapshot.getChildren()){
                        if(!getLoadBeforeKey().equals(snapshot.getKey())) { // if snapshot key = startAt key? don't add it again
                            Message message = snapshot.getValue(Message.class);
                            if (message != null) {
                                message.setKey(snapshot.getKey());
                            }
                            messagesList.add(message);
                        }
                    }

                    if(messagesList.size() != 0){
                        //callback.onResult(messagesList);
                        getLoadBeforeCallback().onResult(messagesList);
                        Log.d(TAG, "getMessagesBefore  List.size= " +  messagesList.size()+ " lastkey= "+messagesList.get(messagesList.size()-1).getKey());

                        // Create a reversed list to add messages to the beginning of totalItemsList
                        List<Message> reversedList = new ArrayList<>(messagesList);
                        Collections.reverse(reversedList);
                        for (int i = 0; i < reversedList.size(); i++) {
                            // Add messages to totalItemsList ArrayList to be used to get the initial key position
                            totalItemsList.add(0, reversedList.get(i));
                        }
                    }
                } else {
                    // no data
                    Log.w(TAG, "getMessagesBefore no users exist");
                }
                getListeners();
                isBeforeFirstLoaded =  false;
                Log.d(TAG, "end isBeforeFirstLoaded = "+ isBeforeFirstLoaded);
            }

            @Override
            public void onCancelled(DatabaseError databaseError) {
                // Getting Post failed, log a message
                Log.w(TAG, "getMessagesBefore:onCancelled", databaseError.toException());
            }
        };


        public static void setScrollDirection(int scrollDirection, int lastVisibleItem) {
            Log.d(TAG, "mScrollDirection = " + scrollDirection+ " lastVisibleItem= "+ lastVisibleItem);
            mScrollDirection = scrollDirection;
            mlastVisibleItem = lastVisibleItem;
        }

        // get initial data
        public void getMessages(String initialKey, final int size,
                                @NonNull final ItemKeyedDataSource.LoadInitialCallback<Message> callback) {

            Log.i(TAG, "getMessages initiated. initialKey= " +  initialKey);
            this.initialKey = initialKey;
            Query messagesQuery;
            isInitialFirstLoaded = true;

            ValueEventListener  initialMessagesListener = new ValueEventListener() {
                @Override
                public void onDataChange(DataSnapshot dataSnapshot) {
                    // [START_EXCLUDE]
                    Log.d(TAG, "start onDataChange. isInitialFirstLoaded = "+ isInitialFirstLoaded);

                    if (!isInitialFirstLoaded){
                        // Remove post value event listener
                        removeListeners();
                        Log.d(TAG, "usersChanged Invalidated removeEventListener");
                        //isInitialFirstLoaded =  true;
                        Log.d(TAG, "onInvalidated(). isInitialFirstLoaded = "+ isInitialFirstLoaded);
                        invalidatedCallback.onInvalidated();
                        return;
                    }

                    if (dataSnapshot.exists()) {
                        final List<Message> messagesList = new ArrayList<>();
                        // loop throw users value
                        for (DataSnapshot snapshot: dataSnapshot.getChildren()){
                            Message message = snapshot.getValue(Message.class);
                            if (message != null) {
                                message.setKey(snapshot.getKey());
                            }
                            messagesList.add(message);
                            // Add messages to totalItemsList ArrayList to be used to get the initial key position
                            totalItemsList.add(message);

                            //Log.d(TAG, "getMessage = "+ message.getMessage()+" getSnapshotKey= " +  snapshot.getKey());
                        }
                        if(messagesList.size() != 0){
                            callback.onResult(messagesList);
                            Log.d(TAG, "getMessages  List.size= " +  messagesList.size()+ " lastkey= "+messagesList.get(messagesList.size()-1).getKey() + " getInitialKey= "+ getInitialKey() );
                        }
                    } else {
                        // no data
                        Log.w(TAG, "getMessages no users exist");
                    }
                    isInitialFirstLoaded =  false;
                    Log.d(TAG, "end isInitialFirstLoaded = "+ isInitialFirstLoaded);
                }

                @Override
                public void onCancelled(DatabaseError databaseError) {
                    // Getting Post failed, log a message
                    Log.w(TAG, "loadPost:onCancelled", databaseError.toException());
                }
            };

            if (initialKey == null) {// if it's loaded for the first time. Key is null
                Log.d(TAG, "getMessages initialKey= " + initialKey);
                messagesQuery = mMessagesRef.orderByKey()//limitToLast to start from the last (page size) items
                        .limitToLast(size);

            } else {// not the first load. Key is the last seen key
                Log.d(TAG, "getMessages initialKey= " + initialKey);
                switch (mScrollDirection){
                    case REACHED_THE_BOTTOM:
                        Log.d(TAG, "messages query = REACHED_THE_BOTTOM");
                        messagesQuery = mMessagesRef.orderByKey()
                                .limitToLast(size);
                        break;
                    case REACHED_THE_TOP:
                        Log.d(TAG, "messages query = REACHED_THE_TOP");
                        messagesQuery = mMessagesRef.orderByKey()
                                .limitToFirst(size);
                        break;
                    default:
                        if(getInitialKeyPosition() >= mlastVisibleItem ){
                            // InitialKey is in the bottom, must load data from bottom to top
                            Log.d(TAG, "messages query = Load data from bottom to top");
                            messagesQuery = mMessagesRef.orderByKey()
                                    .endAt(initialKey)
                                    .limitToLast(size);

                        }else{
                            // InitialKey is in the top, must load data from top to bottom
                            Log.d(TAG, "messages query = Load data from top to bottom");
                            messagesQuery = mMessagesRef.orderByKey()
                                    .startAt(initialKey)
                                    .limitToFirst(size);
                        }
                        break;
                }
            }

            // Clear the list of total items to start all over
            totalItemsList.clear();

            messagesQuery.addValueEventListener(initialMessagesListener);
            mListenersList.add(new FirebaseListeners(messagesQuery, initialMessagesListener));
        }

    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...