无法在 android 反应本机模块中调整线性布局子项的大小

Cannot resize linear layout children in android react native module

Full code here

Video of correct behavior in java and incorrect in react native here

我修改了一个线性布局,通过调整左子元素的大小来响应触摸,而右子元素占据 space 的其余部分,模拟水平滚动,可以是 'opened' 或 'closed' 使用下面的代码

    LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) leftView.getLayoutParams();
    //Log.d("ll","width " + newWidth);
    lp.width=newWidth;
    leftView.setLayoutParams(lp);

在 react native 中,touch 仍在调用此方法并记录预期值,但未更新子项的大小。它唯一更新的时间是当我将可见性切换为消失然后再次可见时。从 java 调用视图上的 invalidate/requestLayout 或从 js 调用 forceUpdate 没有任何效果。

是否需要调用其他代码来使视图无效并重绘?是否有提示我需要给出该组件滚动或响应触摸的反应?

尝试一项一项地删除版面的内容。可能是 children 具有换行内容的其中之一。您可以为此使用 hierarchyViewer 和 uiautomatorviewer 还要考虑重量。如果可能 post hierarchyViewer 数据。

LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) leftView.getLayoutParams();
lp.width=newWidth;
leftView.setLayoutParams(lp);
leftView.requestLayout(); 

以上代码可以解决您的问题。

RN android 在大多数情况下确实不会更新子布局或可见性或适配器更改。通过在需要更新时将挂钩插入自定义视图,然后调用此代码通常会恢复正常行为。在某些情况下,测量不会像正常情况那样发生,我必须发布延迟运行,然后导致此无效。处理节点的父节点可能并非在所有情况下都是绝对必要的,但对于某些情况是这样。

在视图管理器中

Method markNewLayout, getShadowNode;

public ViewManager(){
    super();
    if (markNewLayout == null) {
        try {
            markNewLayout = CSSNode.class.getDeclaredMethod("markHasNewLayout");
            markNewLayout.setAccessible(true);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    try{
    if (getShadowNode==null){
        getShadowNode = UIImplementation.class.getDeclaredMethod("resolveShadowNode",int.class);
        getShadowNode.setAccessible(true);
    }
    } catch (Exception e) {
    e.printStackTrace();
}
public class MyShadowNode extends LayoutShadowNode {
@Override
public void markUpdated(){
    super.markUpdated();
    if (hasNewLayout()) markLayoutSeen();
    dirty();
}
@Override
public boolean isDirty(){
    return true;
}




 @Override
    protected CustomView createViewInstance(final ThemedReactContext reactContext) {

 view.setRnUpdateListener(new CustomView.RNUpdateListener() {
            MyShadowNode node;
            @Override
            public void needsUpdate() {
                view.requestLayout();


                Runnable r = new Runnable() {
                    @Override
                    public void run() {

                        if (node ==null){
                            try {
                                node = (MyShadowNode) getShadowNode.invoke(uiImplementation, view.getId());
                            }
                            catch (Exception e){
                                e.printStackTrace();
                            }
                        }
                            if (node != null) {
                                if (node.hasNewLayout()) node.markLayoutSeen();
                                ReactShadowNode parent = node.getParent();
                                while (parent != null) {
                                    if (parent.hasNewLayout()) {
                                        try {
                                            markNewLayout.invoke(parent,view.getId());
                                        } catch (Exception e) {
                                            e.printStackTrace();
                                        }
                                        parent.markLayoutSeen();
                                    }
                                    parent = parent.getParent();
                                }
                                node.markUpdated();
                            }
                            Log.d(getName(), "markUpdated");
                    }

                };
                reactContext.runOnNativeModulesQueueThread(r);
            }
        });


    }

我遇到了同样的问题,我在 this React Native 存储库的旧报告问题上找到了答案。

在您的自定义布局中添加以下代码,之后甚至不需要调用 requestLayout() 或 invalidate()。一旦您更新布局的 LayoutParams,更改就会传播。此解决方案与 ReactPicker.java and ReactToolbar.java 中使用的解决方案相同。

@Override
public void requestLayout() {
    super.requestLayout();

    // The spinner relies on a measure + layout pass happening after it calls requestLayout().
    // Without this, the widget never actually changes the selection and doesn't call the
    // appropriate listeners. Since we override onLayout in our ViewGroups, a layout pass never
    // happens after a call to requestLayout, so we simulate one here.
    post(measureAndLayout);
}

private final Runnable measureAndLayout = new Runnable() {
    @Override
    public void run() {
        measure(
                MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.EXACTLY),
                MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.EXACTLY));
        layout(getLeft(), getTop(), getRight(), getBottom());
    }
};