Recyclerview inside fragment inside viewpager notifydatasetchanged not working - android

I have a recyclerview inside a fragment and that fragment is inside a view pager.
notifydatasetchanged() is not working even though the same code works in other projects of mine. The only difference I can see is that it is inside a view pager now?.
This is how notify the change:
public void updateRecyler(){
// Clear the current array
alarmsFromSP.clear();
// Update the array (This is working the array size is changing)
alarmsFromSP.addAll(AlarmCollection.getAlarms(getActivity()));
// Notify the change
adapter.notifyDataSetChanged();
}
I create the recylerview in oncreate like this:
public class FragmentAllAlarms extends Fragment {
// Arraylist of the alarm data from the strings
ArrayList<Alarm> alarmsFromSP;
tabRefreshReceiver r;
RecyclerView rv;
AlarmsAdapter adapter;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_all_alarms, container, false);
// Recycler
rv = (RecyclerView) v.findViewById(R.id.mainAlarmRecyclerView);
rv.setLayoutManager(new LinearLayoutManager(getActivity()));
// Get saved data for alarms from the shared preferences string
alarmsFromSP = AlarmCollection.getAlarms(getActivity());
System.out.println("StringIs"+alarmsFromSP.size());
// Adapter
adapter = new AlarmsAdapter(getActivity(), alarmsFromSP);
rv.setAdapter(adapter);
Is it possible I am losing reference to the array or is it somthing to do with the viewpager?
I have been stuck with this for two days now any help is greatly appreciated.
Thanks in advance

make sure that updateRecyler() is called,
and try to change adapter.notifyDataSetChanged(); to rv.getAdapter().notifyDataSetChanged();
this worked for me once.

I have faced the same problem, then I found a temporary solution, instead of using notifyDataSetChanged() use
recyclerView.setAdapter(adapter);
recyclerView.scrollToPosition(postion);

Related

Refreshing a recyclerview in a fragment

I have 2 activities: RoutineViewer and ExerciseViewer. Both activities contain fragments that are sub-classes of ParentFragment.java.
RoutineViewer's fragment (RoutineViewerFragment) is displaying a recyclerview. The recyclerview's data is from a static ArrayList in ParentFragment.java.
From RoutineViewerFragment, I can start the ExerciseViewer activity, and the fragment inside it (ExerciseViewerFragment) is used to make changes to customRoutineExercise in ParentFragment.java. After making the changes in ExerciseViewerFragment, I can call requireActivity().finish() on ExerciseViewer.java, which will take me back to RoutineViewerFragment. However, my problem is that the data in RoutineViewerFragment is not reflecting the changes I made in ExerciseViewerFragment.
Some solutions I have tried (but didn't work in this case) from looking at other similar problems:
// In RoutineViewerFragment onResume() with if else conditions
mAdapter.notifyItemChanged(// insert relevant position);
// In RoutineViewer.java onResume() with if else
getSupportFragmentManager().beginTransaction.detach(currentfragment).attach(currentfragment).commit()
I'm guessing that the reason why mAdapter.notifyItemChanged(position) isn't working is because I'm not directly passing the static customRoutineExercise in the constructor of mAdapter, but rather a value of the HashMap instead.
As for the .detach().attach(), I've read that it forces the onCreateView() to run again, which theoretically should refresh the recyclerview with the updated data, but it isn't.
I've left the important code segments below. Any help is appreciated, thanks!
ParentFragment.java
protected static int selectedCustomRoutine;
protected static int selectedCustomExercise;
protected static ArrayList<customRoutine> customRoutineInfo = new ArrayList<>();
protected static HashMap <Integer, ArrayList<customExercise>> customRoutineExercise = new HashMap<>();
RoutineViewer.java
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_routine_viewer);
Intent intent = getIntent();
routineType = intent.getStringExtra("routineType");
routinePosition = intent.getIntExtra("routinePosition", 0);
selectedFragment = new RoutineViewerCustomFragment(routinePosition);
getSupportFragmentManager().beginTransaction().replace(R.id.routineV_fragment_container, selectedFragment).commit();
break;
}
RoutineViewerFragment.java
private int routinePosition;
public RoutineViewerCustomFragment(int routinePosition) {
// constructor
this.routinePosition = routinePosition;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_routine_viewer_custom, container, false);
buildRecyclerView(rootView);
return rootView;
}
// other code
private void buildRecyclerView(View rootView) {
mRecyclerView = rootView.findViewById(R.id.recyclerView);
mRecyclerView.setHasFixedSize(true);
mLayoutManager = new LinearLayoutManager(getActivity());
mAdapter = new RoutineViewerCustomAdapter(customRoutineExercise.get(routinePosition));
mRecyclerView.setLayoutManager(mLayoutManager);
mRecyclerView.setAdapter(mAdapter);
}
// Other code
private void goExerciseViewer(int position) {
selectedCustomExercise = //number;
selectedCustomRoutine = //number;
}
ExerciseViewerFragment.java
private void saveChanges() {
customRoutineExercise.get(selectedCustomRoutine).get(selectedCustomExercise).setSetNumber(Integer.parseInt(editText1.getText().toString()));
customRoutineExercise.get(selectedCustomRoutine).get(selectedCustomExercise).setRepNumber(Integer.parseInt(editText2.getText().toString()));
}
Code Smell: The recyclerview's data is from a static ArrayList in ParentFragment.java.
Don't do this.
Have a repository where you store your "recycler view list" (exercises?), and have the recyclerView request (or observe if you use a ViewModel) a list.
On the fragment where you modify this list, have said fragment talk to the same repository either via its own viewModel or a shared one or whatever you use (search for Android ViewModels jetPack if unfamiliar).
This way, when the recyclerView comes back, it will ask for a new list (and will get the updated copy) or if observing, it will be notified that a new list is available.
The list you pass to the adapter is/should be a copy/immutable.
This is not the same but it's an Activity that has a RecyclerView and "asks" directly for the data to a repo. (This example has nothing to do with your problem, but notice how the adapter is dummy, it's just told to display the list).
https://github.com/Gryzor/CheckBoxCounter/blob/master/app/src/main/java/com/androidonlinux/checkboxcounter/MainActivity.kt

