【问题标题】:React-native inside a Fragment片段内的反应原生
【发布时间】:2016-02-05 10:12:51
【问题描述】:

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

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

【问题讨论】:

    标签: android-fragments react-native


    【解决方案1】:

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

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

    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 组件的片段,例如:

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

    不过,还需要做更多的工作。我们的父 Activity 需要将一些东西传递给 ReactInstanceManager 以使 React Native 生命周期正常工作。这是我最终得到的结果:

    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 项目内部使用的模式,所以我简单地复制了它。以下是它的实现方式:

    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 上,最终证明更简单。

    希望这对其他人有帮助!

    【讨论】:

    • 我已经尝试过你的实现。它正在片段中打开反应页面,但反应中的触摸侦听器不起作用。你能建议一些方法来处理这个自定义视图吗?
    • @Corrupt 我不知道为什么会这样。当您使用普通的ReactActivity 或在使用react-native init 创建的应用程序中尝试时,它是否有效?
    • 我已经尝试过反应活动并使用上述命令制作项目,但它不起作用。我在我的日志猫中收到“无法向 JS 发送触摸,因为尚未附加催化剂实例”。
    • @Corrupt 尝试将此添加到您的活动中:
    • 我要补充的,你没有提到。
    【解决方案2】:

    有一些库可以为您处理这个问题。

    我使用的是react-native-android-fragment

    按照链接的 GitHub 存储库中的说明:

    1. 将以下行添加到您的 build.gradle compile 'com.github.hudl:react-native-android-fragment:v0.43.2'

    例如

    allprojects {
      repositories {
        ...
        maven { url 'https://jitpack.io' }
      }
    }
    
    dependencies {
      // Version will correspond to its dependnecy on React Native
      compile 'com.github.hudl:react-native-android-fragment:v0.43.2'
    }
    
    1. 将您的反应代码构建到片段中

      Fragment reactFragment = new ReactFragment.Builder() .setComponentName("HelloWorld") .setLaunchOptions(launchOptions) // A Bundle of launch options .build();

    2. 将 Fragment 放置在您的 XML 布局文件中的 FrameLayout 中。在我的例子中,FrameLayout ID 是 react_holder。

      getSupportFragmentManager() .beginTransaction() .add(R.id.react_holder, reactFragment) .commit();

    【讨论】:

    【解决方案3】:

    现在有一个官方的ReactFragment 可用的here 可用于在片段内托管本机反应。

    只要确保你的 react 本地主机设置正确,因为片段会尝试在应用程序级别访问 react 本地主机,或者在子类中重载它:

    // inside the ReactFragment
    protected ReactNativeHost getReactNativeHost() {
        return ((ReactApplication) getActivity().getApplication()).getReactNativeHost();
    }
    

    然后您可以使用以下方法创建片段:

    val reactNativeProcessFragment = ReactFragment.Builder()
        .setComponentName("nameUsedIn_AppRegistry.registerComponent")
        .build()
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-02-28
      • 2018-01-31
      • 2021-12-30
      • 2020-11-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多