Android - Add item to listview in fragment update - android

Been stuck on this for a while.
I currently have 3 fragments each containing lists - A | B | C (Favorites)
A and B retrieve data from online, C is an offline favorite list.
When a user favorites something in A it shows up in the favorite list straight away as the ViewPageAdapter loads 1 extra page off the screen. So A | B are already loaded, which means when I go to favorites (C) it has to reload.
My problem is - When I favorite something in B, the app refuses to reload favorites (C) as C was already loaded when I clicked on B and the only way to see what I have added is refresh the app.
I have tried:
Changing setOffscreenPageLimit(); to 0 so it has to reload each fragment every time - even if clunky just to see it working, and it still refuses to.
NotifyDataSetChanged also hasn't worked or I don't understand it properly.
InstatiateItem in the ViewPageAdapter, but couldn't get that working, couldn't find a good example of it to understand
Creating a new listadapter in the favorite code just to try to get it load the new data - which I can see in the logs it is adding to the favourite list but it just isn't being reloaded by the ViewPageAdapter
Lots of Googling
What happens is, regardless of what I do, when the user goes from B to C, no new code runs as C's code all ran once I clicked on B and it just goes straight to C's list.
I'm using Google's SlidingTabLayout and SlidingTabStrip for the fragments which all works fine, nothing changed in it. found here - https://developer.android.com/samples/SlidingTabsBasic/src/com.example.android.common/view/SlidingTabLayout.html
Code:
public void favourite()
{
//if the user wants to favourite
ParseQuery<ParseObject> query = ParseQuery.getQuery("Favourite");
//queries the id from the local data store in-case they have already favourited
query.whereEqualTo("id",id);
query.fromLocalDatastore();
query.getFirstInBackground(new GetCallback<ParseObject>() {
public void done(ParseObject object, ParseException e)
{
if(object!=null)
{
Toast.makeText(getApplicationContext(),"You have already pinned this",Toast.LENGTH_SHORT).show();
}
else
{
//otherwise create a new ParseObject and save the id,title and backdrop to
//the local datastore
final ParseObject favouriteShow = new ParseObject("FavouriteShow");
favouriteShow.put("id", id);
favouriteShow.put("title", title);
favouriteShow.put("backdrop", backdropPath);
favouriteShow.pinInBackground();
Toast.makeText(getApplicationContext(),"Pinned - "+ title,Toast.LENGTH_SHORT).show();
//need to set favourite as null for it to redraw with the favourited icon
mFavourite.setImageDrawable(null);
mFavourite.setImageResource(R.drawable.favourited_already);
}
}
});
}
ViewPagerAdapter:
public class ViewPagerAdapter extends FragmentStatePagerAdapter {
CharSequence Titles[];
int tabNumber;
public ViewPagerAdapter(FragmentManager fm, CharSequence[] mTitles, int mTabNumber) {
super(fm);
this.Titles = mTitles;
this.tabNumber = mTabNumber;
}
#Override
public Fragment getItem(final int position) {
Fragment fragment = null;
switch (position) {
case 0:
fragment = new FragmentA();
break;
case 1:
fragment = new FragmentB();
break;
case 2:
fragment = new FavouritesFragmentC();
break;
}
return fragment;
}
#Override
public CharSequence getPageTitle(int position) {
return Titles[position];
}
#Override
public int getCount() {
return tabNumber;
}
}
Thanks very much for your help.
Adam

Use ViewPager.OnPageChangeListener and update the corresponding
fragment in onPageSelected(position) method using callback pattern.

I've personally had a similar problem.
The solution to this problem depends on the whether the data shown in each tab is contextually related.
If not, the best way to fix this is actually changing the navigational structure of the application. So instead of having tabs, you should use a navigation drawer. Therefore every time the user enters the screen through the drawer, the fragment is created anew. By doing this it will solve the problem you have and improve the user experience.
If so, you may need to create as base fragment for all of the tabs to inherit, with a function called refresh, then you can ask the Adapter to refresh all your tabs. This method is quite hacky mostly because a favourites section doesn't belong in tabs.

Related

Change the next page dynamically of a ViewPager

