-
Notifications
You must be signed in to change notification settings - Fork 4.8k
docs: add fragment to native-components-android.md #2599
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
5d5adec
docs: add fragment to native-components-android.md
f4z3k4s a40f3a9
chore: fix linting errors
f4z3k4s 85ab41f
chore: fix alex errors
f4z3k4s 9e6ed65
docs: update native-components-android.md
f4z3k4s 360a153
docs: update native-components-android.md
f4z3k4s File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -188,3 +188,274 @@ MyCustomView.propTypes = { | |
|
||
var RCTMyCustomView = requireNativeComponent(`RCTMyCustomView`); | ||
``` | ||
|
||
# Integration with an Android Fragment Example | ||
|
||
In order to integrate existing Native UI elements to your React Native app, you might need to use Android Fragments to give you a more granular control over your native component than returning a `View` from your `ViewManager`. You will need this if you want to add custom logic that is tied to your view with the help of [lifecycle methods](https://developer.android.com/guide/fragments/lifecycle), such as `onViewCreated`, `onPause`, `onResume`. The following steps will show you how to do it: | ||
|
||
## 1. Create a `Fragment` | ||
|
||
`MyFragment.java` | ||
|
||
```java | ||
// replace with your package | ||
package com.mypackage; | ||
|
||
import android.os.Bundle; | ||
import android.view.LayoutInflater; | ||
import android.view.View; | ||
import android.view.ViewGroup; | ||
import androidx.fragment.app.Fragment; | ||
|
||
// replace with your view's import | ||
import com.mypackage.CustomView; | ||
|
||
public class MyFragment extends Fragment { | ||
CustomView customView; | ||
|
||
@Override | ||
public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) { | ||
super.onCreateView(inflater, parent, savedInstanceState); | ||
customView = new CustomView(); | ||
return customView; // this CustomView could be any view that you want to render | ||
} | ||
|
||
@Override | ||
public void onViewCreated(View view, Bundle savedInstanceState) { | ||
super.onViewCreated(view, savedInstanceState); | ||
// do any logic that should happen in an `onCreate` method, e.g: | ||
// customView.onCreate(savedInstanceState); | ||
} | ||
|
||
@Override | ||
public void onPause() { | ||
super.onPause(); | ||
// do any logic that should happen in an `onPause` method | ||
// e.g.: customView.onPause(); | ||
} | ||
|
||
@Override | ||
public void onResume() { | ||
super.onResume(); | ||
// do any logic that should happen in an `onResume` method | ||
// e.g.: customView.onResume(); | ||
} | ||
|
||
@Override | ||
public void onDestroy() { | ||
super.onDestroy(); | ||
// do any logic that should happen in an `onDestroy` method | ||
// e.g.: customView.onDestroy(); | ||
} | ||
} | ||
``` | ||
|
||
## 2. Create the `ViewManager` subclass | ||
|
||
`MyViewManager.java` | ||
|
||
```java | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would add imports here. Specifically the one on |
||
// replace with your package | ||
package com.mypackage; | ||
|
||
import android.view.Choreographer; | ||
import android.view.View; | ||
import android.widget.FrameLayout; | ||
|
||
import androidx.annotation.NonNull; | ||
import androidx.annotation.Nullable; | ||
import androidx.fragment.app.FragmentActivity; | ||
|
||
import com.facebook.react.bridge.ReactApplicationContext; | ||
import com.facebook.react.bridge.ReadableArray; | ||
import com.facebook.react.common.MapBuilder; | ||
import com.facebook.react.uimanager.annotations.ReactProp; | ||
import com.facebook.react.uimanager.annotations.ReactPropGroup; | ||
import com.facebook.react.uimanager.ViewGroupManager; | ||
import com.facebook.react.uimanager.ThemedReactContext; | ||
|
||
import java.util.Map; | ||
|
||
public class MyViewManager extends ViewGroupManager<FrameLayout> { | ||
|
||
public static final String REACT_CLASS = "MyViewManager"; | ||
public final int COMMAND_CREATE = 1; | ||
|
||
ReactApplicationContext reactContext; | ||
|
||
public MyViewManager(ReactApplicationContext reactContext) { | ||
this.reactContext = reactContext; | ||
} | ||
|
||
@Override | ||
public String getName() { | ||
return REACT_CLASS; | ||
} | ||
|
||
/** | ||
* Return a FrameLayout which will later hold the Fragment | ||
*/ | ||
@Override | ||
public FrameLayout createViewInstance(ThemedReactContext reactContext) { | ||
return new FrameLayout(reactContext); | ||
} | ||
|
||
/** | ||
* Map the "create" command to an integer | ||
*/ | ||
@Nullable | ||
@Override | ||
public Map<String, Integer> getCommandsMap() { | ||
return MapBuilder.of("create", COMMAND_CREATE); | ||
} | ||
|
||
/** | ||
* Handle "create" command (called from JS) and call createFragment method | ||
*/ | ||
@Override | ||
public void receiveCommand(@NonNull FrameLayout root, String commandId, @Nullable ReadableArray args) { | ||
super.receiveCommand(root, commandId, args); | ||
int reactNativeViewId = args.getInt(0); | ||
int commandIdInt = Integer.parseInt(commandId); | ||
|
||
switch (commandIdInt) { | ||
case COMMAND_CREATE: | ||
createFragment(root, reactNativeViewId); | ||
break; | ||
default: {} | ||
} | ||
} | ||
|
||
/** | ||
* Replace your React Native view with a custom fragment | ||
*/ | ||
public void createFragment(FrameLayout root, int reactNativeViewId) { | ||
ViewGroup parentView = (ViewGroup) root.findViewById(reactNativeViewId).getParent(); | ||
setupLayout(parentView); | ||
|
||
final MyFragment myFragment = new MyFragment(); | ||
FragmentActivity activity = (FragmentActivity) reactContext.getCurrentActivity(); | ||
activity.getSupportFragmentManager() | ||
.beginTransaction() | ||
.replace(reactNativeViewId, myFragment, String.valueOf(reactNativeViewId)) | ||
.commit(); | ||
} | ||
|
||
public void setupLayout(View view) { | ||
Choreographer.getInstance().postFrameCallback(new Choreographer.FrameCallback() { | ||
@Override | ||
public void doFrame(long frameTimeNanos) { | ||
manuallyLayoutChildren(view); | ||
view.getViewTreeObserver().dispatchOnGlobalLayout(); | ||
Choreographer.getInstance().postFrameCallback(this); | ||
} | ||
}); | ||
} | ||
|
||
/** | ||
* Layout all children properly | ||
*/ | ||
public void manuallyLayoutChildren(View view) { | ||
// propWidth and propHeight coming from react-native props | ||
int width = propWidth; | ||
int height = propHeight; | ||
|
||
view.measure( | ||
View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY), | ||
View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY)); | ||
|
||
view.layout(0, 0, width, height); | ||
} | ||
``` | ||
|
||
## 3. Register the `ViewManager` | ||
|
||
`MyPackage.java` | ||
|
||
```java | ||
// replace with your package | ||
package com.mypackage; | ||
|
||
import com.facebook.react.ReactPackage; | ||
import com.facebook.react.bridge.ReactApplicationContext; | ||
import com.facebook.react.uimanager.ViewManager; | ||
|
||
import java.util.Arrays; | ||
import java.util.List; | ||
|
||
public class MyPackage implements ReactPackage { | ||
|
||
@Override | ||
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) { | ||
return Arrays.<ViewManager>asList( | ||
new MyViewManager(reactContext) | ||
); | ||
} | ||
|
||
} | ||
``` | ||
|
||
## 4. Register the `Package` | ||
|
||
`MainApplication.java` | ||
|
||
```java | ||
@Override | ||
protected List<ReactPackage> getPackages() { | ||
List<ReactPackage> packages = new PackageList(this).getPackages(); | ||
... | ||
packages.add(new MyPackage()); | ||
return packages; | ||
} | ||
``` | ||
|
||
## 5. Implement the JavaScript module | ||
|
||
I. `MyViewManager.jsx` | ||
|
||
```jsx | ||
import { requireNativeComponent } from 'react-native'; | ||
|
||
export const MyViewManager = requireNativeComponent( | ||
'MyViewManager' | ||
); | ||
``` | ||
|
||
II. ` MyView.jsx` calling the `create` method | ||
|
||
```jsx | ||
import React, { useEffect, useRef } from 'react'; | ||
import { UIManager, findNodeHandle } from 'react-native'; | ||
|
||
import { MyViewManager } from './my-view-manager'; | ||
|
||
const createFragment = (viewId) => | ||
UIManager.dispatchViewManagerCommand( | ||
viewId, | ||
UIManager.MyViewManager.Commands.create.toString(), // we are calling the 'create' command | ||
[viewId] | ||
); | ||
|
||
export const MyView = ({ style }) => { | ||
const ref = useRef(null); | ||
|
||
useEffect(() => { | ||
const viewId = findNodeHandle(ref.current); | ||
createFragment(viewId!); | ||
}, []); | ||
|
||
return ( | ||
<MyViewManager | ||
style={{ | ||
...(style || {}), | ||
height: style && style.height !== undefined ? style.height || '100%', | ||
width: style && style.width !== undefined ? style.width || '100%' | ||
}} | ||
ref={ref} | ||
/> | ||
); | ||
}; | ||
|
||
``` | ||
|
||
If you want to expose property setters using `@ReactProp` (or `@ReactPropGroup`) annotation: _see ImageView example above_ |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adding an import on the
androidx
version ofFragment
would be beneficial.