Как использовать addOnScrollListener с разными списками в одном упражнении - PullRequest
0 голосов
/ 19 февраля 2019

В моем приложении есть два метода getData и getItemsByLabel.Каждый из них получает разные списки с помощью модифицированного метода обратного вызова, и я использовал метод навигационного ящика onNavigationItemSelected, чтобы каждый раз, когда пользователь нажимал на определенный элемент, в RecyclerView отображался другой список.

Проблема в том, что я использую метод addOnScrollListener для обнаружения поведения прокрутки из любого списка в RecyclerView, который вызывает наложение элементов в отображаемом списке.

Следовательно, проблема заключается в прокруткепроисходит сбой, элементы из основного списка и списка выбранной категории / элемента перекрываются.

Вот мой код.

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    Fabric.with(this, new Crashlytics());

    // Crashlytics.logException(new Exception("My first Android non-fatal error"));
    // I'm also creating a log message, which we'll look at in more detail later
    // Crashlytics.log("MainActivity started");

    swipeRefreshLayout = findViewById(R.id.swipeRefreshLayout);
    recyclerView = findViewById(R.id.recyclerView);
    manager = new LinearLayoutManager(this);
    emptyView = (TextView) findViewById(R.id.empty_view);
    progressBar = findViewById(R.id.spin_kit);
    adapter = new PostAdapter(this, items);
    recyclerView.setLayoutManager(manager);
    recyclerView.setAdapter(adapter);
    toolbar = findViewById(R.id.toolbar);
    setSupportActionBar(toolbar);
    getSupportActionBar().setDisplayShowTitleEnabled(false);
    toolbar.setTitle(R.string.home);

    DrawerLayout drawer = findViewById(R.id.drawer_layout);
    ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
            this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
    drawer.addDrawerListener(toggle);
    toggle.syncState();
    NavigationView navigationView = findViewById(R.id.nav_view);
    navigationView.getMenu().getItem(0).setChecked(true);
    navigationView.setNavigationItemSelectedListener(this);
    swipeRefreshLayout.setColorSchemeColors(getResources().getColor(R.color.colorPrimaryGreen));
    swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
        @Override
        public void onRefresh() {
            if (navigationView.getMenu().getItem(0).isChecked()) {
                if (Utils.hasNetworkAccess(MainActivity.this)) {
                    getData();
                } else {
                    Toast.makeText
                            (MainActivity.this, "You must connect to the Internet to update the list"
                                    , Toast.LENGTH_LONG).show();
                }
            } else {
                for (int i = 1; i < 7; i++) {
                    if (navigationView.getMenu().getItem(i).isChecked()) {
                        getItemsByLabel(navigationView.getMenu().getItem(i).getTitle().toString());
                    }
                }
            }

            new Handler().postDelayed(new Runnable() {
                @Override
                public void run() {
                    swipeRefreshLayout.setRefreshing(false);
                }
            }, 3000);
        }

    });

    recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
        @Override
        public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
            super.onScrollStateChanged(recyclerView, newState);
            if (newState == AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
                isScrolling = true;
                if (!recyclerView.canScrollVertically(1)) {
                    progressBar.setVisibility(View.GONE);
                }
            }
        }

        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
            super.onScrolled(recyclerView, dx, dy);
            if (dy > 0) {
                currentItems = manager.getChildCount();
                totalItems = manager.getItemCount();
                scrollOutItems = manager.findFirstVisibleItemPosition();
                if (isScrolling && (currentItems + scrollOutItems == totalItems)) {
                    isScrolling = false;
                    getData(); // This is where I call getData <--
                }
            }
        }
    });

    if (Utils.hasNetworkAccess(this)) {
        getData();
    } else {
        if (runtimeExceptionDaoItems == null || runtimeExceptionDaoItems.queryForAll().isEmpty()) {
            Toast.makeText(this, "There's no data", Toast.LENGTH_LONG).show();
        } else {
            items.addAll(runtimeExceptionDaoItems.queryForAll());
            Toast.makeText(this, "From Database", Toast.LENGTH_LONG).show();
        }
    }
}

long lastPress;
Toast backpressToast;