I'm using a ViewPager and displaying a lot of different Fragments inside it, not only in content but they use different classes as well. The list to be displayed should be changed dynamically and even though I manage to swap items around and add new ones to the adapter(and calling notifyDataSetChanged), if I try changing the next item it will still slide to it when using mPager.setCurrentItem(mPager.getCurrentItem() + 1);
I am just adding a new Fragment between the current item and the current next one, it is displayed correctly in the adapter but as the next one was already preloaded then getItem in the adapter is not even called.
Is there another method "stronger" than notifyDataSetChanged that tells my ViewPager that it should get the next item again?
CODE SAMPLES:
The add and get item methods inside my FragmentPagerAdapter(only samples, not the actual code)
public void add(#NonNull Integer fragmentIndex) {
mFragmentOrder.add(fragmentIndex);
notifyDataSetChanged();
}
#Override
public Fragment getItem(int position) {
int selectedFragment = mFragmentOrder(position);
Fragment fragment;
switch (selectedFragment) {
case 1:
fragment = new FragmentA();
break;
case 2:
fragment = new FragmentB();
break;
case 3:
fragment = new FragmentC();
break;
default:
fragment = new FragmentD();
break;
}
return fragment;
}
This is the function used to go to the next item(I don't allow swiping)
public void goToNext() {
mPager.setCurrentItem(mPager.getCurrentItem() + 1);
}
EDITS:
Edit 1: I had already tried using a FragmentStatePagerAdapter instead and setting the OffscreenPageLimit to 0, but to no avail.
Edit 2: [Solution] Using a FragmentStatePagerAdapter AND overwriting the getItemPosition function to return POSITION_NONE or the index in the appropriate cases solved the problem. For some reason even after implementing the right version of this function the normal FragmentPagerAdapter kept delivering the wrong Fragment.
By default, FragmentPagerAdapter assumes that the number and positions of its items remain fixed. Therefore, if you want to introduce for dynamism, you have to provide for it yourself by implementing the getItemPosition(Object object) method in the inherited adapter class. A very basic (but unefficient) implementation would be this:
#Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
Every time the parent view is determining whether the position of one of its child views (items) has changed, this code will force the fragment to be recreated. If you want to avoid the recreation when unnecessary, you have to include some logic in the method. Something like this:
#Override
public int getItemPosition (Object object) {
if (fragmentOrder.indexOf(object) == -1) {
return POSITION_NONE;
} else {
return index;
}
}
Finally, pay attention to possible memory leaks by adding an onDestroyView method to your fragments and nullifying the views you are using.
Here is a good discussion of these issues with the two PagerAdapters.

Loading same data from API on different Tab Fragments

I have 3 tab fragments in my app, each meant to show different data from the same API URL (one call of 10 records).
This is the final result im trying to achieve:
Tab 1: Show records 1-5 from my API URL
Tab 2: Show records 6-10 from my API URL
Tab 3: Show different values from records 1-10
My goal is to load all this data in the first tab (Tab1) and show different results from the the JSON data returned in each tab as explained above.
Progress so far: Coded Tab1 to show records 1-10.
Need your help with:
Make Tab1 show records 1-5 and not all 10. Since I want to load all 10 results in the same run and wont call the API multiple times, I don't wish to limit the number of results.
Code Tab2 show records 6-10 from the JSON DATA received in Tab1. Means I need to communicate from Tab2 to my Tab1
Hope to get direction for this, Thanks!
What I've been thinking about:
Setting 3 static JSONOAdapters and set it with the json result and use it for each tab. but..
Does static JSON Adapters will be a good solution or may cause memory problems?
Another problem: When I use this:
public void updateData(JSONArray jsonArray) {
// update the adapter's dataset
mJsonArray = jsonArray;
notifyDataSetChanged();
}
I get results 1-10, I have no idea how to limit it to the first 5 or results 6-10.
Load data from 1 to 10 in your activity or fragment contains 2 fragments
After that add function setData for each fragment which data you want.
Last, go to your fragment and load data you set before.
Load Data in your activity.
Initiate ViewPager Adapter and send Array of Items in the Constructor.
public class MyPagerAdapter extends FragmentPagerAdapter {
private static int NUM_ITEMS = 3;
ArrayList<Item> arr;
public MyPagerAdapter(FragmentManager fragmentManager,ArrayList<Item> arr) {
super(fragmentManager);
this.arr=arr;
}
// Returns total number of pages
#Override
public int getCount() {
return NUM_ITEMS;
}
// Returns the fragment to display for that page
#Override
public Fragment getItem(int position) {
switch (position) {
case 0: // Fragment # 0 - This will show FirstFragment
return FirstFragment.newInstance(arr.subList(0,5));
case 1: // Fragment # 1
return FirstFragment.newInstance(arr.subList(6,10));
case 2: // Fragment # 2
//Change your values according to you need then pass the arr in Frag 3
return SecondFragment.newInstance(arr);
default:
return null;
}
}
// Returns the page title for the top indicator
#Override
public CharSequence getPageTitle(int position) {
return "Page " + position;
}
}
Make static method in your Fragment to receive the array of items.

I've got a weird bug when using a ViewPager inside a Fragment

Okay i'll try and make this as clear as possible. I have a Fragment called CheckerManager which contains a ViewPager. This ViewPager will allow the user to swipe between 3 Fragments all of which are an instance of another Fragment called CheckerFragment. I'm using a FragmentPagerAdapter to handle paging. Here's how it looks
private class PagerAdapter extends FragmentPagerAdapter {
CharSequence mTabTitles[];
public PagerAdapter(FragmentManager fm, CharSequence tabTitles[]) {
super(fm);
mTabTitles = tabTitles;
}
#Override
public Fragment getItem(int position) {
switch(position) {
case 0:
return CheckerFragment.newInstance(MainFragment.DRAW_TITLE_LOTTO);
case 1:
return CheckerFragment.newInstance(MainFragment.DRAW_TITLE_DAILY);
case 2:
return CheckerFragment.newInstance(MainFragment.DRAW_TITLE_EURO);
default:
return null;
}
}
#Override
public CharSequence getPageTitle(int position) {
return mTabTitles[position];
}
#Override
public int getCount() {
return 3;
}
}
I know that the ViewPager will always create the Fragment either side of the current Fragment. So say my 3 CheckerFragments are called A, B and C and the current Fragment is A. B has already been created. But my problem is that even though I am still looking at Fragment A, Fragment B is the 'active' Fragment. Every input I make is actually corresponding to Fragment B and not A. The active Fragment is always the one which has been created last by the ViewPager.
I've looked at quite a few things to see if anyone has had the same problem but i'm finding it difficult to even describe what's wrong. I think it's something to with the fact that all of the ViewPagers fragments are of the same type ie - CheckerFragment. I have a working implementation of a ViewPager inside a fragment elsewhere in the application and the only difference I can tell is that each page is a different type of Fragment.
Any help would be appreciated!
*EDIT
PagerAdapter adapter = new PagerAdapter(getChildFragmentManager(), tabTitles);
ViewPager viewPager = (ViewPager)view.findViewById(R.id.viewPagerChecker);
viewPager.setAdapter(adapter);
I feel pretty stupid but I found out what the issue was. In my CheckerFragment I would call getArguments() to retrieve a String extra and I would use this to determine how to layout the fragment. Problem was I made this extra a static member of CheckerFragment. So every time a new Fragment was created it was using the most recent extra.
Moral of the story - Don't make your fragments extra a static member if you plan on making multiple instances of that fragment.

Losing views with multiple fragments in a ViewPager

I've been working on an app to display media that has a layout that resembles the below mockup. Each fragment is a pair of linearlayout elements with 4 custom layouts to show a imageview and textview.
If I have more than 2 fragments and flick right-left to navigate through the various fragments the 4 custom imageviews are not being displayed inside the fragment. I assume it's due to garbage collection resulting from Android loading all images onto the heap.
I've tried using the onResume() methods of each fragment to rebuild the layouts when they are visible, with disastrous results.
Since different users may have different fragment counts based on how they set up their media, what would be considered best practice for an activity that's image heavy like this. Also does anyone have any suggestions for how to tell if the fragment's layout needs to be rebuilt.
This is my first Android app, the learning curve feels quite steep.
EDIT: As requested, here is the adapter and the fragment.
public static class HomeScreenPagerAdapter extends FragmentPagerAdapter {
public HomeScreenPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int i) {
MediaPayload mediaWrapper = new MediaPayload();
mediaWrapper.Item = items[i];
Fragment fragment = new EhsFragment();
Bundle bundle = new Bundle();
bundle.putSerializable("InterActivityPayload", payload);
bundle.putSerializable("MediaWrapper", mediaWrapper);
fragment.setArguments(bundle);
return fragment;
}
#Override
public int getCount() {
return items.length;
}
#Override
public CharSequence getPageTitle(int position) {
return items[position].Name;
}
}
The fragment is 500+ lines, so I dropboxed it. I can post it inline if people prefer, but it's a lot of code.

