sbaar sbaar - 3 months ago 42
Android Question

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

I have modified a linear layout to respond to touch by resizing the left child while the right child takes up the rest of the space, simulating a horizontal scroll that can be 'opened' or 'closed' using the following code

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


In react native, touch is still calling this method and logging the expected values, but the size of the children is not updated. The only time it updates is when I switch the visibility to gone then visible again. Calling invalidate/requestLayout on the view from java or forceUpdate from js does not have any effect.

Is there some other code I need to call to invalidate and redraw the view? Is there a hint I need to give to react that this component scrolls or responds to touch?

Answer

RN android is indeed not updating children layout or visibility or adapter changes under most conditions. By inserting hooks into the custom view when an update is needed that will call this code normal behavior is restored, mostly. There are still some cases where measurement does not happen like it does normally and I have to postDelayed Runnables that then cause this invalidation. Dealing with a node's parents may not be strictly necessary in all cases, but it is for some.

In View Manager

public LayoutShadowNode createShadowNodeInstance() {
    node = new MyShadowNode();
    return node;
}
protected CustomView createViewInstance(final ThemedReactContext reactContext) {   

...
        customView.setRnUpdateListener(new CustomView.RNUpdateListener() {

        Method m;
        @Override
        public void needsUpdate() {
            Runnable r = new Runnable() {
                @Override
                public void run() {

                    if (m == null) {
                        try {
                            m = CSSNode.class.getDeclaredMethod("markHasNewLayout");
                            m.setAccessible(true);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                    try {
                        if (node != null) {
                            if (node.hasNewLayout()) node.markLayoutSeen();
                            ReactShadowNode parent = node.getParent();
                            while (parent != null) {
                                if (parent.hasNewLayout()) {
                                    try {
                                        m.invoke(parent);
                                    } catch (Exception e) {
                                        e.printStackTrace();
                                    }
                                    parent.markLayoutSeen();
                                }
                                parent = parent.getParent();
                            }
                            node.markUpdated();
                        }
                        Log.d(getName(), "markUpdated");
                    } 
                }

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

__

public class MyShadowNode extends LayoutShadowNode {
@Override
public void markUpdated(){
    super.markUpdated();
    if (hasNewLayout()) markLayoutSeen();
    dirty();
}
@Override
public boolean isDirty(){
    return true;
}


}
Comments