Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Lazy loading ViewPagers in a ScrollView

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);
}
like image 853
Rob Avatar asked Feb 06 '14 02:02

Rob


2 Answers

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

like image 154
Chaosit Avatar answered Oct 20 '22 15:10

Chaosit


Could you set a breakpoint at

if (image.getLocalVisibleRect(scrollBounds)) {

and see what properties (width/height/position) image and scrollBounds have?

Also, when is

private void initViews() {

called?

like image 27
martin Avatar answered Oct 20 '22 14:10

martin