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);
}
Related
I have an Android MVP project. I want to keep out any Android references out of the presenter. This way, I can keep UI separated in the Activity/View.
There is a ListView in the Activity which uses a custom ArrayList in the adapter (MyAdapter). This uses MyModel objects to populate the ListView with data.
Now, I'm trying to initialize the adapter and the ListView.
By doing that on the activity I would end up with something like
`MyAdapter adapter = new MyAdapter<MyModel>(this, R.layout.list_item, items);`
The problem with this is that the Activity now have access to the Model and has a reference to an ArrayList of items which I wanted to keep only in the presenter and manage it from there.
I can't move setup the adapter on the Presenter because I would have to share the context from the Activity to the presenter, setup the adapter and pass it back to the Activity. The problem with this is that the presenter now depends on an Android context object (There shouldn't be any Android code in the Presenter part of an MVP Android project).
So the question is what do I do in this case? Where and how do I handle setting up the ArrayAdapter?
You can keep a list of items in the Presenter and then, send it to the Activity when you need to set up the adapter.
I think you can do:
Contract:
public interface ViewContract {
void setupContentList(ArrayList<MyModel> list);
}
public interface PresenterContract {
void onViewCreated();
}
Activity:
public class MainActivity extends Activity implements ViewContract {
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
......
mListView = findViewById(R.id.listview);
mPresenter.onViewCreated();
}
public void setupContentList(ArrayList<MyModel> list) {
MyAdapter adapter = new MyAdapter<MyModel>(this, R.layout.list_item, items);
mListView.setAdapter(adapter);
}
}
Presenter:
public class Presenter implements PresenterContract {
public Presenter() {
ArrayList<MyModel> mItems = new ArrayList();
// Add items to the list
}
public void onViewCreated() {
mView.setupContentList(mItems);
}
}
I recommend you to use RecyclerView and not ListView for better performance.
The activity can hold the RecyclerView, you should ask the presenter to fetch the data you want from your model repository, etc.
After the presenter retrieved the data you should notify the Activity about the new data, and set it to the RecyclerAdapter.
Now your activity holds inflates all regarding the view, and the presenter is a hub for data and logic.
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!
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
I have an Activity which holds a ViewPager with 2 Fragments. One fragment is for adding items to ListView and another fragment is holding the ListView.
I've been trying for almost 3 days now without any positive results. How do I update the other fragment's ListView from the first fragment?
I'm trying to call the method that updates ListView from the Activity that holds ViewPager but it doesn't work.
Calling the method from ViewPager activity :
#Override
public void onPageSelected(int position) {
library.populateListView(getApplicationContext());
aBar.setSelectedNavigationItem(position);
}
This is the populateListView method:
public void populateListView(Context context){
CustomListViewAdapter customAdapter = new CustomListViewAdapter(getDatabaseArrayList(context), getActivity());
if (lView != null)
{
lView.setAdapter(customAdapter);
}
customAdapter.notifyDataSetChanged();
}
However this doesn't work because the lView variable (ListView) is null because the fragment isn't shown at the moment when this is being called.
I am assuming that function populateListView() is a function of the Fragment containing the ListView. You are calling populateListView() on every call to onPageSelected. Should you not check what is the position that is being selected. Anyway the populateListView() method should be a public method of the Fragment containing ListView. And You Can Instantiate The Fragment from the Viewpager adapter in the Activity and than call this method. In That way the listView should not be null.
#Override
public void onPageSelected(int position) {
ListViewFragment frag=(ListViewFragment)adapter.instantiateItem(viewPager, 1);
//here adapter is the ViewPager Adapter and you must supply the viewpager that contains
//the fragments and also the position of the fragment to instantiate.
//For example 0 or 1 etc.
frag.populateListView(getApplicationContext());
aBar.setSelectedNavigationItem(position);
}
Understand Fragments
Please see this link. I have gone in great detail explaining the concept of fragments.
Pay particular attention to the definition of rootview:
public void onActivityCreared(Bundle savedInstanceState){
super.onActivityCreated(savedInstanceState);
// Do stuff on creation. This is usually where you add the bulk of your code. Like clickListners
// You can define this object as any element in any of your xml's
View rootview = inflater.inflate(R.layout.xml_the_fragment_uses container,false);
rootview.findViewById(R.id.your_id).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//Do something
}
});
}
In the above case I defined a button for an on click listener, but you can just as easily define a ListView along with its appropriate methods.
Alternate Solution
A second method could be using getView or getActivity (check the communicating with activity section).
For example:
ListView listView = (ListView) getView().findViewById(R.id.your_listView_id);
OR (more likely solution for your problem)
View listView = getActivity().findViewById(R.id.list);
Please read this post for additional information.
Good Luck.
To do this safely, you have to keep your listview data in a higher level Parent-Activity not the fragments. Make your MainActivityclass singleton class by making the constructor private and create a getInstance method that return the only initialized instance of your `MainActivity.
This will allow you to keep your data instance safe from being re-initialized or lost. Then, in onResume of your fragment re-set the data (get it from the MainActivity) to your listview adapter and call notifyDataSetChanged() method from the adapter instance.
This will do the trick.
I have a ViewPager with two Fragments. On the first one, there are buttons (it is a menu) and they represent different categories. On the second one, it is a listView which display data.
I am looking for a way to change the data to display on the listView when the user click on one of the menu items.
For example : the user click on the menu item 1 (fragment 1), the listView on the fragment 2 have to be updated with the corresponding data.
I read the documentations from Google Android developer about fragments but I couldn't work it out, I didn't find a solution to my problem.
Maybe I'm missing something ?
EDIT : I tried to clear my ListView by clearing the list of data (ArrayList> listItems) with listItems.clear(). I didn't find how to do adapter.clear(), it seems that this method doesn't exist in the class Adapter.
So to summarize : I created an update method in my fragment which contains the ListView (Fragment2). I called it through a callback method in the main activity, that part seems to work (I checked it with debug mode).
In the update method of my Fragment 2, I clear data with listItems.clear() and then the OnActivityCreated() of the fragment is executed. In it, there is the call of my thread which download data and then it create the ListView in OnPostExecute.
Here is a sample of my code (fragment2) :
// After data are ready
lvListe = (ListView) getView().findViewById(R.id.lvListe);
lvListe.setSelected(true);
lvListe.setScrollingCacheEnabled(false);
adapter = new LazyAdapter(getActivity(),
getActivity().getBaseContext(), listItems,
R.layout.affichageitem, new String[] { "image", "title",
"subtitle" }, new int[] { R.id.img_title,
R.id.titre_main, R.id.soustitre_main });
lvListe.setAdapter(adapter);
lvListe.setClickable(true);
lvListe.setOnItemClickListener(onItemClickListener);
// Update method :
public void update(String requete, String type)
{
this.type = type;
this.requete = requete;
listItems.clear();
}
The way that I would probably do it is to create a callback method in the Activity hosting the ViewPager and also a method in the second Fragment to update the data.
Then you would call it like:
class Fragment1...
{
...
callBackUpdate(data);
}
class Activity...
{
...
public void callBackUpdate(String data)
{
Fragment2.update(data);
}
}
class Fragment2...
{
...
public void update(String data)
{
//do whatever
}
}
The developers page shows how to create a callback method to the Activity.
edit: Actually that's how the Developer's page says to do it. They create the interface OnArticleSelectedListener for the callback.
edit: I'm assuming you are using this library for the LazyLoader: https://github.com/thest1/LazyList
If so, then you can just add the method to clear the data yourself. To do so, add a method like this in ListAdapter:
public void clear()
{
data = new String[](); //depends on what data is, if it's a List then just call data.clear() for example
imageLoader=new ImageLoader(activity.getApplicationContext()); //clear the images
notifyDataSetChanged(); //notify the adapter that the data has changed
}
This might not exactly fit how you currently have it, but you should get the idea.
Try using something known as a FragmentTransaction manager at http://developer.android.com/reference/android/app/FragmentTransaction.html
and also the FragmentManager http://developer.android.com/reference/android/app/FragmentManager.html
Check out some of the questions on stackoverflow related to FragmentManager and FragmentTransaction to figure this out. All you need to do is replace the fragment with a new one which is inflated by a particular xml