I've been writing a native android module that wraps a BottomSheetBehavior.
A very simple BottomSheetBehavior can be implemented like this https://gist.github.com/cesardeazevedo/a4dc4ed12df33fe1877fc6cea42475ae
The first thing that i faced, the entire page must be a child of a CoordinatorLayout and the BottomSheetBehavior at the end of it.
So i had to write 2 native modules, <CoordinatorLayout />
and <BottomSheetBehavior />
.
This is the bottomSheetBehavior wrapper.
BottomSheetBehaviorManager.java
public class BottomSheetBehaviorManager extends ViewGroupManager<BottomSheetBehaviorView> {
@Override
public BottomSheetBehaviorView createViewInstance(ThemedReactContext context) {
return new BottomSheetBehaviorView(context);
}
}
BottomSheetBehaviorView.java
public class BottomSheetBehaviorView extends RelativeLayout {
public BottomSheetBehaviorView(Context context) {
super(context);
int width = ViewGroup.LayoutParams.WRAP_CONTENT;
int height = ViewGroup.LayoutParams.WRAP_CONTENT;
// int height = 1000; // fixed a height works, it only slide up half of the screen
CoordinatorLayout.LayoutParams params = new CoordinatorLayout.LayoutParams(width, height);
params.setBehavior(new BottomSheetBehavior());
this.setLayoutParams(params);
BottomSheetBehavior<BottomSheetBehaviorView> bottomSheetBehavior = BottomSheetBehavior.from(this);
bottomSheetBehavior.setHideable(false);
bottomSheetBehavior.setPeekHeight(200);
}
}
And my react component become like this.
index.android.js
return () {
<CoordinatorLayout style={{flex: 1}}>
<View><!--app--></View>
<BottomSheetBehavior>
<View style={{height: 300}}> <!--height doesnt work-->
<Text>BottomSheetBehavior !</Text>
</View>
</BottomSheetBehavior>
</CoordinatorLayout>
)
And it works!
But i've been struggling to make the BottomSheet wrap their childs with wrap_content
, it's was not supposed to slide the entire screen, it should only slide through the wrapped content (in this case a lorem ipsum text), it works with android components but it not working with react components. So, how to make a RelativeLayout
to wraps a react <View style={{height: 300}} />
component? I also have tried to implemented some measure shadownode, but didn't worked as expected, i don't know how they work.
I've added this example on my github for everyone wants to try it. https://github.com/cesardeazevedo/react-native-bottom-sheet-behavior
After a lot of debugging, finally i got it, i had to do 2 things, first was to override the onMeasure function and apply the child height into setMeasuredDimension
, and apparently fixed the height issue, but after playing a little bit, any change on the state breaks the position of the bottomSheet, so i had to call requestLayout for each state change through UIManager.dispatchViewManagerCommand, and works very well.
So, that's the implementation that fixes.
commit
BottomSheetBehaviorView.js
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
View child = this.getChildAt(0);
if (child != null) {
setMeasuredDimension(widthMeasureSpec, child.getHeight());
}
}
BottomSheetBehaviorManager.js
@Override
public Map<String, Integer> getCommandsMap() {
return MapBuilder.of("setRequestLayout", COMMAND_SET_REQUEST_LAYOUT);
}
@Override
public void receiveCommand(BottomSheetBehaviorView view, int commandType, @Nullable ReadableArray args) {
if (commandType == COMMAND_SET_REQUEST_LAYOUT) {
setRequestLayout(view);
}
}
private void setRequestLayout(BottomSheetBehaviorView view) {
view.requestLayout();
}
BottomSheetBehavior.js
componentDidUpdate() {
UIManager.dispatchViewManagerCommand(
findNodeHandle(this),
UIManager.RCTBottomSheetBehaviorAndroid.Commands.setRequestLayout,
[],
)
}
I realized that updating state while sliding flickers all layout, after looking some libraries code, i found the needsCustomLayoutForChildren function, which is described on ViewGroupManager.java
/**
* Returns whether this View type needs to handle laying out its own children instead of
* deferring to the standard css-layout algorithm.
* Returns true for the layout to *not* be automatically invoked. Instead onLayout will be
* invoked as normal and it is the View instance's responsibility to properly call layout on its
* children.
* Returns false for the default behavior of automatically laying out children without going
* through the ViewGroup's onLayout method. In that case, onLayout for this View type must *not*
* call layout on its children.
*/
public boolean needsCustomLayoutForChildren() {
return false;
}
So i fixed returning true on CoordinatorLayoutManager.java
Here is how it looks like.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With