Chris Chris - 3 months ago 41
Android Question

Can a ReactRootView be nested (android)

I have an android application built already that I would like to use React Native in. It's a large application and I don't want to migrate everything at once. For example, my toolbar code is a bit complex and I'd like to leave it native for now. So my layout looks something like this:

<RelativeLayout
android:id="@+id/relative_layout"
android:background="@color/background_color"
android:layout_width="fill_parent"
android:layout_height="fill_parent">

<android.support.v7.widget.Toolbar
android:id="@+id/action_bar"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"/>

<com.facebook.react.ReactRootView
android:id="@+id/react_root"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>


And in onCreate() instead of creating a new ReactRootView I'm doing this:

mReactRootView = (ReactRootView) findViewById(R.id.react_root);
... // Other init
setContentView(mReactRootView);


However, I'm getting an error on setContentView(mReactRootView):

java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.


This leads me to believe I cannot nest a ReactRootView inside other views. How would I go about keeping my current Toolbar implementation, but still using React Native for the rest of the page? I know this exists: https://facebook.github.io/react-native/docs/native-components-android.html Is bridging to the native component my only option?

Edit: Followed Konstantin's suggestion below and here is what I have:

ReactFragment.java

public class ReactFragment extends Fragment implements DefaultHardwareBackBtnHandler {
ViewGroup group = (ViewGroup) inflater.inflate(R.layout.react_fragment, container, false);
activity = getActivity();

mReactRootView = new ReactRootView(activity);
mReactInstanceManager = ReactInstanceManager.builder()
.setApplication(activity.getApplication())
.setBundleAssetName("index.bundle")
.setJSMainModuleName("index")
.addPackage(new MainReactPackage())
.setUseDeveloperSupport(true)
.setInitialLifecycleState(LifecycleState.RESUMED)
.build();

group.addView(mReactRootView);
mReactRootView.startReactApplication(mReactInstanceManager, "AwesomeProject", null);
mReactInstanceManager.onHostResume(activity, this);
return group;
}
// onPause() onDestroy() etc.


react_fragment.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/react_root"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
</FrameLayout>


Added to main_content.xml

...
<fragment
android:name="com.amazon.kindlestore.main.ReactFragment"
android:id="@+id/react_frag"
android:layout_width="fill_parent"
android:layout_height="fill_parent"/>
...


And in my main activity I'm adding the Fragment like this:

ReactFragment reactFragment = new ReactFragment();
FragmentManager fm = getFragmentManager();
FragmentTransaction fragmentTransaction = fm.beginTransaction();
fragmentTransaction.add(R.id.react_frag, reactFragment);
fragmentTransaction.commit();


This seems to be working, I have my react components rendering below my native Toolbar

Answer

You have to create a fragment for RN ui part and use it in your xml layout. In this fragment create ReactRootView, then create or obtain ReactInstanceManager, which you'll need to hook to fragment's lifecycle callbacks. And then you start your RN application inside the ReactRootView using this instance manager.

Check out this official guide (don't forget to select Android in top menu)