@Override
public void onBackPressed() {
    DrawerLayout drawer = findViewById(R.id.drawer_layout);
    if (drawer.isDrawerOpen(GravityCompat.START)) {
        drawer.closeDrawer(GravityCompat.START);
    } else {
        long currentTime = System.currentTimeMillis();
        if (currentTime - lastPress > 5000) {
            backpressToast = Toast.makeText(getBaseContext(), "Press back again to exit", Toast.LENGTH_LONG);
            backpressToast.show();
            lastPress = currentTime;
        } else {
            if (backpressToast != null) backpressToast.cancel();
            super.onBackPressed();
        }
    }
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.main, menu);
    SearchManager searchManager = (SearchManager) getSystemService(SEARCH_SERVICE);
    SearchView searchView = (SearchView) menu.findItem(R.id.app_bar_search).getActionView();
    searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
    searchView.setQueryHint(getResources().getString(R.string.searchForPosts));
    searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
        @Override
        public boolean onQueryTextSubmit(String keyword) {
            getItemsBySearch(keyword);
            return false;
        }

        @Override
        public boolean onQueryTextChange(String keyword) {
            return false;
        }
    });

    searchView.setOnCloseListener(() -> {
        emptyView.setVisibility(View.GONE);
        recyclerView.setVisibility(View.VISIBLE);
        getData();
        return false;
    });

    return true;
}

@SuppressWarnings("StatementWithEmptyBody")
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
    // Handle navigation view item clicks here.

    switch (item.getItemId()) {
        case R.id.home:
            getData();
            break;
        case R.id.accessory:
            toolbar.setTitle(R.string.accessory);
            getItemsByLabel("Accessory");
            break;
        case R.id.arcade:
            toolbar.setTitle(R.string.arcade);
            getItemsByLabel("Arcade");
            break;
        case R.id.fashion:
            toolbar.setTitle(R.string.fashion);
            getItemsByLabel("Fashion");
            break;
        case R.id.food:
            toolbar.setTitle(R.string.food);
            getItemsByLabel("Food");
            break;
        case R.id.heath:
            toolbar.setTitle(R.string.heath);
            getItemsByLabel("Heath");
            break;
        case R.id.lifeStyle:
            toolbar.setTitle(R.string.lifestyle);
            getItemsByLabel("Lifestyle");
            break;
        case R.id.sports:
            toolbar.setTitle(R.string.sports);
            getItemsByLabel("Sports");
            break;
        case R.id.settings:
            break;
    }

    DrawerLayout drawer = findViewById(R.id.drawer_layout);
    drawer.closeDrawer(GravityCompat.START);
    return true;
}

