Am unsure how to correctly use ViewModel with RecyclerView to maintain state on configuration change, eg rotation. With code as is, rotating the screen does not restore the list to the pre-rotate state, it reverts to the initial blank screen.
Here is my MainActivity with the ViewModel and RecyclerView both set up in onCreate. I'm using dummy data atm to try to get it working, see addDummyComposerData() which adds the data to the ViewModel and calls NotifyDataSetChanged() on the Adapter:
public class MainActivity extends AppCompatActivity {
private TabLayout mMainTabs;
private RecyclerView mRecyclerList;
private RecyclerView.LayoutManager mListLayoutManager;
private MyListAdapter mListAdapter;
private List<Composer> mComposerList = new ArrayList<>();
private MainActivityViewModel mViewModel;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mViewModel = ViewModelProviders.of(this).get(MainActivityViewModel.class);
mViewModel.SetDataListComp(mComposerList);
Toolbar myToolbar = (Toolbar) findViewById(R.id.my_toolbar);
setSupportActionBar(myToolbar);
mRecyclerList = (RecyclerView)findViewById(R.id.recycler_list);
mListLayoutManager = new LinearLayoutManager(getApplicationContext());
mRecyclerList.setLayoutManager(mListLayoutManager);
mListAdapter = new MyListAdapter(this,mViewModel.GetDataListComp());
mRecyclerList.setAdapter(mListAdapter);
mMainTabs = (TabLayout) findViewById(R.id.main_tabs);
mMainTabs.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
#Override
public void onTabSelected(TabLayout.Tab tab) {
switch (tab.getPosition()){
case 0://composer
addDummyComposerData();
}
}
#Override
public void onTabUnselected(TabLayout.Tab tab) {
mComposerList.clear();
mListAdapter.notifyDataSetChanged();
}
#Override
public void onTabReselected(TabLayout.Tab tab) {
switch (tab.getPosition()){
case 0://composer
addDummyComposerData();
}
}
});
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.main, menu);
return true;
}
private void addDummyComposerData(){
mComposerList.clear();
Composer composer = new Composer("Albinoni, Tomaso (1671-1751)");
mComposerList.add(composer);
composer = new Composer("Bach, Johann Sebastian (1685-1750)");
mComposerList.add(composer);
//etc etc
mViewModel.SetDataListComp(mComposerList);
mListAdapter.notifyDataSetChanged();
}
}
Here is the ViewModel class:
public class MainActivityViewModel extends ViewModel {
private MusicQuery mCurrentQuery;
private List<Composer> mDataListComp;
public void SetCurrentQuery(MusicQuery query){
mCurrentQuery = query;
}
public MusicQuery GetCurrentQuery(){
return mCurrentQuery;
}
public void SetDataListComp (List<Composer> list){
mDataListComp = list;
}
public List<Composer> GetDataListComp(){
return mDataListComp;
}
}
And the ListAdapter:
public class MyListAdapter extends RecyclerView.Adapter<MyListAdapter.MyViewHolder> {
private List<Composer> composerList;
private int selectedPos = RecyclerView.NO_POSITION;
private MainActivity mMainActivityContext;
private MainActivityViewModel mMainActivityViewModel;
public class MyViewHolder extends RecyclerView.ViewHolder {
private static final String TAG = "UI Output";
private String selectedText;
public TextView mComposerView;
public MyViewHolder(View v) {
super(v);
mComposerView = (TextView) itemView.findViewById(R.id.composer_name);
}
}
public MyListAdapter(Context context, List<Composer> composerList){
mMainActivityContext = (MainActivity) context;
this.composerList = composerList;
}
#Override
public MyViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
View mComposerView = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.composer_layout,viewGroup,false);
//mComposerView.setOnClickListener(mClickListener);
return new MyViewHolder(mComposerView);
}
#Override
public void onBindViewHolder(MyViewHolder holder, int position){
Composer mComposer = composerList.get(position);
holder.mComposerView.setText(mComposer.getName());
holder.itemView.setSelected(selectedPos == position);
}
#Override
public int getItemCount(){
if(composerList != null){
return composerList.size();
} else {
return 0;
}
}
}
Should I be referencing the ViewModel from inside the Adapter?
You should remove the mViewModel.SetDataListComp(mComposerList); line inside the onCreate() method. This is resetting the list on rotation.
Some explanation:
When the screen rotates, the activity is being destroyed and recreated.
This means that the onCreate() method will be called again.
And because of this, the above line is resetting the list data of the ViewModel.
Only store the list in the ViewModel as this is where the data should be stored, and survives te rotation change. So remove the private List<Composer> mComposerList = new ArrayList<>(); from the MainActivity. There is no need to store the list in the Activity, only the ViewModel should modify it.
Solution:
As for initializing the list, we can do the same in the ViewModel (or we can initialize it in the constructor):
// Option 1: Initializing it directly
private List<Composer> mComposerList = new ArrayList<>();
// Option 2: Initializing it via the constructor
public MainActivityViewModel() {
mComposerList = new ArrayList<>();
}
Next, just modify the list when needed with the viewModel.SetDataListComp(<list>) method that you already have. It should update the list inside the view model. Now, when the activity is rotated, the viewmodel still has the list data and can pass it again to the mainactivity (with the viewModel.GetDataListComp()method).
Related
I've added swipe to my app, to delete specified note. For couple of days, I've facing problem with displaying data after swipe. For clarify:
Let's say, we have two items in recycler view. Whenever we swipe one of them, the second one should be visible, but it's not until I'll re-run the app. How I may solve it?
Main Activity
public class MainActivity extends AppCompatActivity implements MemoAdapter.OnNoteListener {
private static final String TAG = "MainActivity";
//Vars
private ArrayList<Note> mNotes = new ArrayList<>();
private MemoRepository mRepository;
private MemoAdapter mMemoAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mRepository = new MemoRepository(this);
Toolbar toolbar = (Toolbar) findViewById(R.id.memoToolbar);
setSupportActionBar(toolbar);
setTitle("My memos");
initRecyclerView();
}
//This method would be called after getting result from memo_content such as new memo, or edited existing memo.
#Override
protected void onResume() {
super.onResume();
getMemos();
}
private void getMemos(){
mRepository.getAllMemos().observe(this, new Observer<List<Note>>() {
#Override
public void onChanged(List<Note> notes) {
if (mNotes.size() > 0){
notes.clear();
}
if (notes != null){
mNotes.addAll(notes);
mMemoAdapter.watchMemoChanges((ArrayList<Note>) notes);
}
}
});
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.icon_menu, menu);
return super.onCreateOptionsMenu(menu);
}
#Override
public boolean onOptionsItemSelected(#NonNull MenuItem item) {
switch (item.getItemId()){
case R.id.addNewNote:
Toast.makeText(this, "Click!", Toast.LENGTH_SHORT).show();
startActivity(new Intent(this, memo_content.class));
break;
case R.id.deleteAllNotes:
Toast.makeText(this, "Delete!", Toast.LENGTH_SHORT).show();
mRepository.deleteAllMemos();
break;
}
return super.onOptionsItemSelected(item);
}
private void initRecyclerView(){
//UI
RecyclerView mRecyclerView = findViewById(R.id.recyclerView);
mRecyclerView.setHasFixedSize(true);
RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(this);
mMemoAdapter = new MemoAdapter(mNotes, this);
new ItemTouchHelper(itemTouch).attachToRecyclerView(mRecyclerView);
mRecyclerView.setLayoutManager(mLayoutManager);
mRecyclerView.setAdapter(mMemoAdapter);
}
#Override
public void onMemoClick(int position) {
Intent intent = new Intent(this, memo_content.class);
intent.putExtra("memo_content", mNotes.get(position));
startActivity(intent);
}
private ItemTouchHelper.SimpleCallback itemTouch = new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.START | ItemTouchHelper.LEFT) {
#Override
public boolean onMove(#NonNull RecyclerView recyclerView, #NonNull RecyclerView.ViewHolder viewHolder, #NonNull RecyclerView.ViewHolder target) {
return false;
}
#Override
public void onSwiped(#NonNull RecyclerView.ViewHolder viewHolder, int direction) {
mRepository.deleteMemo(mNotes.get(viewHolder.getAdapterPosition()));
Log.d(TAG, "onSwiped: "+mNotes.get(viewHolder.getAdapterPosition()));
}
};
}
Adapter
public class MemoAdapter extends RecyclerView.Adapter<MemoAdapter.MemoViewHolder> {
private ArrayList<Note> mNotes;
private OnNoteListener mListener;
public class MemoViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
TextView title, timestamp;
private MemoViewHolder(#NonNull final View itemView) {
super(itemView);
this.title = (TextView) itemView.findViewById(R.id.title);
this.timestamp = (TextView) itemView.findViewById(R.id.timestamp);
itemView.setOnClickListener(this);
}
#Override
public void onClick(View v) {
mListener.onMemoClick(getAdapterPosition());
}
}
public MemoAdapter(ArrayList<Note> notes, OnNoteListener listener) {
this.mNotes = notes;
this.mListener = listener;
}
#NonNull
#Override
public MemoViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.single_memo, parent, false);
return new MemoViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull MemoViewHolder holder, int position) {
holder.title.setText(mNotes.get(position).getTitle());
holder.timestamp.setText(mNotes.get(position).getTimestamp());
}
public void watchMemoChanges(ArrayList<Note> notes){
this.mNotes = notes;
notifyDataSetChanged();
}
#Override
public int getItemCount() {
return mNotes.size();
}
public interface OnNoteListener{
void onMemoClick(int position);
}
Short answer:
You need to remove the statement notes.clear() when you receive a change in the LiveData list from the database via the observer.
Detailed answer
When you first run your app, it will show all right list because the condition if (mNotes.size() > 0) is not met, and so the received list won't be cleared via notes.clear(), so the RecyclerView will display the right data.
But when you delete a note, then when the observer is triggered again with the new list, the condition if (mNotes.size() > 0) will be met, so you will clear the list that is coming from the database before feeding the adapter with it, so the RecyclerView will be free of data.
So to solve this please replace notes.clear(); with mNotes.clear();
So the right code will be:
private void getMemos(){
mRepository.getAllMemos().observe(this, new Observer<List<Note>>() {
#Override
public void onChanged(List<Note> notes) {
if (mNotes.size() > 0){
mNotes.clear();
}
if (notes != null){
mNotes.addAll(notes);
mMemoAdapter.watchMemoChanges((ArrayList<Note>) notes);
}
}
});
}
Wish that help you out.
I didn't examine the rest of code, please let me know if there is another issue to help more
since several days I try to handle data in recyclerview in viewpager2. The viewpager has an adapter managing the data in recyclerview. But everything I try to do seems to not work. Maybe I missundersteand the purpose or something. I hope you can help me.
This activity manages the viewpager and its adapter. It sends the data to the inner recyclerview:
public class AudioFilePanel extends AppCompatActivity
{
private String currentTab;
private ViewPagerAdapter adapter;
private ViewPager2 viewPager;
private TabLayout tabLayout;
private Map<String, List<String>> content;
private String path;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_audio_file_panel);
viewPager = findViewById(R.id.view_pager2);
tabLayout = findViewById(R.id.tabs);
Button addFilesByTag = findViewById(R.id.add_files_with_tag);
if (null == currentTab)
{
currentTab = "music";
}
content = listByTag();
adapter = new ViewPagerAdapter(getApplicationContext(), new ArrayList<>(content.values()));
viewPager.setAdapter(adapter);
new TabLayoutMediator(tabLayout, viewPager,
(tab, position) -> tab.setText(content.keySet().toArray()[position].toString())).attach();
tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener()
{
#Override
public void onTabSelected(TabLayout.Tab tab)
{
currentTab = tab.getText().toString();
}
#Override
public void onTabUnselected(TabLayout.Tab tab)
{
}
#Override
public void onTabReselected(TabLayout.Tab tab)
{
}
});
addFilesByTag.setOnClickListener(l ->
{
Intent fileBrowser = new Intent(AudioFilePanel.this, FileBrowser.class);
startActivityForResult(fileBrowser, 1);
});
}
private void updateViweData()
{
content = listByTag();
adapter = new ViewPagerAdapter(getApplicationContext(), new ArrayList<>(content.values()));
viewPager.setAdapter(adapter);
}
private Map<String, List<String>> listByTag()
{
Map<String, List<String>> result = new HashMap<>();
DirectoryDao dao = new DirectoryDao(getApplicationContext());
String[] categories = {"music", "ambience", "effect"};
for (String category : categories)
{
List<String> directories =
dao.getDirectoriesForCategory(category).stream().map(Directory::getPath).collect(Collectors.toList());
result.put(category, directories);
}
return result;
}
This is the view pager adapter. It takes the data directly from the activity and should trigger data update in the inner recyclerview every time when an item will be clicked. See line 118-128:
public class ViewPagerAdapter extends RecyclerView.Adapter<ViewPagerAdapter.ViewHolder>
{
private List<List<String>> filesListsByCategory;
private LayoutInflater mInflater;
private Context ctx;
private ItemListAdapter adapter;
private List<String> categoryFiles;
public ViewPagerAdapter(Context context, List<List<String>> data)
{
this.mInflater = LayoutInflater.from(context);
this.filesListsByCategory = data;
this.ctx = context;
}
#NotNull
#Override
public ViewHolder onCreateViewHolder(#NotNull ViewGroup parent, int viewType)
{
return new ViewHolder(mInflater.inflate(R.layout.item_viewpager, parent, false));
}
#Override
public void onBindViewHolder(ViewHolder holder, int position)
{
FileBrowserService fbs = new FileBrowserService();
categoryFiles = filesListsByCategory.get(position);
adapter = new ItemListAdapter(categoryFiles, new ItemList.OnListFragmentInteractionListener()
{
#Override
public void onListFragmentInteraction(String item)
{
categoryFiles = fbs.getFiles(categoryFiles.get(position));
categoryFiles.add(0, "previous directory");
updateUi(adapter, holder);
}
});
holder.myView.setAdapter(adapter);
}
private void updateUi(ItemListAdapter adapter, ViewHolder holder)
{
adapter.notifyDataSetChanged();
holder.myView.setAdapter(adapter);
}
#Override
public int getItemCount()
{
return filesListsByCategory.size();
}
class ViewHolder extends RecyclerView.ViewHolder
{
RecyclerView myView;
RelativeLayout relativeLayout;
ViewHolder(View itemView)
{
super(itemView);
myView = itemView.findViewById(R.id.my_list);
myView.setLayoutManager(new LinearLayoutManager(ctx));
relativeLayout = itemView.findViewById(R.id.container);
}
}
}
It is the adapter of inner recyclerview. It represents just a list of elements:
public class ItemListAdapter extends RecyclerView.Adapter<ItemListAdapter.ViewHolder>
{
private List<String> files;
private final OnListFragmentInteractionListener mListener;
public ItemListAdapter(List<String> items, OnListFragmentInteractionListener listener)
{
files = items;
mListener = listener;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
{
return new ViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.fragment_item, parent, false));
}
#Override
public void onBindViewHolder(final ViewHolder holder, int position)
{
holder.file = files.get(position);
holder.mContentView.setText(files.get(position));
holder.mView.setOnClickListener(v ->
{
if (null != mListener)
{
// Notify the active callbacks interface (the activity, if the
// fragment is attached to one) that an item has been selected.
mListener.onListFragmentInteraction(holder.file);
}
});
}
#Override
public int getItemCount()
{
return files.size();
}
class ViewHolder extends RecyclerView.ViewHolder
{
private final View mView;
private final TextView mContentView;
private String file;
private ViewHolder(View view)
{
super(view);
mView = view;
mContentView = view.findViewById(R.id.content);
}
}
}
The last thing is the ItemList fragment.
public class ItemList extends Fragment
{
// TODO: Customize parameter argument names
private static final String ARG_COLUMN_COUNT = "column-count";
// TODO: Customize parameters
private int mColumnCount = 1;
private OnListFragmentInteractionListener mListener;
/**
* Mandatory empty constructor for the fragment manager to instantiate the fragment (e.g. upon screen orientation
* changes).
*/
public ItemList()
{
}
// TODO: Customize parameter initialization
#SuppressWarnings("unused")
public static ItemList newInstance(int columnCount)
{
ItemList fragment = new ItemList();
Bundle args = new Bundle();
args.putInt(ARG_COLUMN_COUNT, columnCount);
fragment.setArguments(args);
return fragment;
}
#Override
public void onAttach(Context context)
{
super.onAttach(context);
if (context instanceof OnListFragmentInteractionListener)
{
mListener = (OnListFragmentInteractionListener) context;
} else
{
throw new RuntimeException(context.toString() + " must implement OnListFragmentInteractionListener");
}
}
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
if (getArguments() != null)
{
mColumnCount = getArguments().getInt(ARG_COLUMN_COUNT);
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View view = inflater.inflate(R.layout.fragment_item_list, container, false);
if (view instanceof RecyclerView)
{
Context context = view.getContext();
RecyclerView recyclerView = (RecyclerView) view;
if (mColumnCount <= 1)
{
recyclerView.setLayoutManager(new LinearLayoutManager(context));
} else
{
recyclerView.setLayoutManager(new GridLayoutManager(context, mColumnCount));
}
recyclerView.setAdapter(new ItemListAdapter(getArguments().getStringArrayList("list"), mListener));
}
return view;
}
#Override
public void onDetach()
{
super.onDetach();
mListener = null;
}
/**
* This interface must be implemented by activities that contain this fragment to allow an interaction in this
* fragment to be communicated to the activity and potentially other fragments contained in that activity.
* <p/>
* See the Android Training lesson
* <a href= "http://developer.android.com/training/basics/fragments/communicating.html"
* >Communicating with Other Fragments</a> for more information.
*/
public interface OnListFragmentInteractionListener
{
// TODO: Update argument type and name
void onListFragmentInteraction(String item);
}
}
When I click, the data won't update. What I would like to have is:
This is what you have to do
add this method into your adapter class
public void updateData(List<String> filesList, int flag) {
if (flag == 0) { //append
for (int i = 0; i < filesList.size(); i++) {
files.add(messageList.get(i));
notifyItemInserted(getItemCount());
}
} else { //clear all
files.clear();
notifyDataSetChanged();
}
}
Then whenever you need to update recycle view call like below
mItemListAdapter.updateData(yourNewListOfItems, 0);
if you need to reset recycle-view call like below
mItemListAdapter.updateData(null, 1);
Add notifyDataSetChanged() in your adapter
In my Android application I'm using a TabLayout with a FragmentStatePagerAdapter to show dynamically generated fragments.
It shows correctly the fragments the first time, but when going back to some that are already viewed and are not the adjacent ones it shows a blank page...
I've looked into this and there seems to be two way of fixing this:
Using FragmentStatePagerAdapter instead of FragmentPagerAdapter, which I already did and it didn't change anything.
Change
adapter = new FragmentStatePagerAdapter(*getSupportFragmentManager*());
into:
adapter = new FragmentStatePagerAdapter(*getChildFragmentManager*());
that I can't do because I'm in AppCompatActivity and this method is in Fragment...
Is there a way to solve this issue without extending this activity out of FragmentActivity that would screw up other stuff in my project?
Code:
private class SubsPagerAdapter extends FragmentStatePagerAdapter {
private final ArrayList<Fragment> fragmentsList = new ArrayList<>();
private final ArrayList<String> titleList = new ArrayList<>();
SubsPagerAdapter(FragmentManager manager) {
super(manager);
}
#Override
public Fragment getItem(int position) {
return fragmentsList.get(position);
}
#Override
public int getCount() {
return fragmentsList.size();
}
void addFragment(Fragment fragment, String title) {
fragmentsList.add(fragment);
titleList.add(title);
}
#Override
public CharSequence getPageTitle(int position) {
return titleList.get(position);
}
#Override
public Parcelable saveState() {
return null;
}
}
Implementation:
subsPager = (ViewPager) findViewById(R.id.subsPager);
tabLayout = (TabLayout) findViewById(R.id.tabs_view);
subsPager.setAdapter(adapter);
tabLayout.setupWithViewPager(subsPager)
Edit - Fragment Code:
public class PostsListFragment extends Fragment {
private ArrayList<CustomPost> posts;
private Sorting sorting;
private String name;
private RecyclerView recyclerView;
private SubPostsAdapter adapter;
private LinearLayoutManager linearLayoutManager;
private Fetcher fetcher
public PostsListFragment() {
this.posts = new ArrayList<>();
}
public static Fragment newInstance(String name) {
PostsListFragment pf = new PostsListFragment();
pf.name = name;
return pf;
}
#Override
public View onCreateView (LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
recyclerView = (RecyclerView) inflater.inflate(R.layout.posts_list_holder, container, false);
//default sorting
this.sorting = Sorting.HOT;
this.fetcher = new Fetcher(name);
loadItems();
return recyclerView;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
setHasOptionsMenu(true);
}
private void loadItems() {
if (posts.size() == 0) {
new Thread() {
#Override
public void run() {
posts.addAll(fetcher.fetchPosts(sorting));
new Thread() {
#Override
public void run() {
linearLayoutManager = new LinearLayoutManager(recyclerView.getContext());
adapter = new SubPostsAdapter(posts, getActivity());
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
recyclerView.setLayoutManager(linearLayoutManager);
recyclerView.setAdapter(adapter);
}
});
}
}.start();
}
}.start();
}
}
}
It was more stupid then it looked, removing this line:
if (posts.size() == 0) {
from loadItems in the Fragment, makes it work as intended.
Thanks to cricket_007 for the advices!
I initially followed the accepted answer in this previously posted question about how to save and restore the layout manager for a RecyclerView.
I have my RecyclerView in Activity B. [Note: Activity A will launch Activity B, transferring data via an intent.]
When a user clicks a viewholder in Activity B, Activity C is launched. Once Activity C is closed and destroyed, Activity B is visible, and I can see all of my data restored in the RecyclerView.
The Problem:
However, if I navigate away from my app (ie pressing the Home Key), it is pushed to the background. When my app is reinstated, onCreate() is called again. The LayoutManager's state is being saved, however none of my data is showing in the RecyclerView.
I can tell my LayoutManager state was saved after some debugging. It shows up as this when onCreate() is called again after reinstating the app from the background:
Saved Instance State = Bundle[{ActivityB - Linear Layout State=android.support.v7.widget.LinearLayoutManager$SavedState#6358f8f, android:viewHierarchyState=Bundle[mParcelledData.dataSize=1296]}]
I am not sure how to fix this after hours of trying.
My Code:
public class ActivityB extends AppCompatActivity {
private RecyclerView recyclerView;
private LinearLayoutManager linearLayoutManager;
private Parcelable linearLayoutState;
private RecyclerAdapter adapter;
private Button more;
private String id;
private List<VideoItem> list;
private List<VideoItem> newList;
private final String LINEARLAYOUT_STATE_KEY = "Activity B - Linear Layout State";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_blayout);
// Retrieving data from Activity A
Intent intent = getIntent();
Bundle bundle = intent.getBundleExtra("bundle");
someClass.innerClass class = bundle.getParcelable("data");
id = class.getID();
list = class.getList();
newList = new ArrayList<>();
// Instantiating Recycler View
recyclerView = (RecyclerView)findViewById(R.id.activityb_recyclerview);
recyclerView.setNestedScrollingEnabled(false);
linearLayoutManager = new LinearLayoutManager(context);
recyclerView.setLayoutManager(linearLayoutManager);
adapter = new RecyclerAdapter();
recyclerView.setAdapter(adapter);
moreButton = (Button)findViewById(R.id.activityb_more);
moreButton.setOnClickListener(new fetchMoreClickListener());
}
#Override
public void onResume(){
if (linearLayoutState != null) {
linearLayoutManager.onRestoreInstanceState(linearLayoutState);
} else {
// If no layout state is found, I need to populate my
// adapter.
// Here I am simply cloning my list again, to keep the original
// list intact. generateSubset() removes items from the cloned list
// to create a smaller subset that is added to the adapter.
for (ListItem item : list){
newList.add(new ListItemi(item));
}
adapter.addItems(generateSubset(0));
}
super.onResume();
}
#Override
protected void onSaveInstanceState(Bundle outState) {
linearLayoutState = linearLayoutManager.onSaveInstanceState();
outState.putParcelable(LINEARLAYOUT_STATE_KEY, linearLayoutState);
super.onSaveInstanceState(outState);
}
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
linearLayoutState = savedInstanceState.getParcelable(LINEARLAYOUT_STATE_KEY);
}
The other Activity LifeCycle Methods have not been overridden. I am not getting any error messages. Just my data stored in my viewholders is no longer showing in the RecyclerView.
Edit 1:
Here is my RecyclerAdapter code:
public class RecyclerAdapter extends RecyclerView.Adapter{
private static Date now;
private List list;
public static class ItemHolder extends RecyclerView.ViewHolder
implements View.OnClickListener {
private Context context;
private String key;
private Bundle itemBundle;
private SimpleDraweeView photo;
private TextView title;
public ItemHolder(Context context, View view){
super(view);
this.context = context;
this.key = null;
this.itemBundle = null;
photo = (SimpleDraweeView)view.findViewById(R.id.itemholder_photo);
title = (TextView)view.findViewById(R.id.itemholder_title);
view.setOnClickListener(this);
}
public void bindData(Item item){
if (key == null){ key = item.getID(); }
if (itemBundle == null){
itemBundle = new Bundle();
itemBundle.setClassLoader(Item.class.getClassLoader());
itemBundle.putParcelable("data", item);
}
photo.setImageURI(Uri.parse(item.getPhotoURL()));
}
#Override
public void onClick(View view) {
Intent intent = new Intent(context, ActivityB.class);
intent.putExtra("bundle", itemBundle);
context.startActivity(intent);
}
}
public RecyclerAdapter(){
this.list = new ArrayList<>();
this.now = new Date();
}
public RecyclerAdapter(ArrayList<VideoItem> list){
this.list = list;
this.now = new Date();
this.notifyItemRangeInserted(0, list.size());
}
#Override
public RecyclerAdapter.ItemHolder onCreateViewHolder(ViewGroup parent, int viewType) {
Context context = parent.getContext();
View recyclerItem = LayoutInflater.from(context)
.inflate(R.layout.itemholder_item, parent, false);
return new ItemHolder(context, recyclerItem);
}
#Override
public void onBindViewHolder(RecyclerAdapter.ItemHolder holder, int position) {
Item item = list.get(position);
holder.bindData(item);
}
private static Date getNow(){ return now; }
private static void updateNow(){ now = new Date(); }
#Override
public int getItemCount() {
return list.size();
}
public void addItem(Item item){
list.add(item);
this.notifyItemInserted(list.size() - 1);
}
public void addItems(ArrayList<Item> items){
int oldSize = list.size();
list.addAll(items);
this.notifyItemRangeInserted(oldSize, items.size());
}
}
Try changing your onResume() method to look something like the following:
#Override
public void onResume(){
super.onResume();
if (linearLayoutState != null) {
linearLayoutManager.onRestoreInstanceState(linearLayoutState);
}
// Here comes the code to populate your data.
// I'm not sure how you do this, so I just copy/paste your code
for (ListItem item : list){
newList.add(new ListItemi(item));
}
// Now instatiate and add the adapter to the RecyclerView
adapter = new RecyclerAdapter(newList);
recyclerView.setAdapter(adapter);
}
}
I have a list of items in a RecyclerView and i set the onClickListener in the onBindViewHolder for each view. The click listener works just fine, the issue is that I can click on two items in the list at the same time and both of them will run their onClick method. When you have ListViews if you try to click on two items at the same time it does not allow you.
For example:
Lets say you are already touching on an item in a listview and during that time you try to touch another item it won't let you. Recyclerview allows that.
How can we make the RecyclerView to work like a ListView for when clicking?
Below is my implementation
public class DataCardAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private Context mContext;
private ArrayList<Data> mDatas = new ArrayList<>();
private Data mData;
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View card = LayoutInflater.from(mContext).inflate(R.layout.card, parent, false);
return new DataCardViewHolder(mContext, card, mData);
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
Data data = mDatas.get(position);
((DataCardViewHolder )holder).configureDataCard(data);
}
public static class DataCardViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
private Context mContext;
private Data mData;
public DataCardViewHolder(Context context, View view, Data data) {
super(view);
mContext = context;
mData= data;
}
public void configureDataCard(final Data data) {
mData= data;
itemView.setOnClickListener(this);
}
#Override
public void onClick(View v) {
Log.v("DataCardViewHolder","onClick with data: " + mData.getData().toString());
}
}
}
My RecyclerView is programmatically add in Java but not in xml. And I try this and it works:
mRecyclerView.setMotionEventSplittingEnabled(false);
If your RecyclerView is add in xml, you may try adding this in your RecyclerView:
android:splitMotionEvents="false"
And now in the recycler-list when you click on one item and don't let go, you can not click another item.
Unfortunately, RecyclerView will not handle that for you. Create a Handler with timeout:
public class DelayedClick {
protected boolean canClick = true;
protected Handler myHandler = new Handler();
private Runnable mMyRunnable = new Runnable()
{
#Override
public void run() {
canClick = true;
}
};
public boolean getState() {
if(canClick) {
myHandler.postDelayed(mMyRunnable, 200);
canClick = false;
return true;
}
else return false;
}
}
#Override
public void onClick(View v) {
if (!reClick.getState()) {
return;
}
//Code to execute on click
}