Lazy loading ViewPagers in a ScrollView
android, android-viewpager, lazy-loading, listview, scrollview
Solution
Why not use a ListView instead of ScrollView? It has a function of recycling views, so loading will start only for those pagers which are visible on screen right now. Otherwise you should subclass your ScrollView in order to check if Pager is visible right now
Problem
Im trying to inflate multiple `ViewPagers` into a `ScrollView` where each `ViewPager` is using a FragmentStatePagerAdapter. The `ViewPager` consist of fragments with three `ImageViews` for each page. Im using UniversalImageLoader for the async loading of the images but I cant get a lazy load to work. The problem is when my app is inflating lets say 10 `ViewPagers` with 3 `ImageViews` for each `TripplePosterFragment1`, and another 10 `ViewPagers` with 3 `ImageViews` for `TripplePosterFragment2` since the `mPager.setOffscreenPageLimit(1)` kicks in. Its a total of 60 async requests firing during my `progressLayout` is visible. My `MainFragment` with following method: ``` private void renderSubCarousels(ArrayList<Carousel> carousels) { LayoutInflater mInflater = (LayoutInflater)getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE); int i = 1; LinearLayout subCarouselLayout = (LinearLayout) mScrollView.findViewById(R.id.index_subcategory_carousel_container); for (final Carousel carousel : carousels) { RelativeLayout carouselWrapper = (RelativeLayout) mInflater.inflate(R.layout.main_pager_layout, null); LinearLayout mContainer = (LinearLayout) carouselWrapper.findViewById(R.id.main_pager_container); mContainer.setId(Constants.BASEID_LINEARLAYOUT + i); ViewPager mPager = (ViewPager) mContainer.findViewById(R.id.main_pager); mPager.setId(Constants.BASEID_VIEWPAGER + i); mPager.setAdapter(new PagerPosterAdapter(getFragmentManager(),Constants.TRIPLE_POSTERS, carousel)); subCarouselLayout.addView(carouselWrapper); i++; } progressLayout.setVisibility(View.GONE); } ``` My `FragmentStatePagerAdapter`: ``` public class PagerPosterAdapter extends FragmentStatePagerAdapter { private int mPageSize; private Carousel mCarousel; public PagerPosterAdapter(FragmentManager fm, int pageSize, Carousel carousel) { super(fm); this.mPageSize = pageSize; this.mCarousel = carousel; } @Override public int getCount() { int maxPageIndex = (int) Math.ceil((float) mCarousel.getAssets().size() / mPageSize); return maxPageIndex; } @Override public Fragment getItem(int position) { ArrayList<Asset> assets = mCarousel.getAssets(); int from = position*mPageSize; int to = (position+1)*(mPageSize); if(to > assets.size()-1) to = assets.size(); return (TripplePosterFragment.newInstance(assets.subList(from, to))); } } ``` The `TripplePosterFragment` where the ImageView rendering happens: ``` private void initViews() { ImageView mFirstImageView = (ImageView) mLinearLayout.findViewById(R.id.first_pager_poster); ImageView mSecondImageView = (ImageView)mLinearLayout.findViewById(R.id.second_pager_poster); ImageView mThirdImageView = (ImageView)mLinearLayout.findViewById(R.id.third_pager_poster); for(int i = 0;i < assets.size(); i++){ ImageView image = null; switch(i){ case 0: image = (ImageView) mLinearLayout.findViewById(R.id.first_pager_poster); break; case 1: image = (ImageView) mLinearLayout.findViewById(R.id.second_pager_poster); break; case 2: image = (ImageView) mLinearLayout.findViewById(R.id.third_pager_poster); break; default: continue; } //The ScrollView in MainFragment CustomScrollView mParentScrollView = (CustomScrollView)getActivity().findViewById(R.id.index_scrollviewparent); Rect scrollBounds = new Rect(); mParentScrollView.getHitRect(scrollBounds); if (image.getLocalVisibleRect(scrollBounds)) { // Any portion of the imageView, even a single pixel, is within the visible window ImageLoader.getInstance().displayImage(assets.get(i).getPoster(), image); System.out.println("Im within"); else { // NONE of the imageView is within the visible window System.out.println("Im NOT within"); } } } ``` I need to check if the images are visible and load them when they are. But `System.out.println("Im NOT within");` is spammed in the `Log`. Im sure there is a way of doing it similar to this, but I need some help. I've tried a quick approach of doing much of the meat inside a ListView but its not optimal, at least after some reading, ViewPager inside ListView UPDATE Same core issue as before, but using a new approach HorizontalListViews instad of Pagers. `ListViews` seems like the way to go. By using `ArrayAdapter` to populate a regular vertical `ListView` with multiple `HListViews` lazy loads the images vertically for each new row, and horizontally for each image inside, as intended. Problem: Horizontal positions are lost. The `horizontalAdapter` is inherited from a recycled view onto next list item. This positions the new list item to where the recycled list item was. ``` public View getView(int position, View convertView, ViewGroup parent) { final ViewHolder holder; View view = convertView; if (view == null) { holder = new ViewHolder(); view = mInflater.inflate(R.layout.carousel_frame_layout, null); holder.carouselList = (HListView) view.findViewById(R.id.hListView); view.setTag(holder); } else { holder = (ViewHolder) view.getTag(); } Carousel mCarousel = mData.get(position); // Problem: Re-uses adapters from convertview if (holder.carouselList.getAdapter() == null) { SubCategoryCarouselAdapter horizontalAdapter = new SubCategoryCarouselAdapter(mContext, mCarousel.getAssets()); holder.carouselList.setAdapter(horizontalAdapter); } else { ((SubCategoryCarouselAdapter) holder.carouselList.getAdapter()).update(mCarousel.getAssets()); } return view; } ``` The solution could be to save the inner `horizontalAdapter` to a designated list item position, but I dont know how to do this when the `HListView` itself is recycled. UPDATE 3 Found a working solution for `HListViews`. But I have still to test if this is working for `ViewPagers`. Its lazy loading images both horizontally and vertically when using `addHeaderView` on parent `ListView` for each inner `HListView`. I´ve used HorizontalVariableListView together with UniversalImageLoader for the carousels. ``` LayoutInflater mInflater = (LayoutInflater) getActivity().getSystemService (Context.LAYOUT_INFLATER_SERVICE); for (final Carousel carousel : carousels) { View carouselWrapper = mInflater.inflate(R.layout.carousel_sub_category_layout, null); listView.addHeaderView(carouselWrapper); listView.setAdapter(null); HListView horizontalList = (HListView) carouselWrapper.findViewById(R.id.hListView); SubCategoryCarouselAdapter mAdapter = new SubCategoryCarouselAdapter(getActivity(), carousel.getAssets()); horizontalList.setAdapter(mAdapter); } ```