Search code examples
androidlitho

Litho, ViewPager and Tabs. How to load several Tab's layouts using Litho?


I'm trying to implement a ViewPager with Tabs just like this:

snapshot

The fragment's layouts are rendered with Facebook's Litho, but the problem here is that only the first item, in this case 'Look', is loaded. See the code:

MainActivity:

public class MainActivity extends AppCompatActivity {

private FragmentPagerAdapter mPagerAdapter;
private ViewPager mViewPager;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    mPagerAdapter = new FragmentPagerAdapter(getSupportFragmentManager()) {
        private final Fragment[] mFragments = new Fragment[]{
                new Look(),
                new Chat(),
                new Flirt(),
                new Friends(),
                new Me(),
        };

        @Override
        public Fragment getItem(int position) {
            return mFragments[position];
        }

        @Override
        public int getCount() {
            return mFragments.length;
        }

    };
    mViewPager = (ViewPager) findViewById(R.id.container);
    mViewPager.setAdapter(mPagerAdapter);
    mViewPager.setOffscreenPageLimit(3);
    TabLayout tabLayout = (TabLayout) findViewById(R.id.tabs);
    tabLayout.setTabTextColors(Color.parseColor("#616161"), Color.parseColor("#ffffff"));
    tabLayout.setupWithViewPager(mViewPager);
    tabLayout.getTabAt(0).setText("LOOK");
    tabLayout.getTabAt(1).setText("CHAT");
    tabLayout.getTabAt(2).setText("FLIRT");
    tabLayout.getTabAt(3).setText("ADD");
    tabLayout.getTabAt(4).setText("ME");
    findViewById(R.id.options).setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            Intent i = new Intent(MainActivity.this, Options.class);
            startActivity(i);
        }
    });
}
}

And, for simplicity's sake, let's assume that all the fragments (Look, Chat, ...) has the same code inside its onCreateView method:

public class Flirt extends Fragment {

public Flirt() {
}

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    final ComponentContext contex = new ComponentContext(getContext());
    final Component component = Text.create(contex)
            .text("Hello World")
            .textSizeDip(50)
            .textColor(Color.GRAY)
            .build();
    return LithoView.create(contex, component);
}
}

It is expected that the five fragments' view be loaded when MainActivity is created, but only the first one, whichever it be, is displayed. What am I doing wrong? How could I get it?

EDIT:

If I change mViewPager.setOffscreenPageLimit(4) to (0), I get that all views get loaded, but one by one, and they are loaded everytime they're on focus, which is not desired. What I'm trying to get is that the five Pages get loaded at time, and only once.


Solution

  • Call this

    public static boolean incrementalMountUsesLocalVisibleBounds = false;
    

    before populating views in viewPager. This should do the trick.

    How it works

    in class IncrementalMountHelper we have the following piece of code:

    // ViewPager does not give its child views any callbacks when it moves content onto the screen,
    // so we need to attach a listener to give us the information that we require.
    ViewParent viewParent = lithoView.getParent();
    while (viewParent != null) {
      if (ComponentsConfiguration.incrementalMountUsesLocalVisibleBounds
          && viewParent instanceof ViewPager) {
        final ViewPager viewPager = (ViewPager) viewParent;
        final IncrementalMountHelper.ViewPagerListener viewPagerListener =
            new ViewPagerListener(mComponentTree, viewPager);
        ViewCompat.postOnAnimation(
            viewPager,
            new Runnable() {
              @Override
              public void run() {
                viewPager.addOnPageChangeListener(viewPagerListener);
              }
            });
        mViewPagerListeners.add(viewPagerListener);
      }
    
      if (viewParent instanceof LithoView && ((LithoView) viewParent).doesOwnIncrementalMount()) {
        lithoView.setDoesOwnIncrementalMount(true);
      }
    
      viewParent = viewParent.getParent();
    }
    

    viewPagerListener here is responsible for calling componentTree.incrementalMountComponent();

    This is not getting called because ComponentsConfiguration.incrementalMountUsesLocalVisibleBounds is false by default and you have to set it true.

    I yet do not know yet what all other effects will be when you set it true, but, setting it true makes your ViewPager work.