Recyclerview refreshing items without even calling notifyDataSetChanged()

Let me explain you what's happening. I have a fragment and in its onCreateView() I am setting the adapter to recyclerview without any item..
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
downloadsList = new ArrayList<>();
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(context);
downloadsRecyclerview.setLayoutManager(linearLayoutManager);
downloadsList.clear();
downloadAdapter = new DownloadAdapter(context, downloadsList);
downloadsRecyclerview.setAdapter(downloadAdapter);
}
Now the weird part is whenever I add items to downloadsList ( which is basically an ArrayList ), then my recyclerview get updated with the content available on downloadsList. I am not even calling notifyDataSetChanged() or setting new adapter again... Any help..
If you don't want to reload entire RecyclerView using notifyDataSetChanged() then simple add notifyItemInserted()
when you add value in downloadsList then after just call
downloadAdapter.notifyItemInserted(downloadsList.size()-1)

Setting different adapter in single RecyclerView in Android

I have MenuActivity which has RecyclerView and ViewPager. This ViewPager has 3 pages which use three different Fragment.
FragmentOne,FragmentTwo, FragmentThree, All these Fragments use RecyclerView data.
I want to use three different ArrayList need to be set in RecyclerView Adapter, which is depends on what Fragment user is viewing.
One solution is to put RecyclerView in all three fragment and get update data in Fragment. I just want to know if it is possible to set Adapter data in MenuActivity based on what Fragment is called.
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_menu, container, false);
recyclerView = (RecyclerView) rootView.findViewById(R.id.recycler_view);
itemList = new ArrayList<>();
prepareMenuData();
vegadapter = new MenuItemAdapter(getContext(),vegItemList);
nonvegadapter = new MenuItemAdapter(getContext(),nonvegItemList);
dessertAdapter = new MenuItemAdapter(getContext(),dessertItemList);
recyclerView.setAdapter(vegadapter);
RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(getContext());
recyclerView.setLayoutManager(layoutManager);
return rootView;
}
prepareMenuData();
for (MenuItem item:itemList)
{
if(item.getCategory().equals("VEG"))
{
vegItemList.add(item);
vegadapter.notifyDataSetChanged();
}
if(item.getCategory().equals("NONVEG"))
{
nonvegItemList.add(item);
nonvegadapter.notifyDataSetChanged();
}
if(item.getCategory().equals("DESSERTVEG"))
{
dessertItemList.add(item);
dessertAdapter.notifyDataSetChanged();
}
}
This code does not know when to set vegadapter,nonvegadapter etc.
Please suggest.
Thanks
Deepak
Technically yes you can, but whether thats good practice is another question.
You'll have to have the RecyclerView in each Fragment regardless but you can get the data for the adapter from the Activity. Fragments have access to their Activity, thats how the Android lifecycle works so from the Fragment you could call ((MenuActivity) getActivity()).getMenuList(); and then pass the result to that Fragments adapter. This will however couple the Fragment with the Activity which isn't good practice.
Given the fact that it looks like your data isn't dynamic what I would personally do is make your MenuItem class implement Parcelable, then when you create your Fragments you can pass through your ArrayList of MenuItems as an argument, this way your Fragment is independent of your Activity.

