Does anyone know what might cause viewPager.getChildCount() to return zero? I do findViewById inside onCreate and there I also do viewPager.setAdapter and tabLayout.setupWithViewPager(viewPager). Then inside onResume I log the child count and it is zero! Why is that?
Oh, btw, the layout of the ViewPager displays no problem although getChildCount returns 0.
I was under the impression that after I call viewPager.setAdapter(pagerAdapter) then the ViewPager and the PagerAdapter would have the same count. But clearly that is not true. So why might this be happening?
UPDATE
You guys raise some very good points. Thank you. The objective is that I want to iterate through all the Fragments in my ViewPager/PagerAdapter. When I use getChildCount it is often 0 at unexpected times, but when it is non-zero everything works fine.
for(int i=0; i<viewPager.getChildCount(); i++){
MyFragmentInterface frag = pagerAdapter.getRegisteredFragment(i);
. . .
}
However when I use viewPager.getAdapter().getCount(), the value is always the same as pagerAdapter.getCount(). But I tend to get NullPointerException because apparently there are no fragments yet.
for(int i=0; i<viewPager.getAdapter().getCount(); i++){
MyFragmentInterface frag = pagerAdapter.getRegisteredFragment(i);
. . .
}
ANSWER
I seem to have fixed it with
for(int i=0; i<pagerAdapter.getCount(); i++){
Fragment frag = pagerAdapter.getItem(i);
. . .
}
No more NullPointerException or size 0
as far i know ViewPager doesn't implement this method
ViewPager.getChildCount() == ViewGroup.getChildCount()
but as you see:
public abstract class ViewGroup extends View implements ViewParent, ViewManager {
// Number of valid children in the mChildren array, the rest should be null or not
// considered as children
private int mChildrenCount
/**
* Returns the number of children in the group.
*
* #return a positive integer representing the number of children in
* the group
*/
public int getChildCount() {
return mChildrenCount;
}
it returns a mChildren count - count of view group child views**
so in short:
** this mean any child view (that is not the same as object/item of pager)
further see method:
ViewPager.populate(int newCurrentItem)
ViewPager rely on TabLayout - so term item is an "tablayout" tab mapped to view pager adapter item ( could be fragment or view)
as you can add child views to any view - this is not the same as add view pager item to it - you can have something like
[ T1 ][ T2 ][ T3 ] <- tab layout
< -- > |----------------------------|
pager |[ pager item ] | < - fragment as view pager item
(view) |----------------------------|
< -- > |[ child view of pager ]| < - child view of pager
Thanks for the education, friend. +1. Do you know how I might solve the problem of iterating through my fragments? – Nouvel Travay
to iterate: ?? mhh let me think ....
maybe get array and iterate ???
1) via loop :)
List<Fragment> fragments = getAdapter().getArrayOfFragments();
for(Fragment fragment : fragments) {
String title = fragment.getTitle();
}
2) or via iterator :))
List<Fragment> fragments = some implementation of List interface;
Iterator<Fragment> iterator = fragments.iterator();
while(iterator.hasNext()) {
Fragment fragment = iterator.next();
}
hint for simplest method to get array ?:
// its only a hint how - not solution - class need override methods from base class
class MyAdapter extends BaseAdapter {
private List<Items> _items;
public List<Items> getItems() { return _items; }
MyAdapter(List<Items> items)
_items = items;
}
}
// set
ViewPager.setAdapter(new MyAdapter(listOfItems);
// retrieve
Adapter adapter = ViewPager.getAdapter();
if(adapter != null && MyAdapter.class.isAssignableFrom(adapter.getClass()) {
// magic
MyAdapter myadapter = (MyAdapter) adapter;
List<Items> myList = myAdapter.getItems()
}
ps. sorry for typos i dont' know english and i'm bot in real time :D
TODO FOR YOU (FOR TODAY)
general read about java language
datatypes, classes, interfaces
inheritance
ps. i forgot about generics
then try write simple class which inherits from other class and implements a mhhh
this interface :
/**
* Created by ceph3us on 03.09.2015.
*/
public interface IListAdapter<T> extends ListAdapter{
void add(T listElement);
void addAll(List<T> listOfElements);
void clear();
void remove(T listElement);
void notifyDataSetChanged();
T getItem(int position);
List<T> getList();
}
as for your case :)
you have a holder [ in java term "definition" ] for an array (primitive) of fragments
private Fragment registeredFragments[] = new Fragment[FRAGMENT_TITLES.length];
when u fill this array ??
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return DogsFragment.newInstance(); <- ?? **here** ??
i guess no ?? :)
maybe try adjust something like ??
case 0:
// if my array contains this type of fragment return it
// else make new one ??? and pack to this array ???
return DogsFragment.newInstance();
second matter is instantiation of this holder ???
[FRAGMENT_TITLES.length] ?? make array of ?? what size ???
don't use primitive array for this ( think forward when u will add new fragment - as newbie try use list - at this level It will save you time for looking to solutions for many problems ....
i say it one more time READ ABOUT INTERFACES ESPECIAL ABOUT ONE named
List
Related
I've four tabs and four fragments (each one for each tab).
Each fragment has a vertical recycler view. Since all fragments view look similar I'm re-using the same layout file, same recycler view items and same adapter.
The issue is that only one item is loaded under the first tab and third tab and fourth tab, While the second tab successfully loads the entire data.
I hope image added below gives better understanding regarding the issue.
Here is my adapter code
public class OthersAdapter extends RecyclerView.Adapter<OthersAdapter.OthersViewHolder> {
private final Context context;
private final ArrayList<LocalDealsDataFields> othersDataArray;
private LayoutInflater layoutInflater;
public OthersAdapter(Context context, ArrayList<LocalDealsDataFields> othersDataArray) {
this.context = context;
this.othersDataArray = othersDataArray;
if (this.context != null) {
layoutInflater = LayoutInflater.from(this.context);
}
}
class OthersViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
TextView othersSmallTitleTextView;
ImageView othersImageView;
OthersViewHolder(View itemView) {
super(itemView);
othersSmallTitleTextView = (TextView) itemView.findViewById(R.id.others_small_title);
othersImageView = (ImageView) itemView.findViewById(R.id.others_image);
itemView.setOnClickListener(this);
}
#Override
public void onClick(View view) {
Intent couponDetailsItem = new Intent(context, LocalDealsActivity.class);
Bundle extras = new Bundle();
extras.putString(Constants.SECTION_NAME, context.getString(R.string.local_deals_section_title));
// Add the offer id to the extras. This will be used to retrieve the coupon details
// in the next activity
extras.putInt(Constants.COUPONS_OFFER_ID, othersDataArray.get(
getAdapterPosition()).getLocalDealId());
couponDetailsItem.putExtras(extras);
context.startActivity(couponDetailsItem);
}
}
#Override
public OthersViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = layoutInflater.inflate(R.layout.others_items, parent, false);
return new OthersViewHolder(view);
}
#Override
public void onBindViewHolder(OthersViewHolder holder, int position) {
String lfImage = othersDataArray.get(position).getLocalDealImage();
String lfCategoryName = othersDataArray.get(position).getLocalDealSecondTitle();
if (lfCategoryName != null) {
// Set the second title
holder.othersSmallTitleTextView.setText(lfCategoryName);
}
if (lfImage != null) {
if (!lfImage.isEmpty()) {
// Get the Uri
Uri lfUriImage = Uri.parse(lfImage);
// Load the Image
Picasso.with(context).load(lfUriImage).into(holder.othersImageView);
}
}
}
#Override
public int getItemCount() {
return othersDataArray.size();
}
}
I like to point out couple of things -
I've checked other answers on Stack Overflow. They talk about setting the recycler view layout_height to wrap_content. This isn't the issue as the layout_height is already wrap_content and also the second tab loads all the data as expected.
And some others answers mentioned to used same versions for all support libraries and I'm already using 25.1.0 version for all the support libraries.
Size of the data array is 20 and returning 20 from the adapter's getItemCount() method.
The data array has the expected number of items in it and they are not null or empty.
Clean build, invalidate/caches doesn't work either.
Finally, I'm using FragmentStatePagerAdapter to load the fragments when the tabs are in focus.
EDIT:
This is how I'm parsing the JSON data received
private void parseLocalDeals(String stringResponse) throws JSONException {
JSONArray localJSONArray = new JSONArray(stringResponse);
// If the array length is less than 10 then display to the end of the JSON data or else
// display 10 items.
int localArrayLength = localJSONArray.length() <= 20 ? localJSONArray.length() : 20;
for (int i = 0; i < localArrayLength; i++) {
// Initialize Temporary variables
int localProductId = 0;
String localSecondTitle = null;
String localImageUrlString = null;
JSONObject localJSONObject = localJSONArray.getJSONObject(i);
if (localJSONObject.has(JSONKeys.KEY_LOCAL_DEAL_ID)) {
localProductId = localJSONObject.getInt(JSONKeys.KEY_LOCAL_DEAL_ID);
}
if (localJSONObject.has(JSONKeys.KEY_LOCAL_DEAL_CATEGORY)) {
localSecondTitle = localJSONObject.getString(JSONKeys.KEY_LOCAL_DEAL_CATEGORY);
}
if (localJSONObject.has(JSONKeys.KEY_LOCAL_DEAL_IMAGE)) {
localImageUrlString = localJSONObject.getString(JSONKeys.KEY_LOCAL_DEAL_IMAGE);
}
if (localImageUrlString != null) {
if (!localImageUrlString.isEmpty()) {
// Remove the dots at the start of the Product Image String
while (localImageUrlString.charAt(0) == '.') {
localImageUrlString = localImageUrlString.replaceFirst(".", "");
}
// Replace the spaces in the url with %20 (useful if there is any)
localImageUrlString = localImageUrlString.replaceAll(" ", "%20");
}
}
LocalDealsDataFields localDealsData = new LocalDealsDataFields();
localDealsData.setLocalDealId(localProductId);
localDealsData.setLocalDealSecondTitle(localSecondTitle);
localDealsData.setLocalDealImage(localImageUrlString);
localDealsDataArray.add(localDealsData);
}
// Initialize the Local Deals List only once and notify the adapter that data set has changed
// from second time. If you initializeRV the localDealsRVAdapter at an early instance and only
// use the notifyDataSetChanged method here then the adapter doesn't update the data. This is
// because the adapter won't update items if the number of previously populated items is zero.
if (localDealsCount == 0) {
if (localArrayLength != 0) {
// Populate the Local Deals list
// Specify an adapter
localDealsRVAdapter = new OthersAdapter(context, localDealsDataArray);
localDealsRecyclerView.setAdapter(localDealsRVAdapter);
} else {
// localArrayLength is 0; which means there are no rv elements to show.
// So, remove the layout
contentMain.setVisibility(View.GONE);
// Show no results layout
showNoResultsIfNoData(localArrayLength);
}
} else {
// Notify the adapter that data set has changed
localDealsRVAdapter.notifyDataSetChanged();
}
// Increase the count since parsing the first set of results are returned
localDealsCount = localDealsCount + 20;
// Remove the progress bar and show the content
prcVisibility.success();
}
parseLocalDeals method is inside a helper class and it is called by using initializeHotels.initializeRV();
initializeRV() initializes the Recycler view, makes a network call to the server and the received data is passed to the parseLocalDeals method. initializeHotels being an instance variable of the Helper class.
EDIT 2:
For those who wants to explore the code in detail, I've moved the part of the code to another project and shared it on Github. Here is the link https://github.com/gSrikar/TabLayout and to understand the hierarchy check out the README file.
Can anyone tell me what I'm missing?
Not much of an answer but too long for a comment.
I have duplicated (almost) your adapter code and it fully works for me. I believe I have done the same as you. I'm using the same layout file, the same item & same adapter for all tabs. I think there are no problems with your adapter code.
I say 'almost' because I had to change a couple of things since I don't have access to your data. I changed your LocalDealsDataField model to include a BitmapDrawable & I changed onBindViewHolder() to handle it.
BitmapDrawable lfImage = othersDataArray.get(position).getLocalDealImage();
holder.othersImageView.setBackground(lfImage);
Since there seems to be no problem with your adapter, I would focus on getting the data or setting up the adapter as your problem. Sorry I can't be of help beyond that.
FYI, here's how I setup the adapter in onCreateView()
rootView = inflater.inflate(R.layout.recycler_view, container, false);
mRecyclerView = (RecyclerView) rootView.findViewById(R.id.recyclerview);
mRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
mAdapter = new OthersAdapter(this.getContext(), list);
mRecyclerView.setAdapter(mAdapter);
Summary
Solved the layout issue at point 1 replacing a LinearLayout by a RelativeLayout, inverting visibility logic to avoid ghost effect and catching exceptions and preventing them when the related view is not found.
Added point 2 to demonstrate that the visual defect is only present on Marshmallow and Nougat devices.
Finally FragmentStatePagerAdapter loads pages before getting focus so a fix is proposed at point 3 (load all pages and update them when are selected).
Further information in the comments below and #d4h answer.
The fourth page is not using the same layout, only the same RecyclerView and id, perhaps a work in progress. The layout issue can be solved using the same layout that previous pages but I consider this change out of scope.
1. Partially fixed for Marshmallow and Nougat devices. Work in progress.
Update2 Changing LinearLayout by RelativeLayout and inverting visibility logic solves layout issue:
Update: Commenting initializeTrending in all the fragment initializations also works onApi23+
I'll check it later, seems as deals are correctly loaded but then trending is loaded and deals are lost. WIP here.
If trending array empty and trending view gone, deals are not shown, but using invisible are shown
2. You are loading a wrong page on Marshmallow and Nougat devices
FragmentStatePagerAdapter first call to getItem() wrong on Nougat devices
This ended up having nothing to do with the FragmentStatePagerAdapter
code. Rather, in my fragment, I grabbed a stored object from an array
using the string ("id") that I passed to the fragment in init. If I
grabbed that stored object by passing in the position of the object in
the array, there was no problem. Only occurs in devices with Android 7.
FragmentStatePagerAdapter - getItem
A FragmentStatePager adapter will load the current page, and one page
either side. That is why it logs 0 and 1 at the same time. When you
switch to page 2, it will load page 3 and keep page 1 in memory. Then
when you get to page 4 it will not load anything, as 4 was loaded when
you scrolled to 3 and there is nothing beyond that. So the int that
you're being given in getItem() is NOT the page that is currently
being viewed, is the one being loaded into memory. Hope that clears
things up for you
These comments are confirmed in this branch and commit
All pages load correctly on Lollipop emulator, the last page has an extra issue, see OthersFragment:
3. Initialize all pages at creation and update them on selection.
Increase OffScreenPageLimit so all pages are initialised
Add on page selected/unselected/reselected listener
These changes solve the issue commented below:
/**
* Implement the tab layout and view pager
*/
private void useSlidingTabViewPager() {
// Create the adapter that will return a fragment for each of the three
// primary sections of the activity.
BottomSectionsPagerAdapter mBottomSectionsPagerAdapter = new BottomSectionsPagerAdapter(getChildFragmentManager());
// Set up the ViewPager with the sections adapter.
ViewPager mBottomViewPager = (ViewPager) rootView.findViewById(R.id.local_bottom_pager);
mBottomViewPager.setOffscreenPageLimit(mBottomSectionsPagerAdapter.getCount());
mBottomViewPager.setAdapter(mBottomSectionsPagerAdapter);
TabLayout tabLayout = (TabLayout) rootView.findViewById(R.id.tab_layout);
tabLayout.setupWithViewPager(mBottomViewPager);
tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
/**
* Called when a tab enters the selected state.
*
* #param tab The tab that was selected
*/
#Override
public void onTabSelected(TabLayout.Tab tab) {
// TODO: update the selected page here
Log.i(LOG_TAG, "page " + tab.getPosition() + " selected.");
}
/**
* Called when a tab exits the selected state.
*
* #param tab The tab that was unselected
*/
#Override
public void onTabUnselected(TabLayout.Tab tab) {
// Do nothing
Log.i(LOG_TAG, "Page " + tab.getPosition() + " unselected and ");
}
/**
* Called when a tab that is already selected is chosen again by the user. Some applications
* may use this action to return to the top level of a category.
*
* #param tab The tab that was reselected.
*/
#Override
public void onTabReselected(TabLayout.Tab tab) {
// Do nothing
Log.i(LOG_TAG, "Page " + tab.getPosition() + " reselected.");
}
});
}
Previous Comments:
Check your LocalFragment getItem() method using breakpoints.
If you select one page, next page is also initialized, and you are sharing the recyclerView, etc.
I would move the initialization outside of getItem() as suggested here:
ViewPager is default to load the next page(Fragment) which you can't
change by setOffscreenPageLimit(0). But you can do something to hack.
You can implement onPageSelected function in Activity containing the
ViewPager. In the next Fragment(which you don't want to load), you
write a function let's say showViewContent() where you put in all
resource consuming init code and do nothing before onResume() method.
Then call showViewContent() function inside onPageSelected. Hope this
will help
Read these related questions (the first has possible workarounds to hack the limit to zero):
ViewPager.setOffscreenPageLimit(0) doesn't work as expected
Does ViewPager require a minimum of 1 offscreen pages?
Yes. If I am
reading the source code correctly, you should be getting a warning
about this in LogCat, something like:
Requested offscreen page limit 0 too small; defaulting to 1
viewPager.setOffscreenPageLimit(couponsPagerAdapter.getCount());
public void setOffscreenPageLimit(int limit) {
if (limit < DEFAULT_OFFSCREEN_PAGES) {
Log.w(TAG, "Requested offscreen page limit " + limit + " too small; defaulting to "
+ DEFAULT_OFFSCREEN_PAGES);
limit = DEFAULT_OFFSCREEN_PAGES;
}
if (limit != mOffscreenPageLimit) {
mOffscreenPageLimit = limit;
populate();
}
}
I have looked at your code, problem is same as explained by #ardock
Solution i would like to propose,
You have to change your code at 3 place ::
Inside all Fragment You are using in ViewPager Don't call initializeRESPECTIVEView() from onCreateView method.
Inside LocalFragment make a list of Fragments you are going to use with ViewPager and pass it to BottomSectionsPagerAdapter. and return Fragment from that list from getItem(int position) of BottomSectionsPagerAdapter.
Add Following code to LocalFragment inside useSlidingTabViewPager().
tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
#Override
public void onTabSelected(TabLayout.Tab tab) {
}
#Override
public void onTabUnselected(TabLayout.Tab tab) {
}
#Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
//Call Respective fragment initializeRESPECTIVEView() method from onTabSelected , you can get fragment instance from list you passed to BottomSectionsPagerAdapter
I will try to explain what I am thinking of doing as an Android app. But I am really confused how would I approach this problem.
When you get JSON from some web API (eg Location) and let's say that JSON has 5 different locations and you want to store each Location as a separate list item in a list view. This is simple, you use a location adapter class, and then those 5 items get stored as a list. For example, JSON updates 24h later and now there are 10 locations. No problems at all - Android handles this because of location adapter and etc. (I know all of this). Basically, what I am trying to tell that android does not need to know how many list items there will be before fetching information from JSON.
Now, the problem is that I am trying to create a swipe views which will represent each of the list items (1 Full view = 1 list item). For example, if there are 5 locations, i can only swipe 4 times and then I will reach the last tab. If there is update, and there are 10 locations, I could only swipe 9 times until I reach the end. I hope you understand idea.
My question is - how do I create dynamic swipe views where each of the list items would have its own separate window and to reach another list item you would swipe?
My main concern is how do you not tell android how many swipe views you will need and he would figure it out when he reads the JSON and knows the number of locations.
Many Thanks
Let's say your data is like this:
{"India","Morocco","China","Russia"}
You can getLength of the JSON object.In this case it is 4.Save that in a static variable.Suppose
max_swipes=4
Then in you swipe method
`if(position<=max_swipes || position==0){//code to swipe }
else
{
//cannot swipe last position
}`
To implement such functionality you just simply can use viewPager. You can copy the code from here and here. These are two file and you just need to copy as it is. After adding these two files in your project you need to create an adapter and here is the thing which makes it dynamic to create swipe views.
I am adding code snippet hope it will help you.
public class ViewPagerAdapter extends FragmentStatePagerAdapter {
private DetailFragment page;
CharSequence Titles[]; // This will Store the Titles of the Tabs which are Going to be passed when ViewPagerAdapter is created
int NumbOfTabs; // Store the number of tabs, this will also be passed when the ViewPagerAdapter is created
// Build a Constructor and assign the passed Values to appropriate values in the class
public ViewPagerAdapter(FragmentManager fm, CharSequence mTitles[], int mNumbOfTabsumb) {
super(fm);
this.Titles = mTitles;
this.NumbOfTabs = mNumbOfTabsumb;
}
//This method return the fragment for the every position in the View Pager, This method is called only when we slide or change the tabs and not called upon rotating the screen
#Override
public Fragment getItem(int position) {
if(position < NumbOfTabs)
{
page= new DetailFragment();
return page;
}else {
return null;
}
}
// This method return the titles for the Tabs in the Tab Strip(in case you want to add title to each page.
#Override
public CharSequence getPageTitle(int position) {
return Titles[position];
}
#Override
public int getCount() {
return NumbOfTabs;
}
}
While creating the instance of this adapter you can pass the number of page you are going to require by calculating the number of items in JSON.
Hope this will help.
I'm writing an application where I need to show some 100 page in ViewPager. There are facility to jump from one index to direct any random index page. Here Every page it self contain list of item.
Now if I just to user ViewPager.current(index) to jump from one to any random index page..then UI get stuck for a moment and then display.
So to avoid this I thought to implement only 7 adapter page view..where these 100(getCount()) page will reuses these 7 page.
But Im'm getting
E/AndroidRuntime(25961): java.lang.IllegalStateException: Fragment already added: ArrayListFragment{24dad9b6 #0 id=0x7f060009}
E/AndroidRuntime(25961): at android.support.v4.app.FragmentManagerImpl.addFragment(FragmentManager.java:1192)
E/AndroidRuntime(25961): at android.support.v4.app.BackStackRecord.run(BackStackRecord.java:616)
E/AndroidRuntime(25961): at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1484)
E/AndroidRuntime(25961): at android.support.v4.app.FragmentManagerImpl.executePendingTransactions(FragmentManager.java.
I'm using FragmentStatePagerAdapter for adapter implementation.
Following snap of my code I'm using
static final int NUM_ITEMS = 100;
private int MAX_AVAILABLE_COUNT = 7;
private ArrayList<ArrayListFragment> listFragment = new ArrayList();
public class MyAdapter extends FragmentStatePagerAdapter {
public MyAdapter(FragmentManager fragmentManager) {
super(fragmentManager);
for (int i = 0; i < MAX_AVAILABLE_COUNT; i++) {
listFragment.add(ArrayListFragment.newInstance(i));
}
}
#Override
public int getCount() {
return NUM_ITEMS;
}
#Override
public Fragment getItem(int position) {
original = position;
if (position >= MAX_AVAILABLE_COUNT - 1) {
int newPosition = position % MAX_AVAILABLE_COUNT;
position = newPosition;
}
Fragment fragment = listFragment.get(position);
return fragment;
}
}
Any suggestion what I'm doing wrong here. Or any other suggestion to achieve it!!
Your error is because you are manipulating with position in getItem() method. You can't do like that because at some point you will return fragment tied to other page and you will get exception like above. And storing references to fragments is also bad practice, always return new instance of fragment object.
In one of my apps, I need to add Fragments on both sides of the ViewPager. First of all, I will get a constant of 5 feeds, and my ViewPager will show feed at index 2 i.e. my current displayed Fragment will contain data present at index 2. So overall my ViewPager will show center of 5 feeds at start and that i have achieved by just setting the ViewPager current Item as 2 like this
viewPager.setCurrentItem(2);
Now user can swipe both sides, when he will swipe left from center position, I will look for next feed i.e fetch from server and add feed at zero index of my ViewPager like this
feedsList.add(0, modelClassObject); // feedsList will contain all feeds and has been used by adapter to show data in fragments.
adapter.notifyDataSetChanged();
and when i swipe right from center position, i will add feed at the last simply like this
feedsList.add(modelClassObject);
adapter.notifyDataSetChanged();
Now the problem is if i only add feeds at the right i.e at the end of the feedsList, everything works fine, but problem comes when i add feeds at zero index. My adapter is not showing that new feed that has been added to zero position instead it is repeating one of the existing feed and that too on the right side but not on the left. I Have tried everything, but nothing is going right way. Here is my adapter code.
private class HorizontalPagerAdapter extends FragmentStatePagerAdapter {
public HorizontalPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int arg0) {
return FeedUserProfileFragment.newInstance(feedsList.get(arg0),
arg0);
}
#Override
public int getCount() {
return feedsList.size();
}
}
I have also used this
#Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
but no results.. :(
So in severe need, If anyone had done that earlier and faced the same issue, please let me know what i am doing wrong. I only need to add fragment at zero index of my ViewPager.
I faced a similar problem before, and my solution was :
at first the list is declared in the adapter itself, so that when creating an instance of that adapter I can have it's own list then.
modified the method getItem(int arg0) in the adapter class so that it returns a specific item from the list depending on that item position.
when creating a new fragment, use instantiate method to create it, and after that add it to your fragments.
So, the complete solution would be :
adapter class:
private class HorizontalPagerAdapter extends FragmentStatePagerAdapter {
public List<Fragment> feedsList;
public HorizontalPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
return feedsList.get(position);
}
#Override
public int getCount() {
return feedsList.size();
}
}
and when creating the adapter:
public static YourPageAdapter adapter_obj; // make sure it's static object
adapter_obj = new YourPageAdapter(getSupportFragmentManager());
adapter_obj.feedsList = new ArrayList<Fragment>();
// then add the list of fixed fragments to it(the 5 in the beginning)
adapter_obj.feedsList = fragments_list; // an ArrayList contains the 5 fragments
and when want to create a new fragment:
adapter_obj.feedsList.add(0, Fragment.instantiate(mContext, ViewPager_Container_Class.class.getName(), page));
adapter_obj.notifyDataSetChanged();
FragmentStatePagerAdapter can't handle it right when you add a new fragment in front.
The workaround is this:
Add a new fragment at the end.
Call notifyDataSetChanged();
Bring the fragment to front.
Call notifyDataSetChanged();
BTW, getItemPosition() should return correct positions all along:
public int getItemPosition(Object object)
{
return feedsList.indexOf( object );
}
So, with your code, it should be:
newFrag = Fragment.instantiate(...);
feedsList.add( newFrag );
adapter_obj.notifyDataSetChanged();
feedsList.remove( feedsList.size() - 1 );
feedsList.add( 0, newFrag );
adapter_obj.notifyDataSetChanged();
I guess the implementation of FragmentStatePagerAdapter doesn't expect both adding a new fragment and changing position happen at the same time.
Is there any way to re-index a SectionIndexer after new items are added to a ListView?
I found this solution, but the overlay is position in the top left corner after the SectionIndexer is refreshed.
Anyone have any ideas?
Once the FastScroller (its in AbsListView class that ListView extends from) obtains your sections by calling SectionIndexer#getSections(), it never re-obtains them unless you enable/disable fast-scrolling like mentioned in the link you mentioned. To get the value to be displayed on screen, FastScroller calls the section's toString method.
One potential solution is to have a custom SectionIndexer that have the following characteristics:
The sections array is of fixed length (max length of the expected number of sections. For example, if the sections represent English alphabet it will be 26)
Have a custom object to represent sections, rather than using strings
Overwrite the toString method of your custom section object to display what you want based on the current 'section values'.
-
e.g. In your custom SectionIndexer
private int mLastPosition;
public int getPositionForSection(int sectionIndex) {
if (sectionIndex < 0) sectionIndex = 0;
// myCurrentSectionLength is the number of sections you want to have after
// re-indexing the items in your ListView
// NOTE: myCurrentSectionLength must be less than getSections().length
if (sectionIndex >= myCurrentSectionLength) sectionIndex = myCurrentSectionLength - 1;
int position = 0;
// --- your logic to find the position goes in here
// --- e.g. see the AlphabeticIndexer source in Android repo for an example
mLastPosition = position;
return mLastPosition;
}
public Object[] getSections() {
// Assume you only have at most 3 section for this example
return new MySection[]{new MySection(), new MySection(), new MySection()};
}
// inner class within your CustomSectionIndexer
public class MySection {
MySection() {}
public String toString() {
// Get the value to displayed based on mLastPosition and the list item within that position
return "some value";
}
}
I found that the best way to do this is to call setContentView(R.layout.whatever) and then re-populate the ListView with your new adapter / new data items. This will redraw the ListView with your new items and the FastScroll Overlay will appear in the correct place.
I found notifyDataSetInvalidated working fine, here's the idea:
public class MyAdapter extends XXXAdapter implements SectionIndexer {
...
public void updateDataAndIndex(List data, Map index) {
// update sections
// update date set
notifyDataSetInvalidated();
}
}
update your data set and index (sections) somehow, and then notifyDataSetInvalidated, the index will refresh.
You can force reloading sections list to ListView by listView.setAdapter(yourAdapter)