在我的应用程序中,有两种方法getData
和getItemsByLabel
。每个人都通过改型的Callback方法获得不同的列表,并且我使用了导航抽屉方法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);
}
});
}
我尝试解决此问题的方法:
ScrollListener
和getData
内的getItemsByLabel
实现ScrollListener
。 onScrolled
方法上放置了循环,以检测抽屉式菜单上的哪个项目被选中以获取其自己的列表。但不幸的是,没有人在工作。 最佳答案
我认为将常见的addOnScrollListener
添加到RecyclerView
不会出现问题。我认为这个问题是由于控制流的实现以及使用API调用获取列表时线程之间的竞争状况而发生的。让我简要地描述一下我的想法。getData
和getItemsByLabel
函数中调用的两个API都是异步的,因此您无法确定API调用何时随数据返回。因此,让我们考虑以下情形:
单击导航抽屉中的项目时调用的
getItemsByLabel
。 onScrolled
函数,并立即触发了getData
的API。 getItemsByLabel
函数返回数据,清除了items
列表,并开始将新数据插入items
。 getData
清除了items
列表中的数据并开始在getItemsByLabel
中添加元素之后,同时返回了items
API。 items
列表具有getData
和getItemsByLabel
中的元素。 ArrayList
实现不是线程安全的,因此很容易在两个API响应中混入数据。 我希望这可以解释您遇到的问题。为避免此问题,您可以考虑使用
boolean
变量(例如getItemsByLabelCalled
),默认情况下其值可能为false
。在导航抽屉中单击某个项目时,请设置getItemsByLabelCalled = true
的值。然后,在以下情况下,在将元素添加到items
列表中之前检查此值。getData
函数内部。 if(getItemsByLabelCalled) return
getItemsByLabel
函数内部,设置和重置getItemsByLabelCalled
的值。请在UPDATE部分中检查以下代码。 希望对您有所帮助!
更新
为了给您示范应该进行哪些更改的示例,请执行以下操作:
以
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;
}
});
}
我不确定不需要更多更改,尽管我不确定。让我知道是否可行。