How to get reference to a View inside CardView from an activity?

I have got CardView with Views inside a RecyclerView. I have created adapter in which assets are attached to Views and everything works. Now, I would like to change these Views from my activity. Is it any simple way to do that?
public class MainActivity extends AppCompatActivity {
RecyclerView recyclerView;
private RecyclerView.LayoutManager layoutManager;
private List<Offer>offers;
TextView timer; //timer inside CardView
private void getViewReferences() {
recyclerView = (RecyclerView)findViewById(R.id.mainRecyclerView);
timer = (TextView)findViewById(R.id.timer);
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getViewReferences();
layoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);
initializeData();
initializeAdapter();
timer.setText("13:16"); //NullPointerException here
}
private void initializeData(){
offers = new ArrayList<>();
offers.add(new Offer("godzina", R.drawable.zdj, 200));
offers.add(new Offer("godzina", R.drawable.zdj));
}
private void initializeAdapter() {
RecyclerViewAdapter adapter = new RecyclerViewAdapter(offers);
recyclerView.setAdapter(adapter);
}
}
So you don't want to change the Views but you want to update the text values of the views in your list.
You can't directly do that by trying to find the Views in the recyclerView and change the text. The adapter is responsible for giving values to your list. So you need to update the dataset in the adapter and call one of the notifyDataSetChanged methods on the adapter to update the recyclerView.
The big advantage of this design is that it places Views in Cache in order to reuse them multiple times without having to inflate and construct them again. This is a huge performance improvement.
You need to read the docs and some tutorials to understand the adapter design pattern better.

Communicate between Adapter, fragments and Sqlite Data base to refresh Fragments

I have three fragments that contains three ListView created using a BaseAdapter. Like the image below:
Anyway my listView is fetching data from an SqliteDatabase. What I need to know is: when I delete an item from my ListView My(Favorite,Rejected) Fragments ListViews are not notified and are not refreshing.
What I have tried so far is :
Call listView.invalidateViews() after notifyDataSetChanged() in the onResume() Method of my fragments .
I tried these solution two Android ListView not refreshing after notifyDataSetChanged
My code is :
In My BaseAdapter I'am using these method to refresh my adapter :
public void UpdateView(List<Voiture> items)
{
this.voitureList = items;
notifyDataSetChanged();
}
In My fragments I'am using these method to notify the adapter :
#Override
public void onResume() {
super.onResume();
adapterLogin.UpdateView(databaseHelper.getAllVoiture(username,currentLength));
listView.setAdapter(new AdapterLogin(getActivity(),voitureList,username,currentLength,1));
}
In the OncreateView() Method I'am using :
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
inflate = inflater ;
x = inflater.inflate(R.layout.fragemnt_favoris,null);
empty = (TextView) x.findViewById(R.id.texteempty);
listView = (ListView) x.findViewById(R.id.list);
activity = (Main2Activity) getActivity() ;
username = activity.getUsername();
databaseHelper = new DatabaseHelper(getActivity());
databaseHelper.InsertActivity(2,username);
voitureList = databaseHelper.getAllVoitureFavourite(1,username);
adapterLogin = new AdapterLogin(getActivity(),voitureList,username,currentLength,2);
if (voitureList.size()>0)
{
listView.setAdapter(adapterLogin);
((BaseAdapter)listView.getAdapter()).notifyDataSetChanged();
}
else
{
empty.setVisibility(View.VISIBLE);
}
// Inflate the layout for this fragment
return x;
}
Any help would be greatly appreciated.
Thanks to Paul answer the idea was to add the notifyDataSetInvalidated in my UpdateView() Method in my adapter and it works just fine and my fragments are refreshing correctly now :
void notifyDataSetInvalidated ()
the method Notifies the attached observers that the underlying data is no longer valid or available. Once invoked this adapter is no longer valid and should not report further data set changes.
refer link :
notifyDataSetInvalidated

Categories

Resources