【发布时间】: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
如何在片段内启动 react-native? 将 react-native 放入 Fragment 时,onCreateView 函数无法从 mReactRootView 返回 View。
View view = inflater.inflate(mReactRootView. , container, false);
【问题讨论】:
标签: android-fragments react-native
我已经通过反复试验设法解决了这个问题。我在互联网上看到过这个问题,并认为这是发布答案的最佳地点。以下是如何使用最新版本的 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 上,最终证明更简单。
希望这对其他人有帮助!
【讨论】:
ReactActivity 或在使用react-native init 创建的应用程序中尝试时,它是否有效?
有一些库可以为您处理这个问题。
我使用的是react-native-android-fragment
按照链接的 GitHub 存储库中的说明:
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'
}
将您的反应代码构建到片段中
Fragment reactFragment = new ReactFragment.Builder()
.setComponentName("HelloWorld")
.setLaunchOptions(launchOptions) // A Bundle of launch options
.build();
将 Fragment 放置在您的 XML 布局文件中的 FrameLayout 中。在我的例子中,FrameLayout ID 是 react_holder。
getSupportFragmentManager()
.beginTransaction()
.add(R.id.react_holder, reactFragment)
.commit();
【讨论】:
现在有一个官方的ReactFragment 可用的here 可用于在片段内托管本机反应。
只要确保你的 react 本地主机设置正确,因为片段会尝试在应用程序级别访问 react 本地主机,或者在子类中重载它:
// inside the ReactFragment
protected ReactNativeHost getReactNativeHost() {
return ((ReactApplication) getActivity().getApplication()).getReactNativeHost();
}
然后您可以使用以下方法创建片段:
val reactNativeProcessFragment = ReactFragment.Builder()
.setComponentName("nameUsedIn_AppRegistry.registerComponent")
.build()
【讨论】: