在片段内反应原生 [英] React-native inside a Fragment

查看:25
本文介绍了在片段内反应原生的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如何在片段内启动 react-native?将 react-native 放入 Fragment 中时,onCreateView 函数无法从 mReactRootView 返回 View.

How to Start react-native inside of a fragment? While putting react-native inside Fragment, onCreateView function is unable to return View from mReactRootView.

View view = inflater.inflate(mReactRootView., container, false);

推荐答案

我已经通过多次试验和错误设法解决了这个问题.我在互联网上看到过这个问题,并认为这是发布答案的最佳地点.以下是如何使用最新版本的 React(撰写本文时为 0.29):

I've managed to figure this out with much trial and error. I've seen this question asked around the internet and thought that this was the best place to post the answer. Here is how to do with the latest version of React (0.29 as of this writing):

我们要做的第一件事是创建一个抽象的 ReactFragment 类,我们将在整个应用程序中使用该类:

The first thing we'll do is create an abstract ReactFragment class that we will use throughout our app:

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);
        mReactInstanceManager =
                ((MyApplication) getActivity().getApplication())
                        .getReactNativeHost()
                        .getReactInstanceManager();

    }

    @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
        );
    }
}

我们现在可以创建渲染 React Native 组件的片段,例如:

We'll now be able to create fragments that render React Native components, e.g.:

public class HelloFragment extends ReactFragment {
    @Override
    public String getMainComponentName() { 
        return "hellocomponent"; // name of our React Native component we've registered 
    }
}

不过,还需要做更多的工作.我们的父 Activity 需要向 ReactInstanceManager 传递一些东西,以便 React Native 生命周期正常工作.这是我最后的结果:

A little more work is required, though. Our parent Activity needs to pass some things into the ReactInstanceManager in order for the React Native lifecycle to work properly. Here's what I ended up with:

public class FragmentActivity extends AppCompatActivity implements DefaultHardwareBackBtnHandler {
    /*
    * Get the ReactInstanceManager, AKA the bridge between JS and Android
    * We use a singleton here so we can reuse the instance throughout our app
    * instead of constantly re-instantiating and re-downloading the bundle
    */
    private ReactInstanceManager mReactInstanceManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_fragment);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                        .setAction("Action", null).show();
            }
        });

        /**
         * Get the reference to the ReactInstanceManager
         */
         mReactInstanceManager =
             ((MyApplication) getApplication()).getReactNativeHost().getReactInstanceManager();


        /*
        * We can instantiate a fragment to show for Activity programmatically,
        * or using the layout XML files.
        * This doesn't necessarily have to be a ReactFragment, any Fragment type will do.
        */

        Fragment viewFragment = new HelloFragment();
        getFragmentManager().beginTransaction().add(R.id.container, viewFragment).commit();
    }

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

    /*
     * Any activity that uses the ReactFragment or ReactActivty
     * Needs to call onHostPause() on the ReactInstanceManager
     */
    @Override
    protected void onPause() {
        super.onPause();

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

    /*
     * Same as onPause - need to call onHostResume
     * on our ReactInstanceManager
     */
    @Override
    protected void onResume() {
        super.onResume();

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

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

最后,您会注意到在整个代码中对 (MyApplication) 的引用;这是一个全局的 Application 对象,用于包含我们的 ReactInstanceManager,也就是 Android 和 React Native 之间的桥梁.这是 React Native 项目内部使用的模式,所以我简单地复制了它.以下是它的实现方式:

Finally, you'll notice the reference to (MyApplication) throughout the code; this is a global Application object to contain our ReactInstanceManager, AKA the bridge between Android and React Native. This is the pattern that the React Native projects use internally, so I simply copied it. Here's how it's implemented:

public class MyApplication extends Application implements ReactApplication {
    private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
        @Override
        public boolean getUseDeveloperSupport() {
            return true;
        }

        @Override
        public List<ReactPackage> getPackages() {
            return Arrays.<ReactPackage>asList(
                    new MainReactPackage()
            );
        }
    };

    @Override
    public ReactNativeHost getReactNativeHost() {
        return mReactNativeHost;
    }
}

最棘手的一点是弄清楚 Fragment 和 Activity 之间的生命周期;ReactRootView 需要对 Activity 上下文的引用才能实例化,因此确保 getActivity() 不会为 null 很重要.此外,在父 Activity 中注册 onHostPause()onHostResume() 起初是不直观的,但一旦 ReactNativeInstanceManager 被抽象化,最终证明更简单远离全局,而不是将其保留在 Activity 或 Fragment 中.

The trickiest bit was figuring out the lifecycle between the Fragment and the Activity; the ReactRootView needs a reference to the Activity context in order to instantiate, so making sure that getActivity() would not be null was important. Also, registering the onHostPause() and onHostResume() in the parent Activity was unintuitive at first, but ultimately proved simpler once the ReactNativeInstanceManager was abstracted away into a global instead of keeping it on the Activity or Fragment.

希望这能帮助其他人!

这篇关于在片段内反应原生的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