ViewPager with Fragments that contain TableLayouts loads slowly:

雨燕双飞 提交于 2019-12-04 07:49:00
Asaf Pinhassi

Since you use pager, you load more then just the first fragment when you start the activity. Make the initial fragment view with a progress bar visible and the table hidden and load the data only when you switch to that Fragment.

in the activity use

mPager.setOnPageChangeListener(new OnPageChangeListener() {

            @Override
            public void onPageSelected(int position) {
                Fragment f = mAdapter.getFragment(mPager.getCurrentItem());
                f.loadData(); // when you finish loading the data, hide the progress bar and show the table

            }

            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

            }

            @Override
            public void onPageScrollStateChanged(int state) {
            }
        });

To use mAdapter.getFragment(), inherit your adapter from:

    public abstract class SmartFragmentPagerAdapter extends FragmentPagerAdapter{

    private SparseArray<String> mFragmentTags;
    private FragmentManager mFragmentManager;

    public SmartFragmentPagerAdapter(FragmentManager fragmentManager) {
        super(fragmentManager);
        mFragmentManager = fragmentManager;
        mFragmentTags = new SparseArray<String>();
    }

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        Object obj = super.instantiateItem(container, position);
        if (obj instanceof Fragment) {
            // record the fragment tag here.
            Fragment f = (Fragment) obj;
            String tag = f.getTag();
            mFragmentTags.put(position, tag);
        }
        return obj;
    }

    public Fragment getFragment(int position) {
        String tag = mFragmentTags.get(position);
        if (tag == null)
            return null;
        return mFragmentManager.findFragmentByTag(tag);
    }

    public FragmentManager getFragmentManager(){
        return mFragmentManager;
    }

}

This for 3 Fragments takes about 10 seconds to load, so the user get a black screen for 10 second before the Activity is actually loaded.

The reason for that is the amount of widgets you use in your layouts plus the depth of the view hierarchy of your Activity. From what you posted you have something like this:

Possible wrapper for ViewPager in the Activity -> ViewPager -> At least a ScrollView(as you say something about scrolling vertically and horizontally) -> Linearlayout -> TableLayout -> TableRow -> content of TableRow

That is a depth of 7(if I'm not mistaken the support fragment framework also inserts some views in between). This is very bad for performance. You also have a lot of widgets(assuming those for loops run for at most 10 iterations, otherwise it's just really bad) in a single Fragment and taking in consideration that the second fragment will also have its view loaded you can see that this will be again a performance hit.

It would be helpful to post your current layouts to see if they couldn't be optimized.

There are small improvements that you could make to your current code but this will not help you to go from 10 seconds to at most 1 second like you should:

There is no point in this code:

if (container == null) {
    return null;
}

I don't know what this does:

SGRaportManagerAppObj.getInstance().parametersRepository.getParametersRepository()

but see if you don't create useless objects(Is that a singleton?). Don't put it in the for loop as it will be run every time, use a normal for to avoid the use of the Iterator:

final int count = SGRaportManagerAppObj.getInstance().parametersRepository.getParametersRepository().size();
for (int i = 0; i < count; i++) {
     final Parameter parameter = SGRaportManagerAppObj.getInstance().parametersRepository.getParametersRepository().get(i);
     // ... rest of code

You should do the same in the other for loops.

Retrieve those resources(colors and drawables) outside the for loops, in the loop only assign them to the views.

Edit: Your layout is very deep and with too many views so the performance will be poor. The only way to overcome this is to use a widget that recycles its views(I know what you said). Alternatives would be:

  • creating those views on a background thread(I wouldn't do this because it's very risky)
  • you could modify the fragment to simply inflate the layout in the onCreateView and return combined with showing a loading ProgressDialog. After the Fragment becomes visible you would start creating the real view of the Fragment in chunks and finally attach it to the fragment. This would be very bad for the user experience.

And as you use a ViewPager will have additional work to make things go alright between the page switch. Maybe you could rethink your current setup.

Most of the time is beeing spend to populate/create the TableLayout. If there is a big amount of data recieved from the server then it's needed to create a big amount of TextViews and set them in the TableLayout

Then do not use a TableLayout. Use a ListView or some other form of AdapterView, so that you only need to "populate/create" enough to fill the visible space, then can populate more as the user scrolls (if the user scrolls at all).

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!