gitsensible
gitsensible

Reputation: 551

Does React Native Really Support Android Fragments?

This question is harder to answer than it appears.

My impression is that React Native is primarily focused around controlling the root view of the whole activity for layouts. I think there is some caution being used in taking on fragments. As a side note React itself (not React Native) seems to have its own concept of a fragment. I am guessing even Yoga layout manager inside React Native flexible as it is does not want to deal with Android Fragments. Not an issue so much for new apps but for integrating React Native into existing apps this is real issue!

Upvotes: 31

Views: 5802

Answers (3)

Wils
Wils

Reputation: 1211

The React Native 0.66 have new instructions about the use of Android Native Fragments, more details here: Display Android Native Activity into React Native View

Upvotes: 0

Stefan Majiros
Stefan Majiros

Reputation: 584

early 2021: I have created also blog post about it: https://stefanmajiros.medium.com/integration-of-native-android-fragments-and-views-into-react-native-a4156621543b

In general, you just render placeholder in React Native (as native UI component), then you would need find the id of that placeholder in React Native, send the id to the native part, and execute command that will attach fragment to the view, similar to this:

getSupportFragmentManager()
.beginTransaction()
.add(placeholderId, yourfragment, "sometag")
.commit();

Upvotes: 1

Dmitriy Mitiai
Dmitriy Mitiai

Reputation: 1200

I don't know, perhaps you already found the solution, but it can be helpful for someone else. The solution, you provided is really has some issues with touch events when you use native android FragmentActivity or AppCompatActivity. But the react-native has own ReactFragmentActivity and if your activity extends this activity, the touch events works well, but only in separated fragment. It does not work with ViewPager and others. Only if fragment uses in separated Activity, like this:

Fragment reactFragment = new MainReactFragment();
getSupportFragmentManager().beginTransaction().replace(R.id.fragment, reactFragment, "reactFragment").commit();

EDIT:

After some trials and errors,I got it to correct work with ViewPager and TabLayout(but I think in other cases it will work as well).So, here is what I did. The ReactFragment looks like this:

public abstract class ReactFragment extends Fragment {

    private ReactRootView mReactRootView;
    private ReactInstanceManager mReactInstanceManager;

    // This method returns the name of our top-level component to show
    public abstract String getMainComponentName();

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        mReactRootView = new ReactRootView(context);
    }

    public void setmReactInstanceManager(ReactInstanceManager mReactInstanceManager){
        this.mReactInstanceManager = mReactInstanceManager;
    }

    @Override
    public ReactRootView onCreateView(LayoutInflater inflater, ViewGroup group, Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        return mReactRootView;
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        mReactRootView.startReactApplication(
                mReactInstanceManager,
                getMainComponentName(),
                null
        );
    }
}

Then implement our fragment:

public class MainReactFragment extends ReactFragment {

    @Override
    public String getMainComponentName() {
        return "reactProject";
    }
}

We need to extend our activity from ReactFragmentActivity and initialize ReactInstanceManager:

public class MainActivity extends ReactFragmentActivity {

    TabLayout tabs;
    ViewPager viewPager;
    private ReactInstanceManager mReactInstanceManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.layout_main);

        tabs = (TabLayout) findViewById(R.id.menuItemTabs);
        viewPager = (ViewPager) findViewById(R.id.tabMainMenu);

        mReactInstanceManager = ReactInstanceManager.builder()
                .setApplication(getApplication())
                .setBundleAssetName("index.android.bundle")
                .setJSMainModuleName("index.android")
                .addPackage(new MainReactPackage())
                .setUseDeveloperSupport(BuildConfig.DEBUG)
                .setInitialLifecycleState(LifecycleState.RESUMED)
                .build();

        TabsAdapter adapter = new TabsAdapter(getSupportFragmentManager(), mReactInstanceManager);
        viewPager.setAdapter(adapter);
        tabs.setupWithViewPager(viewPager);
        
    }

    @Override
    public boolean onKeyUp(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_MENU && mReactInstanceManager != null) {
            mReactInstanceManager.showDevOptionsDialog();
            return true;
        }
        return super.onKeyUp(keyCode, event);
    }

    @Override
    public void invokeDefaultOnBackPressed() {
        super.onBackPressed();
    }

    @Override
    public void onPause() {
        super.onPause();

        if (mReactInstanceManager != null) {
            mReactInstanceManager.onHostPause(this);
        }
    }

    @Override
    public void onResume() {
        super.onResume();

        if (mReactInstanceManager != null) {
            mReactInstanceManager.onHostResume(this, this);
        }
    }

    @Override
    public void onBackPressed() {
        if (mReactInstanceManager != null) {
            mReactInstanceManager.onBackPressed();
        }
        else {
            super.onBackPressed();
        }
    }
}

And, of course, TabAdapter:

public class TabsAdapter extends FragmentStatePagerAdapter {

    ReactInstanceManager mReactInstanceManager;

    public TabsAdapter(FragmentManager fragmentManager, `enter code here`ReactInstanceManager reactInstanceManager){
        super(fragmentManager);
        mReactInstanceManager = reactInstanceManager;
    }

    @Override
    public Fragment getItem(int position) {
        MainReactFragment reactFragment = new MainReactFragment();
        reactFragment.setmReactInstanceManager(mReactInstanceManager);
        return reactFragment;
    }

    @Override
    public int getCount() {
        return 2;
    }

    @Override
    public int getItemPosition(Object item) {
        return POSITION_NONE;
    }

    @Override
    public CharSequence getPageTitle(int position) {
        return "react tab " + position;
    }


    @Override
    public Parcelable saveState() {
        return null;
    }
}

RESULT:

enter image description here,

enter image description here,

enter image description here,

enter image description here

Upvotes: 1

Related Questions