react-native CoordinatorLayout 和 BottomSheetBehavior 的包装器
react-native wrapper for CoordinatorLayout and BottomSheetBehavior
我一直在编写一个原生 android 模块,它包装了一个 BottomSheetBehavior。
一个非常简单的BottomSheetBehavior可以这样实现
https://gist.github.com/cesardeazevedo/a4dc4ed12df33fe1877fc6cea42475ae
我遇到的第一件事是,整个页面必须是 child 的 CoordinatorLayout 和末尾的 BottomSheetBehavior。
所以我不得不编写 2 个本机模块,<CoordinatorLayout />
和 <BottomSheetBehavior />
。
这是 bottomSheetBehavior 包装器。
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);
}
}
然后我的react组件就变成了这样
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>
)
而且有效!
但我一直在努力让 BottomSheet 用 wrap_content
包裹他们的 child,它不应该滑动整个屏幕,它应该只滑动包裹的内容(在这种情况下是 lorem ipsum 文本),它适用于 android 组件,但不适用于 React 组件。那么,如何制作一个 RelativeLayout
来包装一个 React <View style={{height: 300}} />
组件呢?我也尝试过实现一些 measure shadownode,但没有按预期工作,我不知道它们是如何工作的。
我已经在我的 github 中添加了这个示例,供每个想尝试的人使用。
https://github.com/cesardeazevedo/react-native-bottom-sheet-behavior
经过大量调试,终于明白了,我必须做两件事,首先是覆盖 onMeasure 函数并将儿童身高应用到 setMeasuredDimension
,显然解决了身高问题,但是玩了一会儿之后,状态的任何变化都会破坏 bottomSheet 的位置,所以我不得不通过 UIManager.dispatchViewManagerCommand 为每个状态变化调用 requestLayout,并且效果很好。
所以,这就是修复的实现。
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,
[],
)
}
更新
我意识到滑动时更新状态会闪烁所有布局,在查看一些库代码后,我找到了 needsCustomLayoutForChildren 函数,该函数在 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;
}
所以我 fixed 在 CoordinatorLayoutManager.java
返回 true
这是它的样子。
我一直在编写一个原生 android 模块,它包装了一个 BottomSheetBehavior。
一个非常简单的BottomSheetBehavior可以这样实现 https://gist.github.com/cesardeazevedo/a4dc4ed12df33fe1877fc6cea42475ae
我遇到的第一件事是,整个页面必须是 child 的 CoordinatorLayout 和末尾的 BottomSheetBehavior。
所以我不得不编写 2 个本机模块,<CoordinatorLayout />
和 <BottomSheetBehavior />
。
这是 bottomSheetBehavior 包装器。
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);
}
}
然后我的react组件就变成了这样
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>
)
而且有效!
但我一直在努力让 BottomSheet 用 wrap_content
包裹他们的 child,它不应该滑动整个屏幕,它应该只滑动包裹的内容(在这种情况下是 lorem ipsum 文本),它适用于 android 组件,但不适用于 React 组件。那么,如何制作一个 RelativeLayout
来包装一个 React <View style={{height: 300}} />
组件呢?我也尝试过实现一些 measure shadownode,但没有按预期工作,我不知道它们是如何工作的。
我已经在我的 github 中添加了这个示例,供每个想尝试的人使用。 https://github.com/cesardeazevedo/react-native-bottom-sheet-behavior
经过大量调试,终于明白了,我必须做两件事,首先是覆盖 onMeasure 函数并将儿童身高应用到 setMeasuredDimension
,显然解决了身高问题,但是玩了一会儿之后,状态的任何变化都会破坏 bottomSheet 的位置,所以我不得不通过 UIManager.dispatchViewManagerCommand 为每个状态变化调用 requestLayout,并且效果很好。
所以,这就是修复的实现。
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,
[],
)
}
更新
我意识到滑动时更新状态会闪烁所有布局,在查看一些库代码后,我找到了 needsCustomLayoutForChildren 函数,该函数在 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;
}
所以我 fixed 在 CoordinatorLayoutManager.java
返回 true这是它的样子。