How to create carousel ViewPager?

All I want to do is a horizontal carousel in Android.
If I have 3 screens A B and C then I want my ViewPager to allow me to move like
A <-> B,
B <-> C,
C <-> A.
GTalk for Android's conversation can be switched like this.
Samsung's homescreen and application screen can be switched like this.
A B and C are fragments and I'm using an adapter that extends FragmentPagerAdapter. All the fragments will contain a webview.
I have looked here here and here but none of them seem to be doing what I want.
Can anyone guide me in the right direction?
(Cross-posting my answer from an identical StackOverflow question)
One possibility is setting up the screens like this:
C' A B C A'
C' looks just like C, but when you scroll to there, it switches you to the real C.
A' looks just like A, but when you scroll to there, it switches you to the real A.
I would do this by implementing onPageScrollStateChanged like so:
#Override
public void onPageScrollStateChanged (int state) {
if (state == ViewPager.SCROLL_STATE_IDLE) {
int curr = viewPager.getCurrentItem();
int lastReal = viewPager.getAdapter().getCount() - 2;
if (curr == 0) {
viewPager.setCurrentItem(lastReal, false);
} else if (curr > lastReal) {
viewPager.setCurrentItem(1, false);
}
}
}
Note that this calls the alternate form of setCurrentItem and passes false to cause the jump to happen instantly rather than as a smooth scroll.
There are two main drawbacks I see to this. Firstly, upon reaching either end the user has to let the scrolling settle before they can go further. Secondly, it means having a second copy of all of the views in your first and last page. Depending on how resource-heavy your screens are, that may rule out this technique as a possible solution.
Note also that since the view pager doesn't let clicks go through to underlying controls until after the scrolling has settled, it's probably fine to not set up clicklisteners and the like for the A' and C' fragments.
Edit: Having now implemented this myself, there's another pretty major drawback. When it switches from A' to A or C' to C, the screen flickers for a moment, at least on my current test device.
ViewPager settings:
mViewPager = (ViewPager) findViewById(R.id.view_pager);
mViewPager.setAdapter(new YourPagerAdapter(getSupportFragmentManager()));
//Set the number of pages that should be retained to either side of the current page.
mViewPager.setOffscreenPageLimit(1);
mViewPager.setCurrentItem(50);
FragmentPagerAdapter:
public class YourPagerAdapter extends FragmentPagerAdapter {
final int PAGE_COUNT = 100;
final int REAL_PAGE_COUNT = 3;
public YourPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
while (position > REAL_PAGE_COUNT - 1) {
position = position - REAL_PAGE_COUNT ;
}
switch (position) {
case 0:
return FirstFragment.newInstance(position);
case 1:
return SecondFragment.newInstance(position);
case 2:
return ThirdFragment.newInstance(position);
}
return null;
}
#Override
public int getCount() {
return PAGE_COUNT;
}
}
Implement the getItem(int position) like this:
public Fragment getItem(int position)
{
switch(position)
{
case 0:
Fragment A = new A();
return A;
case 1:
Fragment B = new B();
return B;
same for C....
}
}
you can also have a look at here: SimpleViewPager.. download the source and understand it. Hope it helps.

Categories

Resources