private void getData() {
    progressBar.setVisibility(View.VISIBLE);
    String url = BloggerAPI.BASE_URL + "?key=" + BloggerAPI.KEY;

    if (token != "") {
        url = url + "&pageToken=" + token;
    }
    if (token == null) {
        return;
    }

    final Call<PostList> postList = BloggerAPI.getService().getPostList(url);
    postList.enqueue(new Callback<PostList>() {
        @Override
        public void onResponse(@NonNull Call<PostList> call, @NonNull Response<PostList> response) {
            if (response.isSuccessful()) {
                progressBar.setVisibility(View.GONE);
                PostList list = response.body();
                if (list != null) {
                    token = list.getNextPageToken();
                    items.addAll(list.getItems());
                    adapter.notifyDataSetChanged();
            } else {
                progressBar.setVisibility(View.GONE);
                recyclerView.setVisibility(View.GONE);
                emptyView.setVisibility(View.VISIBLE);

                int sc = response.code();
                switch (sc) {
                    case 400:
                        Log.e("Error 400", "Bad Request");
                        break;
                    case 404:
                        Log.e("Error 404", "Not Found");
                        break;
                    default:
                        Log.e("Error", "Generic Error");
                }
            }
        }

        @Override
        public void onFailure(@NonNull Call<PostList> call, @NonNull Throwable t) {
            Toast.makeText(MainActivity.this, "Error occured", Toast.LENGTH_LONG).show();
            Log.e(TAG, "onFailure: " + t.toString());
            Log.e(TAG, "onFailure: " + t.getCause());
            progressBar.setVisibility(View.GONE);
            recyclerView.setVisibility(View.GONE);
            emptyView.setVisibility(View.VISIBLE);
        }
    });
}

//=============================================================================================

public void getItemsByLabel(String label) {
    progressBar.setVisibility(View.VISIBLE);
    String url = BloggerAPI.BASE_URL + "search?q=label:" + label + "&key=" + BloggerAPI.KEY;

    Log.e("Label :", url);

    if (token != "") {
        url = url + "&pageToken=" + token;
    }
    if (token == null) {
        return;
    }

    final Call<PostList> postList = BloggerAPI.getService().getPostList(url);
    postList.enqueue(new Callback<PostList>() {
        @Override
        public void onResponse(@NonNull Call<PostList> call, @NonNull Response<PostList> response) {
            if (response.isSuccessful()) {
                progressBar.setVisibility(View.GONE);
                items.clear();
                recyclerView.swapAdapter(adapter, false);
                PostList list = response.body();
                if (list != null) {
                    token = list.getNextPageToken();
                    items.addAll(list.getItems());
                    adapter.notifyDataSetChanged();
                }
            } else {
                progressBar.setVisibility(View.GONE);
                recyclerView.setVisibility(View.GONE);
                emptyView.setVisibility(View.VISIBLE);
                int sc = response.code();
                switch (sc) {
                    case 400:
                        Log.e("Error 400", "Bad Request");
                        break;
                    case 404:
                        Log.e("Error 404", "Not Found");
                        break;
                    default:
                        Log.e("Error", "Generic Error");
                }
            }
        }

        @Override
        public void onFailure(@NonNull Call<PostList> call, @NonNull Throwable t) {
            Toast.makeText(MainActivity.this, "Error occured", Toast.LENGTH_LONG).show();
            Log.e(TAG, "onFailure: " + t.toString());
            Log.e(TAG, "onFailure: " + t.getCause());
            progressBar.setVisibility(View.GONE);
            recyclerView.setVisibility(View.GONE);
            emptyView.setVisibility(View.VISIBLE);
        }
    });
}

Методы, которые я пытался решить эту проблему:

  1. Реализация ScrollListener внутри каждого метода getData и getItemsByLabel
  2. Создание фрагмента для каждого элемента в меню ящика навигации и реализация ScrollListener на нем.
  3. И, наконец, я включил цикл для onScrolled метода, чтобы определить, какой пункт в меню ящика проверен, чтобы получить свой собственный список.Но, к сожалению, никто не работает.

Ответы [ 4 ]

0 голосов
/ 12 марта 2019

Вы должны ... очистить основной список по каждой категории, щелкнуть и восстановить этот список для категории твердых частиц.

0 голосов
/ 25 февраля 2019

Я не думаю, что возникает проблема добавления общего addOnScrollListener к вашему RecyclerView.Я думаю, что эта проблема возникает из-за реализации потока управления и состояния гонки между потоками при получении списков с помощью вызовов API.Позвольте мне кратко описать мою идею.

Оба API, вызываемые в функциях getData и getItemsByLabel, являются асинхронными, и, следовательно, вы не можете гарантировать, когда вызов API вернется с данными.Поэтому давайте подумаем о следующем сценарии:

  1. getItemsByLabel вызывается, когда вы щелкаете по элементу в блоке навигации.
  2. Тем временем функция onScrolled вызывается как-то иAPI для getData срабатывает сразу.Функция
  3. getItemsByLabel вернулась с данными, очистила список items и начала вставлять новые данные в items.
  4. API getData, возвращенный одновременно с сразу последанные в списке items были очищены getItemsByLabel и начали добавлять элементы в items.
  5. Таким образом, список items содержит элементы как getData, так и getItemsByLabel.
  6. Реализация ArrayList не является поточно-ориентированной, поэтому она склонна к смешиванию данных из обоих ответов API.

Надеюсь, это объясняет проблему, с которой вы столкнулисьиметь.Чтобы избежать этой проблемы, вы можете рассмотреть возможность использования переменной boolean, например getItemsByLabelCalled, которая по умолчанию может иметь значение false.Когда элемент щелкается в панели навигации, установите значение getItemsByLabelCalled = true.Затем проверьте это значение, прежде чем добавлять элементы в список items в следующих случаях.

  1. Внутри функции getData.if(getItemsByLabelCalled) return
  2. Внутри функции getItemsByLabel установите и сбросьте значение getItemsByLabelCalled.Пожалуйста, проверьте следующий код в разделе ОБНОВЛЕНИЕ.

Надеюсь, это поможет!

ОБНОВЛЕНИЕ

Чтобы дать вам демонстрацию того, какие изменения вы должны внести:

Возьмите boolean для отслеживания, когда функцияназывается.

private boolean getItemsByLabelCalled = false; 

Теперь измените функцию getData следующим образом.

private void getData() {
    if(getItemsByLabelCalled) return;

    // Other statements are the same as before
}

Теперь измените функцию getItemsByLabel, чтобы установить переменную getItemsByLabelCalled.

public void getItemsByLabel(String label) {

    // Here is the change
    if(getItemsByLabelCalled) return; 
    else getItemsByLabelCalled = true;

    progressBar.setVisibility(View.VISIBLE);
    String url = BloggerAPI.BASE_URL + "search?q=label:" + label + "&key=" + BloggerAPI.KEY;

    Log.e("Label :", url);

    if (token != "") {
        url = url + "&pageToken=" + token;
    }
    if (token == null) {
        return;
    }

    final Call<PostList> postList = BloggerAPI.getService().getPostList(url);
    postList.enqueue(new Callback<PostList>() {
        @Override
        public void onResponse(@NonNull Call<PostList> call, @NonNull Response<PostList> response) {
            if (response.isSuccessful()) {
                progressBar.setVisibility(View.GONE);
                items.clear();
                recyclerView.swapAdapter(adapter, false);
                PostList list = response.body();
                if (list != null) {
                    token = list.getNextPageToken();
                    items.addAll(list.getItems());
                    adapter.notifyDataSetChanged();
                }

                // Reset again here
                getItemsByLabelCalled = false;
            } else {
                // Reset again here
                getItemsByLabelCalled = false;

                progressBar.setVisibility(View.GONE);
                recyclerView.setVisibility(View.GONE);
                emptyView.setVisibility(View.VISIBLE);
                int sc = response.code();
                switch (sc) {
                    case 400:
                        Log.e("Error 400", "Bad Request");
                        break;
                    case 404:
                        Log.e("Error 404", "Not Found");
                        break;
                    default:
                        Log.e("Error", "Generic Error");
                }
            }
        }

        @Override
        public void onFailure(@NonNull Call<PostList> call, @NonNull Throwable t) {
            Toast.makeText(MainActivity.this, "Error occured", Toast.LENGTH_LONG).show();
            Log.e(TAG, "onFailure: " + t.toString());
            Log.e(TAG, "onFailure: " + t.getCause());
            progressBar.setVisibility(View.GONE);
            recyclerView.setVisibility(View.GONE);
            emptyView.setVisibility(View.VISIBLE);

            // Reset again here
            getItemsByLabelCalled = false;
        }
    });
}

Я думаю, что больше никаких изменений не требуется, хотя я не уверен.Дай мне знать, если это работает.

0 голосов
/ 12 марта 2019

Вы можете использовать флаг, когда настраиваете список в recycleview и настраиваете прослушиватель согласно условиям

    protected void getRefreshView() {
    int childs = getChildCount();
    if (childs > 0) {
        View childView = getChildAt(0);
        if (childView instanceof RecyclerView) {
            mRecyclerView = (RecyclerView) childView;
            mRecyclerView.setOnScrollListener(mScrollListener);
            mRecyclerView.setOnScrollListener(new OnScrollListener() {
            });
        }
    }
}
0 голосов
/ 25 февраля 2019

Я думаю, что проблема возникает из-за recyclerView.swapAdapter(adapter, false);

swapAdapter используется для замены адаптера на новый.Вы передаете тот же адаптер, поэтому этот вызов бесполезен.Даже если вы хотите установить новый адаптер, логический параметр должен иметь значение true, чтобы позволить RecyclerView перерабатывать все существующие представления.

...