In this part I try to add one feature in my app and fix one problem, I would like to save the recyclerview position so when click and navigate to details fragment and click back, this should back to the same position, the issue here is when I click back I see the items overlapping, there are about 8 items and it starts from next list position, while I searching for the solution of this issue, I found this answer then I decided to add the dependencies of recyclerview separately implementation "androidx.recyclerview:recyclerview:1.2.1"
to use this feature
adapter.setStateRestorationPolicy(RecyclerView.Adapter.StateRestorationPolicy.PREVENT_WHEN_EMPTY);
but it doesn't work, the next GIF describe the problem
and this example of the app with this feature
HomeFragment
@AndroidEntryPoint
public class HomeFragment extends Fragment {
private FragmentHomeBinding binding;
private PostViewModel postViewModel;
public static final String TAG = "HomeFragment";
private PostAdapter adapter;
private List<Item> itemArrayList;
private GridLayoutManager titleLayoutManager, gridLayoutManager;
WrapContentLinearLayoutManager layoutManager;
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
binding.homeRecyclerView.addOnItemTouchListener(new RecyclerItemClickListener(requireContext(),
binding.homeRecyclerView, new RecyclerItemClickListener.OnItemClickListener() {
@Override
public void onItemClick(View view, int position) {
Item item = itemArrayList.get(position);
if (Objects.requireNonNull
(Navigation.findNavController(requireView())
.getCurrentDestination()).getId() == R.id.nav_home) {
Navigation.findNavController(requireView())
.navigate(HomeFragmentDirections.actionNavHomeToDetailsFragment(item));
}
}
@Override
public void onLongItemClick(View view, int position) {
}
}));
}
public View onCreateView(@NonNull LayoutInflater inflater,
@Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
binding = FragmentHomeBinding.inflate(inflater, container, false);
setHasOptionsMenu(true);
postViewModel = new ViewModelProvider(this).get(PostViewModel.class);
postViewModel.finalURL.setValue(Constants.getBaseUrl() + "?key=" + Constants.getKEY());
itemArrayList = new ArrayList<>();
adapter = new PostAdapter(getContext(), itemArrayList, this, postViewModel);
layoutManager = new WrapContentLinearLayoutManager(requireContext(),
LinearLayoutManager.VERTICAL, false);
titleLayoutManager = new GridLayoutManager(getContext(), 2);
gridLayoutManager = new GridLayoutManager(getContext(), 3);
// binding.homeRecyclerView.setAdapter(adapter);
binding.shimmerLayout.setVisibility(View.VISIBLE);
binding.homeRecyclerView.setVisibility(View.INVISIBLE);
adapter.setStateRestorationPolicy(RecyclerView.Adapter.StateRestorationPolicy.PREVENT_WHEN_EMPTY);
postViewModel.recyclerViewLayoutMT.observe(getViewLifecycleOwner(), layout -> {
Log.w(TAG, "getSavedLayout: called");
switch (layout) {
case "cardLayout":
binding.loadMoreBtn.setVisibility(View.VISIBLE);
binding.homeRecyclerView.setLayoutManager(layoutManager);
binding.homeRecyclerView.setAdapter(adapter);
adapter.setViewType(0);
break;
case "cardMagazineLayout":
binding.loadMoreBtn.setVisibility(View.VISIBLE);
binding.homeRecyclerView.setLayoutManager(layoutManager);
binding.homeRecyclerView.setAdapter(adapter);
adapter.setViewType(1);
break;
case "titleLayout":
binding.loadMoreBtn.setVisibility(View.GONE);
binding.homeRecyclerView.setLayoutManager(titleLayoutManager);
binding.homeRecyclerView.setAdapter(adapter);
adapter.setViewType(2);
break;
case "gridLayout":
binding.loadMoreBtn.setVisibility(View.GONE);
binding.homeRecyclerView.setLayoutManager(gridLayoutManager);
binding.homeRecyclerView.setAdapter(adapter);
adapter.setViewType(3);
}
});
if (Utils.hasNetworkAccess(requireContext())) {
postViewModel.getPosts();
postViewModel.postListMutableLiveData.observe(getViewLifecycleOwner(), postList -> {
itemArrayList.addAll(postList.getItems());
binding.shimmerLayout.stopShimmer();
binding.shimmerLayout.setVisibility(View.GONE);
binding.homeRecyclerView.setVisibility(View.VISIBLE);
adapter.notifyDataSetChanged();
Log.e(TAG, "ItemsArrayList :" + itemArrayList.get(0).getTitle());
});
} else {
binding.shimmerLayout.setVisibility(View.VISIBLE);
// binding.shimmerLayout.startShimmer();
if (postViewModel.getAllItemsFromDataBase == null) {
noInternetConnectionLayout();
} else {
// Log.e(TAG, "RoomDB Items size :" + itemsDatabase.itemDAO().getAlItems());
binding.shimmerLayout.stopShimmer();
binding.shimmerLayout.setVisibility(View.GONE);
binding.emptyView.setVisibility(View.GONE);
binding.homeRecyclerView.setVisibility(View.VISIBLE);
postViewModel.getAllItemsFromDataBase.observe(getViewLifecycleOwner(), items -> {
if (items.isEmpty()) {
noInternetConnectionLayout();
} else {
binding.loadMoreBtn.setVisibility(View.GONE);
itemArrayList.addAll(items);
adapter.notifyDataSetChanged();
}
});
}
}
postViewModel.errorCode.observe(getViewLifecycleOwner(), errorCode -> {
if (errorCode == 400) {
Snackbar.make(requireView(), R.string.lastPost, Snackbar.LENGTH_LONG).show();
} else {
binding.homeRecyclerView.setVisibility(View.INVISIBLE);
binding.emptyView.setVisibility(View.VISIBLE);
}
});
binding.loadMoreBtn.setOnClickListener(view -> {
AlertDialog dialog = Utils.setProgressDialog(requireContext());
postViewModel.isLoading.observe(getViewLifecycleOwner(), isLoading -> {
if (isLoading) {
dialog.show();
} else {
dialog.dismiss();
}
});
if (Utils.hasNetworkAccess(requireContext())) {
postViewModel.getPosts();
// Log.w(TAG, "loadMoreBtn: " + dialog.isShowing());
} else {
postViewModel.isLoading.postValue(true);
postViewModel.getAllItemsFromDataBase.getValue();
postViewModel.isLoading.postValue(false);
}
});
return binding.getRoot();
}
private void noInternetConnectionLayout() {
binding.shimmerLayout.stopShimmer();
binding.shimmerLayout.setVisibility(View.GONE);
binding.homeRecyclerView.setVisibility(View.GONE);
binding.emptyView.setVisibility(View.VISIBLE);
}
@Override
public void onDestroyView() {
super.onDestroyView();
itemArrayList.clear();
binding = null;
}
@Override
public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
inflater.inflate(R.menu.main, menu);
super.onCreateOptionsMenu(menu, inflater);
SearchManager searchManager = (SearchManager) requireContext().getSystemService(Context.SEARCH_SERVICE);
SearchView searchView = (SearchView) menu.findItem(R.id.app_bar_search).getActionView();
searchView.setSearchableInfo(searchManager.getSearchableInfo(requireActivity().getComponentName()));
searchView.setQueryHint(getResources().getString(R.string.searchForPosts));
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String keyword) {
if (keyword.isEmpty()) {
Snackbar.make(requireView(), "please enter keyword to search", Snackbar.LENGTH_SHORT).show();
}
if (Utils.hasNetworkAccess(requireContext())) {
itemArrayList.clear();
postViewModel.getItemsBySearch(keyword);
adapter.notifyDataSetChanged();
} else {
postViewModel.getItemsBySearchInDB(keyword);
postViewModel.getItemsBySearchMT.observe(getViewLifecycleOwner(), items ->
{
Log.d(TAG, "onQueryTextSubmit database called");
itemArrayList.clear();
itemArrayList.addAll(items);
adapter.notifyDataSetChanged();
}
);
}
return false;
}
@Override
public boolean onQueryTextChange(String newText) {
return false;
}
});
searchView.setOnCloseListener(() -> {
if (Utils.hasNetworkAccess(requireContext())) {
Log.d(TAG, "setOnCloseListener: called");
itemArrayList.clear();
binding.emptyView.setVisibility(View.GONE);
binding.homeRecyclerView.setVisibility(View.VISIBLE);
postViewModel.getPosts();
adapter.notifyDataSetChanged();
} else {
Log.d(TAG, "setOnCloseListener: called");
binding.emptyView.setVisibility(View.GONE);
binding.homeRecyclerView.setVisibility(View.VISIBLE);
postViewModel.getAllItemsFromDataBase.observe(getViewLifecycleOwner(), items ->
{
itemArrayList.addAll(items);
adapter.notifyDataSetChanged();
}
);
}
return false;
});
postViewModel.searchError.observe(getViewLifecycleOwner(), searchError -> {
if (searchError) {
Toast.makeText(requireContext(),
"There's no posts with this keyword", Toast.LENGTH_LONG).show();
}
});
}
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
if (item.getItemId() == R.id.change_layout) {
changeAndSaveLayout();
return true;
}
return super.onOptionsItemSelected(item);
}
Try initializing the adapter in onCreate
instead of onViewCreated
Then you can set the adapter on recyclerview in onViewCreated
.
Also use try using LiveData Event Wrapper : https://stackoverflow.com/a/51762972/9854554
It will prevent livedata observer from getting data again on fragment resume (when we subscribe again on the livedata)
您在onViewCreated,要求每次设置回收视图的数据,我建议视图模型更好的生命周期移交,使用LiveData或Stateflow中的视图模型来证明你的数据的完整性
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.