在片段中反应原生

时间:2016-02-05 10:12:51

标签: android-fragments react-native

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

{% set onlyLimitedAmtProds = [] %} {% for product in products %} {% if product.in_stock_amount < 3 %} {% set onlyLimitedAmtProds = onlyLimitedAmtProds|merge(product) %} {% endif %} {% endfor %}

3 个答案:

答案 0 :(得分:25)

我设法通过大量试验和错误来解决这个问题。我在互联网上看到了这个问题,并认为这是发布答案的最佳地点。以下是如何处理最新版本的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,AKA是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非常重要。此外,在父活动中注册onHostPause()onHostResume()一开始并不直观,但最终证明一旦将ReactNativeInstanceManager抽象为全局而不是将其保留在活动或片段上

希望这可以帮助其他人!

答案 1 :(得分:2)

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

请确保您已正确设置React Native主机,因为该片段会尝试在应用程序级别访问React Native主机,或在子类中重载它:

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

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

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

答案 2 :(得分:1)

有些库可供您处理。

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

根据链接的GitHub存储库中的说明:

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

    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();