I am making an Android e-commerce app connected to WordPress. In my app I have two Fragments: a Shop fragment (with products fetched from the website in it) and My Wishlist fragment (with wishlist products in it). I also have two lists: one for the retrieved products and another one for the wishlist products. The list of the wishlist products contains every product's index inside the retrieved products list so I can get the full details later (image, description, etc...).
The problem I am facing is when adding products to the wishlist, I want to be able to click the wishlist button in the Shop fragment and the wishlist products would show in the My Wishlist fragment. I searched a lot online and I found out that I should use notifyDataSetChanged but I didn't find any example with how to call it from other Fragments.
If I understand correctly, I should call the WishlistProductAdapter from the ShopProductAdapter but I'm a beginner in Android development so I'm not sure if I can pull this off by myself.
I believe that this code, which is inside MyWishlist.java (fragment), can help:
//getting the recyclerview from xml
RecyclerView recyclerView = (RecyclerView) getView().findViewById(R.id.wishlist_products);
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(new StaggeredGridLayoutManager(getResources().getInteger(R.integer.products_column_count), StaggeredGridLayoutManager.VERTICAL));
JSONArray shop_products = //fetched from website, done in MainActivity;
ArrayList<String> wishlist_products = ["3","0"]; // It contains product of index 0 and product of index 3 inside shop_products
//initializing the productlist
List<Product> productList = new ArrayList<>();
for (int i=0; i<wishlist_products.size(); i++) {
String index = wishlist_products.get(i);
JSONObject JO = (JSONObject) shop_products.get(Integer.parseInt(index));
productList.add(
new Product(
i, //index
Integer.parseInt(JO.get("id").toString()), //id
JO.get("title").toString(), //title
null,
null,
JO.get("price").toString(), //price
JO.get("featured_image_url").toString(), //image_url
null
)
);
}
//creating recyclerview adapter
adapter = new WishlistProductAdapter(getActivity().getApplicationContext(), productList);
//setting adapter to recyclerview
recyclerView.setAdapter(adapter);
EDIT:
and Shop.java is the same except for the for loop (fragment):
for (int i=0; i<shop_products.length(); i++) {
JSONObject JO = (JSONObject) shop_products.get(i);
productList.add(
new Product(
i,
Integer.parseInt(JO.get("id").toString()),
JO.get("title").toString(),
null,
null,
JO.get("price").toString(),
JO.get("featured_image_url").toString(),
null
)
);
}
EDIT:
My code with fragments, adapters and MainActivity is here: https://ufile.io/25z9e
Any help is greatly appreciated!
The fragments should communicate with each other via the parent Activity. Two Fragments should never communicate directly.
Let A be the fragment from which you want to call notifyDataSetChanged() of adapter in fragment B.
So, what you can do is create an interface in the fragment A. Implement this interface in the activity. On the click of the button, call the method of the interface. In the activity, where this interface is implemented, you can get the reference to the fragment B. You can create a method in fragment B in which you can call notifyDataSetChanged() on the adapter.
Rough code how to do above -
Create a method in fragment B
public void refreshAdapter(){
adapter.notifyDataSetChanged();
}
Create an interface in Fragment A
interface RefreshInterface{
public void refreshAdapterFragmentB();
}
ParentActivity implements RefreshInterface, you need to define the implementation of the refreshAdapterFramgentB() method, and fragmentB is the reference to fragment B which you have in the activity. You can call the refreshAdapter() method of fragment B like this
#Override
public void refreshAdapterFragmentB(){
fragmentB.refreshAdapter();
}
I finally was able to do show products in My Wishlist fragement after being added from the other fragment inside a List shared by both fragments (using SharedPreferences) using a very simpler solution than the one Kashish suggested.
I first added an onPageSelected listener inside MainActivity like so:
mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
public void onPageScrollStateChanged(int state) {}
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {}
#Override
public void onPageSelected(int position) {
NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
navigationView.setNavigationItemSelectedListener(MainActivity.this);
if(position == 0) {
setSelectedNavItem(0);
myShop.refreshAdapter();
} else if (position == 1) {
setSelectedNavItem(2);
myWishList.refreshAdapter();
}
}
});
Inside each fragment I added a refreshAdapter() method like so:
public void refreshAdapter() {
if(productList != null) {
adapter.productList = productList;
adapter.notifyDataSetChanged();
}
}
I globalized productList inside each fragment's adapter.
P.S.: Thank you so much #Kashish Malhotra for your help!
Related
I have pondered on this for days now, I need help. I have 2 fragments in a viewpager. They are both showing the same values despite feeding them with different data.
Link to image
Fragment A has a spinner dropdown of countries same as Fragment B. Fragment A is supposed to show list of Hospitals when country is selected, Fragment B is supposed to show list of Schools depending on country selected.
PROBLEM
When I select a country in Fragment A, I get the list of hospitals in fragment A. When I swipe to fragment B, I find the list of hospitals which are supposed to be in A displayed. When I select a country in B, the list refreshes and I get the list of schools as should be, but when I swipe back again to fragment A I find the same list of schools instead of list of hospitals previously selected. I am using dagger2 with databinding.
Below code depicts one scenario but they are basically the same just different adapters and also different layouts.
CODE
private void initRecyclerview() {
binding.rvSchools.setHasFixedSize(true);
adapter = new SchoolsAdapter(getContext());
binding.rvSchools.setLayoutManager(new LinearLayoutManager(getContext()));
binding.rvSchools.setAdapter(adapter);
adapter.notifyDataSetChanged();
}
private void countries() {
viewModel.getCountries().removeObservers(getViewLifecycleOwner());
viewModel.getCountries().observe(getViewLifecycleOwner(), new Observer<Resource<List<Countries>>>() {
#Override
public void onChanged(Resource<List<Countries>> countriesList) {
switch (countriesList.status) {
case SUCCESS:
ArrayAdapter<String> spinnerAdapter = new ArrayAdapter<>(getApplicationContext,
R.layout.simple_spinner_item, countriesList);
spinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
binding.spinner.setAdapter(spinnerAdapter);
spinnerAdapter.notifyDataSetChanged();
break;
}
}
});
binding.spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
String country = countriesList.get(position);
schools(country);
}
#Override
public void onNothingSelected(AdapterView<?> parent) {}
});
}
private void schools(String country) {
viewModel.getSchoolsHospitals(country, "School").removeObservers(getViewLifecycleOwner());
viewModel.getSchoolsHospitals(country, "School").observe(getViewLifecycleOwner(), new Observer<Resource<List<SchoolsHospitals>>>() {
#Override
public void onChanged(Resource<List<SchoolsHospitals>> schoolsList) {
switch (schoolsList.status) {
case SUCCESS:
binding.setSchools(schoolsList.data);
}
}
});
}
This is a rather odd behaviour considering how I wanted to re-use code. I was using the same viewModel class because the data is the same. After creating a separate viewModel class for Schools i.e a duplicate ViewModel just different class names, the respective contents were set as needed.
If there is a better approach, it will be much appreciated.
I have a ViewPager whose getCount(); changes based on a list of objects passed through the constructor. Using the adapter.notifyDataSetChanged(); I expect to see another page added. This seems to work correctly but when I remove an item from the mentioned list (used to calculate the getCount()) the ViewPager only removes the page successfully if its outside the offsetLimit.
Exemple: I have two pages and through a button I remove an item from the list. Then I call notifyDataSetChanged() and expect to have only one page left, but... the second "Page" is still there even thought I cannot swipe to it!!!!
How can I come around this? I want to avoid re-instantiating the whole ViewPager with the fragments.
CODE (simplification)
public class Adapter extends FragmentStatePagerAdapter {
private final List<String> list;
public Adapter(FragmentManager fragmentManager, List<String> list) {
super(fragmentManager);
this.list = list;
}
#Override
public int getCount() {
return list.size();
}
}
And this is inside the Activity:
adapter = new Adapter(getSupportFragmentManager(), items);
final ViewPager pager = findViewById(R.id.pager);
pager.setAdapter(adapter);
findViewById(R.id.add).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
items.add("test");
adapter.notifyDataSetChanged();
}
});
findViewById(R.id.remove).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
items.remove(items.size() - 1);
adapter.notifyDataSetChanged();
}
});
With this I can add items from the Activity and update de adapter (it works) and remove ... its doesn't work. What can I do?
You can override getItemPosition() & return POSITION_NONE in it.
getItemPosition() :
Called when the host view is attempting to determine if an item's position has changed. Returns POSITION_UNCHANGED if the position of the given item has not changed or POSITION_NONE if the item is no longer present in the adapter.
The default implementation assumes that items will never change position and always returns POSITION_UNCHANGED.
i am using a recyclerView and loading the data from database.The recyclerView item have 2 buttons. The recyclerView loads fine and clicks work fine for the following code.
allData=db.getCatData(CATEGORYLIST_CIDD,1);
textlistadapter = new TextListAdapter(getActivity(), allData);
recyclerView.setAdapter(textlistadapter);
later i rearrange the adapter data for displaying the data in descending order by using this code
db=new DatabaseHandler(getActivity().getApplicationContext());
allData=db.getCatData(CATEGORYLIST_CIDD,order_id);
textlistadapter = new TextListAdapter(getActivity(), allData);
recyclerView.setAdapter(textlistadapter);
textlistadapter.notifyDataSetChanged();
Now the items are rearranged on the recyclerView, but the onShareButtonClickListener returns null, here is the code of the adapter.
private OnShareButtonClickListener onShareButtonClickListener;
public interface OnShareButtonClickListener {
void onItemClick(View view, Pojo obj, int position);
}
public void setOnShareButtonClickListener(final OnShareButtonClickListener onShareButtonClickListener) {
this.onShareButtonClickListener = onShareButtonClickListener;
}
the click listener code is here
holder.bt_share.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//Snackbar.make(view, "Share Clicked", Snackbar.LENGTH_SHORT).show();
if (onShareButtonClickListener != null) {
onShareButtonClickListener.onItemClick(view, c, position);
}
}
});
The click does not work since the onShareButtonClickListener is null.
The same code works well for the first time and once i change the order then the click events are not working, all other functions are working well.
you should only need to do this once:
textlistadapter = new TextListAdapter(getActivity(), allData);
recyclerView.setAdapter(textlistadapter);
later on, if you want to update/change the data, then you should do something like this:
textlistadapter.setListItems(allData);
textlistadapter.notifyDataSetChanged();
Where setListItems() is a public method in your adapter, an example like this:
public void setListItems(List<> allData) {
this.data.clear();
this.data.addAll(allData);
}
It's easy to understand what you are doing wrong. You populate an array which will feed your adapter with data, that meaning that, after setting the adapter, if you want it's data to change, you only need to change the array you populated the data with, and notify the adapter for the data change.
For example:
myData = db.getData();
myAdapter = new MyAdapter(this,myData);
recyvlerview.setAdapter(myAdapter);
Now every time you want to change the data, you only need to change your myData and notifity the adapter
myAdapter.notifyDataSetChange();
Basically instead of doing this:
db=new DatabaseHandler(getActivity().getApplicationContext());
allData=db.getCatData(CATEGORYLIST_CIDD,order_id);
textlistadapter = new TextListAdapter(getActivity(), allData);
recyclerView.setAdapter(textlistadapter);
textlistadapter.notifyDataSetChanged();
This should suffice
db=new DatabaseHandler(getActivity().getApplicationContext());
allData=db.getCatData(CATEGORYLIST_CIDD,order_id);
textlistadapter.notifyDataSetChanged();
I have multiple RecyclerViews using common RecyclerView.Adapter. Is there is some way to tell RecyclerView to get data from adapter using some offset? Something like 1st view gets 0-15 items from adapter, 2nd view gets 15-30 and so on.
Give the position and and the Object List to the Fragment inside your viewpager at FragmentStatePagerAdapter :
#Override
public Fragment getItem(int position) {
return YourFragment.newInstance(position,yourListOfObjects);
}
Then inside your YourFragment according to position take 15 items from the List and make a adpater, example with RxJava, but you can modify it, to work with regular Java:
int positionOfFragment;
List<Product> list;
Observable
.just(list)
.take(positionOfFragment*15)
.subscribe(new Action1<List<Product>>() {
#Override
public void call(List<Product> orders) {
ProductAdapter adapter = new ProductAdapter(orders, getActivity());
//setAdapter to RecyclerView;
}
});
You mean something like ExpandableListView?
EDITED:
Try this.
Create separate data structure for filtered data at required positions
private List<Object> data;
private List<Object> dataToShow;
public CustomAdapter(List<Object> data){
this.data = data;
this.dataToShow = new ArrayList<>(data);
}
...in all overriden methods use dataToShow field
create method to filter data
public void showDataAtPositions(int startPos, int endPos){
dataToShow.clear();
for(int index = 0; index < data.size(); index++){
if(index >=startPos && index <= endPos){
dataToShow.add(data.get(index));
}
}
notifyDataSetChanged();
}
then just call this method
adapter.showDataAtPositions(0, 15);
adapter.showDataAtPositions(16, 30);
this should work
I have this code, which gets data from various EditTexts and adds this info into a ListView_item in fragment 1. This ListView_item is supposed to be populated in the ListView in fragment 2. I was thinking this might be possible with the help of the EventBus library but I have not quite yet gotten my head around how.
Here is my populate list method
public void populateList() {
ArrayAdapter<Appointment> adapter = new AppointmentListAdapter();
(The listview in fragment 2).setAdapter(adapter);
}
You have two ways to do this :
1) Using Interface: You will find lots of links for how to use interface for data passing.
2) Pass adapter through the activity :
step 1 : In your Fragment1 Make getParent() method :
private MyActivity getParent() {
return ((MyActivity) getActivity());
}
step 2 : In MyActivity Make populateList() method :
public void populateList(ArrayAdapter<Appointment> adapter){
SecondFragment mSecondFragment = (SecondFragment) getFragmentManager().findFragmentByTag(SecondFragment.TAG);
mSecondFragment.setAdapter(adapter);
}
step 3 : In your Fragment1 populateList method look like below :
public void populateList() {
ArrayAdapter<Appointment> adapter = new AppointmentListAdapter();
getParent().populateList(adapter);
}